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
@@ -18,6 +18,24 @@
18
18
  <link href="/electron.css" rel="stylesheet"/>
19
19
  <% } %>
20
20
  <style>
21
+ .menu-container {
22
+ overflow: auto;
23
+ }
24
+ body.dark .mode-display {
25
+ background: rgba(255,255,255,0.04);
26
+ color: rgba(255,255,255,0.7);
27
+ }
28
+ .mode-display {
29
+ padding: 5px 10px;
30
+ background: rgba(0,0,0,0.04);
31
+ /*
32
+ text-align: center;
33
+ color: royalblue;
34
+ */
35
+ color: rgba(0, 0, 0, 0.7);
36
+ font-size: 12px;
37
+ font-weight: bold;
38
+ }
21
39
  .online {
22
40
  width: 10px;
23
41
  height: 10px;
@@ -80,6 +98,8 @@ body aside {
80
98
  position: relative;
81
99
  border-right: 1px solid rgba(0,0,0,0.04);
82
100
  height: 100%;
101
+ display: flex;
102
+ flex-direction: column;
83
103
  /*
84
104
  padding-bottom: 100px;
85
105
  */
@@ -100,7 +120,7 @@ aside {
100
120
  display: flex;
101
121
  flex-direction: column;
102
122
  */
103
- width: 200px;
123
+ width: 240px;
104
124
  overflow: auto;
105
125
  box-sizing: border-box;
106
126
  flex-shrink: 0;
@@ -213,9 +233,11 @@ body.dark #new-window {
213
233
  background: rgba(255,255,255,0.04);
214
234
  }
215
235
  */
236
+ /*
216
237
  .app-info-card img {
217
- margin-right: 10px;
238
+ margin: 10px;
218
239
  }
240
+ */
219
241
  .app-info {
220
242
 
221
243
  /*
@@ -275,6 +297,7 @@ body.dark .header-btns {
275
297
  /*
276
298
  padding: 10px !important;
277
299
  */
300
+ padding: 10px;
278
301
  display: flex;
279
302
  text-align: left;
280
303
  }
@@ -320,7 +343,10 @@ body.dark .app-info-card > * {
320
343
  color: var(--dark-color);
321
344
  }
322
345
  .app-info-container {
323
- padding: 7px 0;
346
+ /*
347
+ padding: 10px 5px;
348
+ */
349
+ padding-left: 5px;
324
350
  min-width: 0;
325
351
  text-overflow: ellipsis;
326
352
  }
@@ -328,6 +354,7 @@ body.dark .app-info-card > * {
328
354
  width: 100%;
329
355
  font-weight: bold;
330
356
  font-size: 16px;
357
+ word-break: break-word;
331
358
  /*
332
359
  padding: 15px 0;
333
360
  */
@@ -390,25 +417,18 @@ body.dark .header-item.cursor {
390
417
  }
391
418
  body .frame-link.selected {
392
419
  /*
393
- border-right: 10px solid gold;
394
- */
395
- /*
396
- background: black !important;
397
- color: white;
398
- */
399
420
  background: rgba(0,0,0,0.06) !important;
400
- /*
401
- background: rgba(0,0,100,0.06) !important;
402
421
  */
422
+ background: royalblue !important;
423
+ color: white !important;
403
424
  }
404
425
  .frame-link.selected .del {
405
426
  color: white;
406
427
  }
407
428
  body.dark .frame-link.selected {
408
429
  /*
409
- background: rgba(0,0,0,0.7) !important;
430
+ background: rgba(255,255,255,0.08) !important;
410
431
  */
411
- background: rgba(255,255,255,0.04) !important;
412
432
  }
413
433
  .frame-link .loader {
414
434
  /*
@@ -790,12 +810,6 @@ body .app-icon {
790
810
  display: none;
791
811
  margin-right: 10px;
792
812
  }
793
- body.minimized .app-icon {
794
- display: block;
795
- }
796
- body.minimized aside {
797
- display: none;
798
- }
799
813
  .top-menu {
800
814
  padding: 10px;
801
815
  text-align: center;
@@ -812,49 +826,782 @@ body.dark .top-menu .btn2.selected {
812
826
  background: black;
813
827
  color: white;
814
828
  }
815
- body.minimized #collapse {
816
- /*
817
- background: none !important;
818
- */
819
- border: none !important;
829
+ .bottombar {
830
+ display: flex;
831
+ justify-content: flex-end;
832
+ padding: 5px;
820
833
  }
821
- body.dark #collapse {
822
- border-bottom: 4px solid white;
823
- /*
824
- background: rgba(255,255,255,0.07);
825
- */
834
+
835
+ #browserview-container {
836
+ flex-grow: 1;
837
+ position: relative;
826
838
  }
827
- #collapse {
828
- border-bottom: 4px solid black;
829
- /*
839
+
840
+ body.dark #fs-status {
841
+ background: rgba(255,255,255,0.04);
842
+ }
843
+ #fs-status {
830
844
  background: rgba(0,0,0,0.04);
831
- */
845
+ gap: 5px;
846
+ box-sizing: border-box;
847
+ margin: 0 10px;
848
+ padding: 5px;
849
+ display: flex;
850
+ align-items: center;
851
+ border-radius: 6px;
832
852
  }
833
- body.dark .mode-selector .btn2.selected {
834
- /*
835
- background: rgba(255,255,255,0.07);
836
- */
837
- border-bottom: 4px solid white;
853
+
854
+ .fs-status-btn {
855
+ background: transparent;
856
+ border: none;
857
+ padding: 8px 12px;
858
+ cursor: pointer;
859
+ border-radius: 4px;
860
+ display: flex;
861
+ align-items: center;
862
+ gap: 8px;
863
+ font-size: 14px;
864
+ color: #24292f;
865
+ transition: background 0.2s;
838
866
  }
839
- .mode-selector .btn2.selected {
840
- border-bottom: 4px solid black;
841
- /*
842
- background: rgba(0,0,0,0.04);
843
- */
867
+
868
+ body.dark .fs-status-btn {
869
+ color: white;
870
+ }
871
+
872
+ .fs-status-btn:hover {
873
+ background: rgba(0,0,0,0.08);
874
+ }
875
+
876
+ body.dark .fs-status-btn:hover {
877
+ background: rgba(255,255,255,0.08);
878
+ }
879
+
880
+ .fs-status-btn .badge:empty {
881
+ display: none;
844
882
  }
845
- .mode-selector {
883
+
884
+ .fs-status-btn .badge {
885
+ background: #dc3545;
886
+ color: white;
887
+ padding: 4px 8px;
888
+ border-radius: 12px;
889
+ font-size: 11px;
890
+ font-weight: 600;
891
+ min-width: 18px;
892
+ text-align: center;
893
+ }
894
+
895
+ /* Git diff modal styles - using specific prefixed classes to avoid collisions */
896
+ .pinokio-git-diff-modal-wrapper {
897
+ width: 90vw !important;
898
+ max-width: 1200px !important;
899
+ height: 80vh !important;
900
+ display: flex !important;
901
+ flex-direction: column !important;
902
+ }
903
+
904
+ .pinokio-git-diff-modal-wrapper .swal2-popup {
905
+ display: flex !important;
906
+ flex-direction: column !important;
907
+ height: 100% !important;
908
+ max-height: none !important;
909
+ grid-template-rows: auto 1fr auto !important;
910
+ grid-template-areas: "header" "content" "actions" !important;
911
+ padding: 0 !important;
912
+ }
913
+
914
+ .pinokio-git-diff-modal-wrapper .swal2-content {
915
+ flex: 1 !important;
916
+ display: flex !important;
917
+ flex-direction: column !important;
918
+ height: 100% !important;
919
+ margin: 0 !important;
920
+ padding: 0 !important;
921
+ }
922
+
923
+ .pinokio-git-diff-modal-wrapper .swal2-title {
924
+ font-size: 16px !important;
925
+ font-weight: 600 !important;
926
+ line-height: 1.2 !important;
927
+ margin: 0 !important;
928
+ padding: 0 !important;
929
+ color: #24292f !important;
930
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
931
+ text-align: center !important;
932
+ height: auto !important;
933
+ display: block !important;
934
+ }
935
+
936
+ .pinokio-git-diff-modal-wrapper .swal2-header {
937
+ padding: 12px 16px !important;
938
+ margin: 0 !important;
939
+ border-bottom: 1px solid #d0d7de !important;
940
+ background: #f6f8fa !important;
941
+ height: auto !important;
942
+ min-height: auto !important;
943
+ max-height: 60px !important;
944
+ flex-shrink: 0 !important;
945
+ flex-grow: 0 !important;
946
+ grid-area: header !important;
947
+ display: flex !important;
948
+ align-items: center !important;
949
+ justify-content: center !important;
950
+ }
951
+
952
+ .pinokio-git-diff-modal-wrapper .swal2-close {
953
+ font-size: 18px !important;
954
+ width: 28px !important;
955
+ height: 28px !important;
956
+ line-height: 28px !important;
957
+ margin: 0 !important;
958
+ padding: 0 !important;
959
+ top: 12px !important;
960
+ right: 12px !important;
961
+ color: #656d76 !important;
962
+ background: transparent !important;
963
+ border: none !important;
964
+ }
965
+
966
+ .pinokio-git-diff-modal-wrapper .swal2-icon {
967
+ display: none !important;
968
+ height: 0 !important;
969
+ width: 0 !important;
970
+ margin: 0 !important;
971
+ padding: 0 !important;
972
+ }
973
+
974
+ .pinokio-git-diff-modal-wrapper .swal2-html-container {
975
+ padding: 0 !important;
976
+ margin: 0 !important;
977
+ height: 100% !important;
978
+ overflow: hidden !important;
979
+ font-size: 14px !important;
980
+ flex: 1 !important;
981
+ display: flex !important;
982
+ flex-direction: column !important;
983
+ grid-area: content !important;
984
+ }
985
+
986
+ .pinokio-git-diff-modal-wrapper .swal2-actions {
987
+ display: flex !important;
988
+ justify-content: center !important;
989
+ align-items: center !important;
990
+ padding: 12px 16px !important;
991
+ margin: 0 !important;
992
+ border-top: 1px solid #d0d7de !important;
993
+ background: #f6f8fa !important;
994
+ grid-area: actions !important;
995
+ height: auto !important;
996
+ min-height: auto !important;
997
+ max-height: 60px !important;
998
+ flex-shrink: 0 !important;
999
+ }
1000
+
1001
+ .pinokio-git-diff-modal-wrapper .swal2-confirm {
1002
+ background: #0969da !important;
1003
+ color: white !important;
1004
+ border: 1px solid #0969da !important;
1005
+ border-radius: 6px !important;
1006
+ padding: 8px 16px !important;
1007
+ font-size: 14px !important;
1008
+ font-weight: 600 !important;
1009
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
1010
+ cursor: pointer !important;
1011
+ transition: background 0.2s !important;
1012
+ margin: 0 !important;
1013
+ min-width: auto !important;
1014
+ height: auto !important;
1015
+ line-height: 1.4 !important;
1016
+ }
1017
+
1018
+ .pinokio-git-diff-modal-wrapper .swal2-confirm:hover {
1019
+ background: #0860ca !important;
1020
+ border-color: #0860ca !important;
1021
+ }
1022
+
1023
+ .pinokio-git-commit-diff-overlay {
1024
+ z-index: 2000 !important;
1025
+ }
1026
+
1027
+ .pinokio-custom-commit-overlay {
1028
+ position: fixed;
1029
+ top: 0;
1030
+ left: 0;
1031
+ width: 100%;
1032
+ height: 100%;
1033
+ background: rgba(0, 0, 0, 0.5);
1034
+ z-index: 20000000;
1035
+ display: flex;
1036
+ justify-content: center;
1037
+ align-items: center;
1038
+ padding: 20px;
1039
+ box-sizing: border-box;
1040
+ }
1041
+
1042
+ .pinokio-custom-commit-modal {
1043
+ background: white;
1044
+ border-radius: 8px;
1045
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
1046
+ width: 90vw;
1047
+ max-width: 1200px;
1048
+ height: 80vh;
1049
+ display: flex;
1050
+ flex-direction: column;
1051
+ overflow: hidden;
1052
+ }
1053
+
1054
+ body.dark .pinokio-custom-commit-modal {
1055
+ background: #1f2328;
1056
+ color: white;
1057
+ }
1058
+
1059
+ .pinokio-custom-commit-header {
1060
+ display: flex;
1061
+ justify-content: space-between;
1062
+ align-items: center;
1063
+ padding: 16px 20px;
1064
+ border-bottom: 1px solid #d0d7de;
1065
+ background: #f6f8fa;
1066
+ }
1067
+
1068
+ body.dark .pinokio-custom-commit-header {
1069
+ border-bottom-color: #30363d;
1070
+ background: #161b22;
1071
+ }
1072
+
1073
+ .pinokio-custom-commit-header h3 {
1074
+ margin: 0;
1075
+ font-size: 18px;
1076
+ font-weight: 600;
1077
+ }
1078
+
1079
+ .pinokio-custom-commit-close {
1080
+ background: none;
1081
+ border: none;
1082
+ font-size: 24px;
1083
+ cursor: pointer;
1084
+ padding: 4px 8px;
1085
+ border-radius: 4px;
1086
+ color: #656d76;
1087
+ }
1088
+
1089
+ .pinokio-custom-commit-close:hover {
1090
+ background: rgba(0, 0, 0, 0.1);
1091
+ color: #24292f;
1092
+ }
1093
+
1094
+ body.dark .pinokio-custom-commit-close {
1095
+ color: #8b949e;
1096
+ }
1097
+
1098
+ body.dark .pinokio-custom-commit-close:hover {
1099
+ background: rgba(255, 255, 255, 0.1);
1100
+ color: #f0f6fc;
1101
+ }
1102
+
1103
+ .pinokio-custom-commit-content {
1104
+ flex: 1;
1105
+ overflow: hidden;
1106
+ padding: 0;
1107
+ }
1108
+
1109
+ .pinokio-commit-message-container {
846
1110
  display: flex;
847
- gap: 3px;
1111
+ align-items: center;
1112
+ gap: 12px;
1113
+ padding: 0;
1114
+ margin: 0;
1115
+ width: 100%;
1116
+ }
1117
+
1118
+ .pinokio-commit-message-input {
1119
+ flex: 1;
1120
+ padding: 8px 12px;
1121
+ border: 1px solid #d0d7de;
1122
+ border-radius: 6px;
1123
+ font-size: 14px;
1124
+ background: white;
1125
+ color: #24292f;
1126
+ outline: none;
1127
+ transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
1128
+ }
1129
+
1130
+ .pinokio-commit-message-input:focus {
1131
+ border-color: #0969da;
1132
+ box-shadow: 0 0 0 3px rgba(9, 105, 218, 0.3);
1133
+ }
1134
+
1135
+ body.dark .pinokio-commit-message-input {
1136
+ background: #21262d;
1137
+ border-color: #30363d;
1138
+ color: #e6edf3;
1139
+ }
1140
+
1141
+ body.dark .pinokio-commit-message-input:focus {
1142
+ border-color: #1f6feb;
1143
+ box-shadow: 0 0 0 3px rgba(31, 111, 235, 0.3);
1144
+ }
1145
+
1146
+ .pinokio-save-version-btn {
1147
+ background: #1f883d;
1148
+ color: white;
1149
+ border: 1px solid rgba(31, 35, 40, 0.15);
1150
+ border-radius: 6px;
1151
+ padding: 8px 16px;
1152
+ font-size: 14px;
1153
+ font-weight: 500;
1154
+ cursor: pointer;
1155
+ transition: background-color 0.2s;
1156
+ white-space: nowrap;
1157
+ }
1158
+
1159
+ .pinokio-save-version-btn:hover {
1160
+ background: #1a7f37;
1161
+ }
1162
+
1163
+ .pinokio-save-version-btn:active {
1164
+ background: #18722f;
1165
+ }
1166
+
1167
+ .pinokio-git-diff-main-container {
1168
+ display: flex !important;
1169
+ height: 100% !important;
1170
+ border: 1px solid #d0d7de !important;
848
1171
  /*
849
- gap: 5px;
1172
+ border-radius: 6px !important;
850
1173
  */
1174
+ overflow: hidden !important;
1175
+ margin: 0 !important;
1176
+ padding: 0 !important;
1177
+ box-sizing: border-box !important;
1178
+ font-family: inherit !important;
1179
+ font-size: inherit !important;
1180
+ line-height: inherit !important;
1181
+ color: inherit !important;
1182
+ background: white !important;
1183
+ text-align: left !important;
1184
+ vertical-align: baseline !important;
1185
+ }
1186
+
1187
+ .pinokio-git-diff-file-list-panel {
1188
+ width: 300px !important;
1189
+ border-right: 1px solid #d0d7de !important;
1190
+ background: #f6f8fa !important;
1191
+ overflow-y: auto !important;
1192
+ overflow-x: hidden !important;
1193
+ margin: 0 !important;
1194
+ padding: 0 !important;
1195
+ box-sizing: border-box !important;
1196
+ flex-shrink: 0 !important;
1197
+ border-top: none !important;
1198
+ border-bottom: none !important;
1199
+ border-left: none !important;
1200
+ color: #24292f !important;
1201
+ font-family: monospace !important;
1202
+ font-size: 12px !important;
1203
+ line-height: 1.4 !important;
1204
+ text-align: left !important;
1205
+ vertical-align: baseline !important;
851
1206
  }
852
- .mode-selector .btn2 {
853
- border-radius: 0;
854
- padding: 10px 0;
855
- min-width: 40px;
1207
+
1208
+ .pinokio-git-diff-file-item-row {
1209
+ padding: 8px 12px !important;
1210
+ cursor: pointer !important;
1211
+ border-bottom: 1px solid #d0d7de !important;
1212
+ font-family: monospace !important;
1213
+ font-size: 12px !important;
1214
+ transition: background 0.2s !important;
1215
+ margin: 0 !important;
1216
+ box-sizing: border-box !important;
1217
+ display: block !important;
1218
+ width: 100% !important;
1219
+ color: #24292f !important;
1220
+ background: transparent !important;
1221
+ border-top: none !important;
1222
+ border-left: none !important;
1223
+ border-right: none !important;
1224
+ text-align: left !important;
1225
+ text-decoration: none !important;
1226
+ font-weight: normal !important;
1227
+ font-style: normal !important;
1228
+ line-height: 1.4 !important;
1229
+ vertical-align: baseline !important;
1230
+ }
1231
+
1232
+ .pinokio-git-diff-file-item-row:hover {
1233
+ background: #e1e4e8 !important;
1234
+ color: #24292f !important;
1235
+ }
1236
+
1237
+ .pinokio-git-diff-file-item-row.pinokio-active-file-item {
1238
+ background: #0969da !important;
1239
+ color: white !important;
1240
+ }
1241
+
1242
+ .pinokio-git-diff-viewer-panel {
1243
+ flex: 1 !important;
1244
+ background: white !important;
1245
+ overflow-y: auto !important;
1246
+ overflow-x: auto !important;
1247
+ font-family: monospace !important;
1248
+ font-size: 12px !important;
1249
+ line-height: 1.4 !important;
1250
+ text-align: left !important;
1251
+ margin: 0 !important;
1252
+ padding: 0 !important;
1253
+ border: none !important;
1254
+ box-sizing: border-box !important;
1255
+ color: #24292f !important;
1256
+ font-weight: normal !important;
1257
+ font-style: normal !important;
1258
+ text-decoration: none !important;
1259
+ vertical-align: baseline !important;
1260
+ white-space: normal !important;
1261
+ min-width: 0 !important;
1262
+ width: 0 !important;
1263
+ position: relative !important;
1264
+ }
1265
+
1266
+ .pinokio-git-diff-empty-state {
1267
+ display: flex !important;
1268
+ align-items: center !important;
1269
+ justify-content: center !important;
1270
+ height: 100% !important;
1271
+ color: #656d76 !important;
1272
+ font-size: 14px !important;
1273
+ margin: 0 !important;
1274
+ padding: 20px !important;
1275
+ box-sizing: border-box !important;
1276
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
1277
+ font-weight: normal !important;
1278
+ font-style: normal !important;
1279
+ text-decoration: none !important;
1280
+ line-height: 1.4 !important;
1281
+ text-align: center !important;
1282
+ vertical-align: baseline !important;
1283
+ background: transparent !important;
1284
+ border: none !important;
856
1285
  }
857
1286
 
1287
+ .pinokio-diff-code-line {
1288
+ padding: 0 !important;
1289
+ white-space: pre-wrap !important;
1290
+ border-left: 4px solid transparent !important;
1291
+ text-align: left !important;
1292
+ display: grid !important;
1293
+ grid-template-columns: 60px 1fr !important;
1294
+ width: 100% !important;
1295
+ box-sizing: border-box !important;
1296
+ margin: 0 !important;
1297
+ font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace !important;
1298
+ font-size: 12px !important;
1299
+ line-height: 1.45 !important;
1300
+ color: #24292f !important;
1301
+ background: transparent !important;
1302
+ border-top: none !important;
1303
+ border-right: none !important;
1304
+ border-bottom: none !important;
1305
+ font-weight: normal !important;
1306
+ font-style: normal !important;
1307
+ text-decoration: none !important;
1308
+ vertical-align: baseline !important;
1309
+ overflow-wrap: break-word !important;
1310
+ word-break: break-all !important;
1311
+ }
1312
+
1313
+ .pinokio-diff-code-line-addition {
1314
+ background: #f0fff4 !important;
1315
+ border-left-color: #28a745 !important;
1316
+ color: #24292f !important;
1317
+ }
1318
+
1319
+ .pinokio-diff-code-line-deletion {
1320
+ background: #ffeef0 !important;
1321
+ border-left-color: #d73a49 !important;
1322
+ color: #24292f !important;
1323
+ }
1324
+
1325
+ .pinokio-diff-code-line-context {
1326
+ background: #f6f8fa !important;
1327
+ color: #24292f !important;
1328
+ }
1329
+
1330
+ .pinokio-diff-code-line-numbers {
1331
+ display: block !important;
1332
+ width: 100% !important;
1333
+ text-align: right !important;
1334
+ padding-right: 12px !important;
1335
+ color: #656d76 !important;
1336
+ user-select: none !important;
1337
+ font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace !important;
1338
+ font-size: 12px !important;
1339
+ line-height: 1.45 !important;
1340
+ font-weight: normal !important;
1341
+ font-style: normal !important;
1342
+ text-decoration: none !important;
1343
+ background: transparent !important;
1344
+ border: none !important;
1345
+ margin: 0 !important;
1346
+ box-sizing: border-box !important;
1347
+ white-space: nowrap !important;
1348
+ overflow: hidden !important;
1349
+ }
1350
+
1351
+ .pinokio-diff-code-line-content {
1352
+ display: block !important;
1353
+ width: 100% !important;
1354
+ padding-left: 12px !important;
1355
+ padding-right: 12px !important;
1356
+ white-space: pre-wrap !important;
1357
+ overflow-wrap: break-word !important;
1358
+ word-break: break-all !important;
1359
+ font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace !important;
1360
+ font-size: 12px !important;
1361
+ line-height: 1.45 !important;
1362
+ color: inherit !important;
1363
+ background: transparent !important;
1364
+ border: none !important;
1365
+ margin: 0 !important;
1366
+ box-sizing: border-box !important;
1367
+ }
1368
+
1369
+ .pinokio-git-diff-modal-wrapper iframe {
1370
+ width: 100% !important;
1371
+ height: 100% !important;
1372
+ border: none !important;
1373
+ display: block !important;
1374
+ margin: 0 !important;
1375
+ padding: 0 !important;
1376
+ box-sizing: border-box !important;
1377
+ }
1378
+
1379
+ /* Git history modal styles */
1380
+ body.dark .pinokio-git-history-modal-wrapper {
1381
+ background: black !important;
1382
+ }
1383
+ .pinokio-git-history-modal-wrapper {
1384
+ padding: 0 !important;
1385
+ width: 90vw !important;
1386
+ max-width: 900px !important;
1387
+ height: 80vh !important;
1388
+ display: flex !important;
1389
+ flex-direction: column !important;
1390
+ }
1391
+ .pinokio-git-history-modal-wrapper .swal2-title {
1392
+ flex-shrink: 0 !important;
1393
+ align-self: stretch !important;
1394
+ display: flex !important;
1395
+ justify-content: space-between !important;
1396
+ align-items: center !important;
1397
+ padding: 16px 20px !important;
1398
+ border-bottom: 1px solid #d0d7de !important;
1399
+ background: #f6f8fa !important;
1400
+ margin: 0 !important;
1401
+ font-size: 18px !important;
1402
+ font-weight: 600 !important;
1403
+ text-align: left !important;
1404
+ }
1405
+ body.dark .pinokio-git-history-modal-wrapper .swal2-title {
1406
+ border-bottom-color: #30363d !important;
1407
+ background: #161b22 !important;
1408
+ color: white !important;
1409
+ }
1410
+ .pinokio-git-history-modal-wrapper .swal2-close {
1411
+ display: none !important;
1412
+ background: none !important;
1413
+ border: none !important;
1414
+ font-size: 24px !important;
1415
+ cursor: pointer !important;
1416
+ padding: 0 !important;
1417
+ margin: 0 !important;
1418
+ width: auto !important;
1419
+ height: auto !important;
1420
+ position: static !important;
1421
+ top: auto !important;
1422
+ right: auto !important;
1423
+ color: #656d76 !important;
1424
+ }
1425
+ body.dark .pinokio-git-history-modal-wrapper .swal2-close {
1426
+ color: #8b949e !important;
1427
+ }
1428
+ .pinokio-git-history-modal-wrapper .swal2-close:hover {
1429
+ color: #1f2328 !important;
1430
+ }
1431
+ body.dark .pinokio-git-history-modal-wrapper .swal2-close:hover {
1432
+ color: white !important;
1433
+ }
1434
+
1435
+ .pinokio-git-history-modal-wrapper .swal2-html-container {
1436
+ text-align: left !important;
1437
+ padding: 0 !important;
1438
+ margin: 0 !important;
1439
+ flex-grow: 1 !important;
1440
+ overflow: hidden !important;
1441
+ min-height: 0 !important;
1442
+ }
1443
+
1444
+ .pinokio-git-history-container {
1445
+ display: flex;
1446
+ flex-direction: column;
1447
+ height: 100%;
1448
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
1449
+ }
1450
+
1451
+ .pinokio-git-history-header {
1452
+ padding: 16px 20px;
1453
+ border-bottom: 1px solid #d0d7de;
1454
+ background: #f6f8fa;
1455
+ flex-shrink: 0;
1456
+ }
1457
+
1458
+ .pinokio-git-history-info {
1459
+ display: flex;
1460
+ gap: 20px;
1461
+ align-items: center;
1462
+ flex-wrap: wrap;
1463
+ }
1464
+
1465
+ .pinokio-git-history-ref,
1466
+ .pinokio-git-history-remote {
1467
+ display: flex;
1468
+ align-items: center;
1469
+ gap: 6px;
1470
+ font-size: 14px;
1471
+ color: #656d76;
1472
+ }
1473
+
1474
+ .pinokio-git-history-ref i {
1475
+ color: #0969da;
1476
+ }
1477
+
1478
+ .pinokio-git-history-remote i {
1479
+ color: #8b949e;
1480
+ }
1481
+
1482
+ .pinokio-git-history-list {
1483
+ flex: 1;
1484
+ overflow-y: auto;
1485
+ padding: 0;
1486
+ }
1487
+
1488
+ .pinokio-git-history-empty {
1489
+ display: flex;
1490
+ align-items: center;
1491
+ justify-content: center;
1492
+ height: 100%;
1493
+ color: #656d76;
1494
+ font-size: 16px;
1495
+ }
1496
+
1497
+ .pinokio-git-commit-item {
1498
+ display: flex;
1499
+ padding: 16px 20px;
1500
+ border-bottom: 1px solid #d0d7de;
1501
+ gap: 12px;
1502
+ transition: background 0.2s;
1503
+ }
1504
+
1505
+ .pinokio-git-commit-item:hover {
1506
+ background: #f6f8fa;
1507
+ }
1508
+
1509
+ .pinokio-git-commit-item:last-child {
1510
+ border-bottom: none;
1511
+ }
1512
+
1513
+ .pinokio-git-commit-avatar {
1514
+ width: 32px;
1515
+ height: 32px;
1516
+ border-radius: 50%;
1517
+ background: #f6f8fa;
1518
+ display: flex;
1519
+ align-items: center;
1520
+ justify-content: center;
1521
+ flex-shrink: 0;
1522
+ color: #656d76;
1523
+ font-size: 14px;
1524
+ border: 1px solid #d0d7de;
1525
+ }
1526
+
1527
+ .pinokio-git-commit-content {
1528
+ flex: 1;
1529
+ min-width: 0;
1530
+ }
1531
+
1532
+ .pinokio-git-commit-message {
1533
+ font-size: 14px;
1534
+ color: #24292f;
1535
+ margin-bottom: 4px;
1536
+ font-weight: 500;
1537
+ white-space: nowrap;
1538
+ overflow: hidden;
1539
+ text-overflow: ellipsis;
1540
+ line-height: 1.4;
1541
+ }
1542
+
1543
+ .pinokio-git-commit-meta {
1544
+ display: flex;
1545
+ gap: 12px;
1546
+ font-size: 12px;
1547
+ color: #656d76;
1548
+ flex-wrap: wrap;
1549
+ }
1550
+
1551
+ .pinokio-git-commit-author {
1552
+ font-weight: 500;
1553
+ }
1554
+
1555
+ .pinokio-git-commit-hash {
1556
+ font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
1557
+ background: rgba(175,184,193,0.2);
1558
+ padding: 2px 6px;
1559
+ border-radius: 3px;
1560
+ cursor: pointer;
1561
+ }
1562
+
1563
+ .pinokio-git-commit-hash:hover {
1564
+ background: rgba(175,184,193,0.3);
1565
+ }
1566
+
1567
+ /* Dark theme support for git history */
1568
+ body.dark .pinokio-git-history-header {
1569
+ background: rgba(255,255,255,0.04);
1570
+ border-bottom-color: rgba(255,255,255,0.1);
1571
+ }
1572
+
1573
+ body.dark .pinokio-git-commit-item:hover {
1574
+ background: rgba(255,255,255,0.04);
1575
+ }
1576
+
1577
+ body.dark .pinokio-git-commit-item {
1578
+ border-bottom-color: rgba(255,255,255,0.1);
1579
+ }
1580
+
1581
+ body.dark .pinokio-git-commit-message {
1582
+ color: rgba(255,255,255,0.9);
1583
+ }
1584
+
1585
+ body.dark .pinokio-git-commit-meta {
1586
+ color: rgba(255,255,255,0.6);
1587
+ }
1588
+
1589
+ body.dark .pinokio-git-commit-avatar {
1590
+ background: rgba(255,255,255,0.04);
1591
+ border-color: rgba(255,255,255,0.1);
1592
+ color: rgba(255,255,255,0.6);
1593
+ }
1594
+
1595
+ body.dark .pinokio-git-commit-hash {
1596
+ background: rgba(255,255,255,0.1);
1597
+ color: rgba(255,255,255,0.8);
1598
+ }
1599
+
1600
+ body.dark .pinokio-git-commit-hash:hover {
1601
+ background: rgba(255,255,255,0.2);
1602
+ }
1603
+
1604
+ /*
858
1605
  @media only screen and (max-width: 1000px) {
859
1606
  .url-bar {
860
1607
  display: none;
@@ -863,8 +1610,13 @@ body.dark .mode-selector .btn2.selected {
863
1610
  display: block;
864
1611
  }
865
1612
  }
1613
+ */
866
1614
 
1615
+ /*
867
1616
  @media only screen and (max-width: 480px) {
1617
+ */
1618
+ /*
1619
+ @media only screen and (max-width: 600px) {
868
1620
  nav .btn2 {
869
1621
  width: auto !important;
870
1622
  min-width: 40px;
@@ -900,11 +1652,6 @@ body.dark .mode-selector .btn2.selected {
900
1652
  width: 100%;
901
1653
  height: 100%;
902
1654
  }
903
- /*
904
- .grid-btns {
905
- display: none;
906
- }
907
- */
908
1655
  #menu {
909
1656
  display: none;
910
1657
  }
@@ -912,8 +1659,18 @@ body.dark .mode-selector .btn2.selected {
912
1659
  display: flex;
913
1660
  }
914
1661
  }
1662
+ */
1663
+ body.minimized {
1664
+ flex-direction: row !important;
1665
+ }
1666
+ @media only screen and (max-width: 800px) {
1667
+ body {
1668
+ flex-direction: row !important;
1669
+ }
1670
+ }
915
1671
  </style>
916
1672
  <link href="/app.css" rel="stylesheet"/>
1673
+ <script src="/timeago.min.js"></script>
917
1674
  <script src="/hotkeys.min.js"></script>
918
1675
  <script src="/sweetalert2.js"></script>
919
1676
  <script src="/nav.js"></script>
@@ -936,287 +1693,333 @@ body.dark .mode-selector .btn2.selected {
936
1693
  <script src="/popper.min.js"></script>
937
1694
  <script src="/tippy-bundle.umd.min.js"></script>
938
1695
  </head>
939
- <body class='<%=theme%> <%=minimized ? "minimized" : ""%>' data-platform="<%=platform%>" data-agent="<%=agent%>">
1696
+ <body class='<%=theme%>' data-platform="<%=platform%>" data-agent="<%=agent%>">
940
1697
  <header class='navheader grabbable'>
941
1698
  <h1>
942
1699
  <a class='home' href="/">
943
1700
  <img class='icon' src="/pinokio-black.png">
944
1701
  </a>
945
- <!--
946
- <div class='app-icon'>
947
- <% if (config.icon) { %>
948
- <img src="<%=config.icon%>"/>
949
- <% } else { %>
950
- <i class="fa-regular fa-circle-dot"></i>
951
- <% } %>
952
- </div>
953
- -->
954
- <button class='btn2' id='back'><div><i class="fa-solid fa-chevron-left"></i></div><div>Prev</div></button>
955
- <button class='btn2' id='forward'><div><i class="fa-solid fa-chevron-right"></i></div><div>Next</div></button>
956
- <button class='btn2' id='refresh-page'><div><i class="fa-solid fa-rotate-right"></i></div><div>Refresh</div></button>
1702
+ <button id='collapse' class='btn2' data-tippy-content="toggle fullscreen view"><i class="fa-solid fa-bars"></i></button>
1703
+ <button class='btn2' id='back' data-tippy-content="back"><div><i class="fa-solid fa-chevron-left"></i></div></button>
1704
+ <button class='btn2' id='forward' data-tippy-content="forward"><div><i class="fa-solid fa-chevron-right"></i></div></button>
1705
+ <button class='btn2' id='refresh-page' data-tippy-content="refresh"><div><i class="fa-solid fa-rotate-right"></i></div></button>
1706
+ <button class='btn2' id='screenshot' data-tippy-content="take a screenshot"><i class="fa-solid fa-camera"></i></button>
1707
+ <div class='sep'></div>
957
1708
  <div class='mode-selector'>
958
- <button id='collapse' class='btn2'><div><i class="fa-solid fa-bars"></i></div><div id='collapse-text'>menu</div></button>
959
- <% if (type === "run") { %>
960
- <a id='switch-dev' class='btn2' href="<%=home.endsWith("/") ? home : home + "/" %>dev"><div><i class="fa-solid fa-code"></i></div><div>dev</div></a>
961
- <a class='btn2 selected'><div><i class="fa-solid fa-circle-play"></i></div><div>run</div></a>
962
- <% } else { %>
963
- <a id='switch-dev' class='btn2 selected'><div><i class="fa-solid fa-code"></i></div><div>dev</div></a>
964
- <a class='btn2' href="."><div><i class="fa-solid fa-circle-play"></i></div><div>run</div></a>
965
- <% } %>
1709
+ <a class="btn2 <%=type === 'review' ? 'selected' : ''%>" href="<%=review_tab%>"><div><i class="fa-regular fa-message"></i></div><div class='caption'>Community</div></a>
1710
+ <a class="btn2 <%=type === 'browse' ? 'selected' : ''%>" href="<%=dev_tab%>"><div><i class="fa-solid fa-code"></i></div><div class='caption'>Dev</div></a>
1711
+ <a class="btn2 <%=type === 'run' ? 'selected' : ''%>" href="<%=run_tab%>"><div><i class="fa-solid fa-circle-play"></i></div><div class='caption'>Run</div></a>
966
1712
  </div>
967
- <div class='flexible filler'></div>
968
- <div class='url-bar'>
969
- <% if (current_urls.https) { %>
970
- <a class='https-url' target="_blank" href="<%=current_urls.https%>"><i class="fa-solid fa-square-arrow-up-right"></i> <%=current_urls.https%></a>
971
- <% } %>
972
- <% if (current_urls.http) { %>
973
- <a class='http-url' target="_blank" href="<%=current_urls.http%>"><i class="fa-solid fa-square-arrow-up-right"></i> <%=current_urls.http%></a>
974
- <% } %>
1713
+ <button class='btn2' id='popout-link' data-tippy-content="open in a web browser"><i class="fa-solid fa-arrow-up-right-from-square"></i></button>
1714
+ <a class='btn2' href="/columns" data-tippy-content="split into 2 columns">
1715
+ <div><i class="fa-solid fa-table-columns"></i></div>
1716
+ </a>
1717
+ <a class='btn2' href="/rows" data-tippy-content="split into 2 rows">
1718
+ <div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
1719
+ </a>
1720
+ <div class="dropdown btn2">
1721
+ <button class='btn2' id='window-management'>
1722
+ <div><i class="fa-solid fa-plus"></i></div>
1723
+ </button>
1724
+ <div class="dropdown-content" id="dropdown-content">
1725
+ <button class='btn2' id='clone-win' data-tippy-content="clone this window">
1726
+ <div><i class="fa-solid fa-clone"></i><div>clone this window</div></div>
1727
+ </button>
1728
+ <button id='new-window' data-tippy-content="open a new window" title='open a new window' class='btn2' data-agent="<%=agent%>">
1729
+ <div><i class="fa-solid fa-plus"></i><div>new window</div></div>
1730
+ </button>
1731
+ </div>
975
1732
  </div>
976
- <a href="/connect" class='btn2'><div><i class="fa-solid fa-circle-user"></i></div><div>Connect</div></a>
977
1733
  <!--
978
- <input type='url' id='location' readonly value="<%=execUrl%>">
979
- <a id='open-location' target="_blank" features="browser"><i class="fa-solid fa-arrow-up-right-from-square"></i></a>
980
- -->
981
- <div class='nav-btns'>
982
- <a class='btn2' href="<%=portal%>" target="_blank"><div><i class="fa-solid fa-question"></i></div><div>Help</div></a>
983
- <button class='btn2' id='genlog'><div><i class="fa-solid fa-laptop-code"></i></div><div>Logs</div></button>
984
- <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>
985
- <a class='btn2' href="/?mode=settings"><div><i class="fa-solid fa-gear"></i></div><div>Settings</div></a>
986
- <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>
1734
+ <div class="dropdown btn2">
1735
+ <button class='btn2' id='window-management'>
1736
+ <div><i class="fa-regular fa-window-restore"></i></div>
1737
+ </button>
1738
+ <div class="dropdown-content" id="dropdown-content">
1739
+ <button class='btn2' id='clone-win' data-tippy-content="clone this window">
1740
+ <div><i class="fa-solid fa-clone"></i><div>clone this window</div></div>
1741
+ </button>
1742
+ <a class='btn2' href="/columns" data-tippy-content="split into 2 columns">
1743
+ <div><i class="fa-solid fa-table-columns"></i><div>split columns</div></div>
1744
+ </a>
1745
+ <a class='btn2' href="/rows" data-tippy-content="split into 2 rows">
1746
+ <div><i class="fa-solid fa-table-columns fa-rotate-270"></i><div>split rows</div></div>
1747
+ </a>
1748
+ <button id='new-window' data-tippy-content="open a new window" title='open a new window' class='btn2' data-agent="<%=agent%>">
1749
+ <div><i class="fa-solid fa-plus"></i><div>new window</div></div>
1750
+ </button>
1751
+ </div>
987
1752
  </div>
1753
+ -->
1754
+ <button class='btn2 hidden' id='close-window' data-tippy-content='close this section'>
1755
+ <div><i class="fa-solid fa-xmark"></i></div>
1756
+ </button>
988
1757
  </h1>
989
1758
  </header>
990
1759
  <div class='appcanvas'>
991
- <aside class='active'>
992
- <div class='header-top header-item'>
993
- <div class='app-info'>
994
- <div class='app-info-card'>
995
- <% if (config.icon) { %>
996
- <img src="<%=config.icon%>"/>
997
- <% } else { %>
998
- <i class="fa-regular fa-circle-dot"></i>
999
- <% } %>
1000
- <div class='app-info-container'>
1001
- <div class='app-info-title'><%=config.title%></div>
1002
- <div class='app-info-description collapsed'><%=config.description%></div>
1003
- </div>
1004
- </div>
1005
- <div class='header-btns <%=type === 'run' ? 'hidden' : ''%>'>
1006
- <div class='btn header-item frame-link revealer' id='edit-toggle' data-group='.edit-group'>
1007
- <div class='tab'>
1008
- <i class='fa-solid fa-pen-to-square'></i> Edit
1760
+ <% if (error) { %>
1761
+ <div class='error-message'>
1762
+ <div>
1763
+ <h1><i class="fa-solid fa-triangle-exclamation"></i> Error</h1>
1764
+ <div><%=error%></div>
1765
+ </div>
1766
+ </div>
1767
+ <% } else { %>
1768
+ <aside class='active'>
1769
+ <div class='header-top header-item'>
1770
+ <div class='app-info'>
1771
+ <div class='app-info-card'>
1772
+ <% if (config.icon) { %>
1773
+ <img src="<%=config.icon%>" onerror="this.src='/pinokio-black.png'"/>
1774
+ <% } else { %>
1775
+ <i class="fa-regular fa-circle-dot"></i>
1776
+ <% } %>
1777
+ <div class='app-info-container'>
1778
+ <div class='app-info-title'><%=config.title%></div>
1779
+ <div class='app-info-description collapsed'><%=config.description%></div>
1009
1780
  </div>
1010
- <div class='loader'><i class="fa-solid fa-angle-down"></i><i class="fa-solid fa-angle-up hidden"></i></div>
1011
1781
  </div>
1012
- <div class='edit-group editsubmenu submenu hidden'>
1013
- <!--
1014
- <a class='btn header-item frame-link' href="/prototype?type=extension&path=<%=path%>">
1782
+ <div class='header-btns <%=type === 'run' ? 'hidden' : ''%>'>
1783
+ <div class='btn header-item frame-link revealer' id='edit-toggle' data-group='.edit-group'>
1015
1784
  <div class='tab'>
1016
- <i class='fa-solid fa-puzzle-piece'></i> Extensions
1785
+ <i class='fa-solid fa-pen-to-square'></i> Edit
1017
1786
  </div>
1018
- </a>
1019
- -->
1020
- <div class='btn header-item frame-link' id='edit'>
1021
- <div class='tab'><i class="fa-solid fa-pencil"></i> Edit</div>
1022
- </div>
1023
- <div class='btn header-item frame-link' id='move'>
1024
- <div class='tab'><i class="fa-solid fa-right-to-bracket"></i> Move</div>
1025
- </div>
1026
- <div class='btn header-item frame-link' id='copy'>
1027
- <div class='tab'><i class="fa-solid fa-copy"></i> Copy</div>
1787
+ <div class='loader'><i class="fa-solid fa-angle-down"></i><i class="fa-solid fa-angle-up hidden"></i></div>
1028
1788
  </div>
1029
- <div class='btn header-item frame-link' id='delete' data-name="<%=name%>">
1030
- <div class='tab'><i class="fa-solid fa-trash-can"></i> Delete</div>
1789
+ <div class='edit-group editsubmenu submenu hidden'>
1790
+ <div class='btn header-item frame-link' id='edit'>
1791
+ <div class='tab'><i class="fa-solid fa-pencil"></i> Edit</div>
1792
+ </div>
1793
+ <div class='btn header-item frame-link' id='move'>
1794
+ <div class='tab'><i class="fa-solid fa-right-to-bracket"></i> Move</div>
1795
+ </div>
1796
+ <div class='btn header-item frame-link' id='copy'>
1797
+ <div class='tab'><i class="fa-solid fa-copy"></i> Copy</div>
1798
+ </div>
1799
+ <div class='btn header-item frame-link' id='delete' data-name="<%=name%>">
1800
+ <div class='tab'><i class="fa-solid fa-trash-can"></i> Delete</div>
1801
+ </div>
1031
1802
  </div>
1032
1803
  </div>
1033
1804
  </div>
1034
1805
  </div>
1035
- </div>
1036
- <div class='m n system' data-type="n">
1037
- <a target="<%=src%>" href="<%=src%>" class='btn header-item frame-link' data-index="0" data-mode="refresh">
1038
- <div class='tab'>
1039
- <i class="fa-regular fa-folder-open"></i> Files
1040
- </div>
1041
- <div class='disk-usage' data-path="/"></div>
1042
- </a>
1043
- <% if (profile || feed) { %>
1044
- <div class='btn header-item frame-link revealer' data-group='.info-group'>
1045
- <div class='tab'>
1046
- <i class='fa-solid fa-circle-info'></i> Info
1047
- </div>
1048
- <div class='loader'><i class="fa-solid fa-angle-down"></i><i class="fa-solid fa-angle-up hidden"></i></div>
1049
- </div>
1050
- <div class='info-group submenu hidden'>
1051
- <% if (profile) { %>
1052
- <a target="_blank" href="<%=profile%>" class='btn header-item frame-link'>
1053
- <div class='tab'><i class="fa-regular fa-circle-user"></i> Publisher</div>
1054
- </a>
1055
- <% } %>
1056
- <% if (feed) { %>
1057
- <a target="_blank" href="<%=feed%>" class='btn header-item frame-link'>
1058
- <div class='tab'><i class="fa-solid fa-rss"></i> Newsfeed</div>
1806
+ <div class='menu-container'>
1807
+ <div class='m n system' data-type="n">
1808
+ <a target="<%=src%>" href="<%=src%>" class='btn header-item frame-link' data-index="0" data-mode="refresh">
1809
+ <div class='tab'>
1810
+ <i class="fa-regular fa-folder-open"></i> Files
1811
+ </div>
1812
+ <div class='disk-usage' data-path="/"></div>
1059
1813
  </a>
1814
+ <% if (profile || feed) { %>
1815
+ <div class='btn header-item frame-link revealer' data-group='.info-group'>
1816
+ <div class='tab'>
1817
+ <i class='fa-solid fa-circle-info'></i> Info
1818
+ </div>
1819
+ <div class='loader'><i class="fa-solid fa-angle-down"></i><i class="fa-solid fa-angle-up hidden"></i></div>
1820
+ </div>
1821
+ <div class='info-group submenu hidden'>
1822
+ <% if (profile) { %>
1823
+ <a target="_blank" href="<%=profile%>" class='btn header-item frame-link'>
1824
+ <div class='tab'><i class="fa-regular fa-circle-user"></i> Publisher</div>
1825
+ </a>
1826
+ <% } %>
1827
+ <% if (feed) { %>
1828
+ <a target="_blank" href="<%=feed%>" class='btn header-item frame-link'>
1829
+ <div class='tab'><i class="fa-solid fa-rss"></i> Newsfeed</div>
1830
+ </a>
1831
+ <% } %>
1832
+ </div>
1060
1833
  <% } %>
1061
- </div>
1062
- <% } %>
1063
- <%if (type!=='run') { %>
1064
- <a id='devtab' data-mode="refresh" target="<%=dev_link%>" href="<%=dev_link%>" class="btn header-item frame-link selected">
1065
- <div class="tab"><i class="fa-solid fa-keyboard"></i> Build</div>
1066
- <div class='loader'><i class='fa-solid fa-angle-right'></i></div>
1067
- </a>
1068
- <% } %>
1069
- <div class="dynamic <%=type==='run' ? '' : 'selected'%>">
1070
- <div class='submenu'>
1071
- <% if (plugin_menu) { %>
1072
- <%- include('./partials/dynamic', { dynamic: plugin_menu, }) %>
1834
+ <%if (type!=='run') { %>
1835
+ <a id='devtab' data-mode="refresh" target="<%=dev_link%>" href="<%=dev_link%>" class="btn header-item frame-link selected" data-index="10">
1836
+ <div class="tab"><i class="fa-solid fa-keyboard"></i> Build</div>
1837
+ <div class='loader'><i class='fa-solid fa-angle-right'></i></div>
1838
+ </a>
1073
1839
  <% } %>
1074
- </div>
1075
- <% if (false) { %>
1076
- <% if (type !== "run") { %>
1077
- <% if (plugin_menu) { %>
1078
- <%- include('./partials/dynamic', { dynamic: plugin_menu, }) %>
1079
- <% } else { %>
1080
- <div class="nested-menu">
1081
- <div class="btn header-item frame-link selected">
1082
- <div class="tab"><i class="fa-solid fa-code"></i> Dev</div>
1083
- <div class="loader"><i class="fa-solid fa-circle-notch fa-spin"></i></div>
1840
+ <div class="dynamic <%=type==='run' ? '' : 'selected'%>">
1841
+ <div class='submenu'>
1842
+ <% if (plugin_menu) { %>
1843
+ <%- include('./partials/dynamic', { dynamic: plugin_menu, }) %>
1844
+ <% } %>
1845
+ </div>
1846
+ <% if (false) { %>
1847
+ <% if (type !== "run") { %>
1848
+ <% if (plugin_menu) { %>
1849
+ <%- include('./partials/dynamic', { dynamic: plugin_menu, }) %>
1850
+ <% } else { %>
1851
+ <div class="nested-menu">
1852
+ <div class="btn header-item frame-link selected">
1853
+ <div class="tab"><i class="fa-solid fa-code"></i> Dev</div>
1854
+ <div class="loader"><i class="fa-solid fa-circle-notch fa-spin"></i></div>
1855
+ </div>
1856
+ </div>
1857
+ <% } %>
1858
+ <% } %>
1859
+ <% } %>
1860
+ </div>
1861
+ <% if (type === "browse") { %>
1862
+ <div class='nested-menu selected git blue'>
1863
+ <div class='btn header-item frame-link reveal'>
1864
+ <div class='tab'><i class="fa-solid fa-clock-rotate-left"></i> Version History</div>
1865
+ <div class='loader'><i class="fa-solid fa-angle-down"></i><i class="fa-solid fa-angle-up hidden"></i></div>
1866
+ </div>
1867
+ <div class='submenu' id='git-repos'>
1868
+ </div>
1869
+ </div>
1870
+ <!--
1871
+ <div class='nested-menu selected blue'>
1872
+ <a target="<%=logs%>" href="<%=logs%>" class='btn header-item frame-link' data-index="1" data-mode="refresh">
1873
+ <div class='tab'>
1874
+ <i class="fa-solid fa-clock-rotate-left"></i> Logs
1084
1875
  </div>
1876
+ <div class='disk-usage' data-path="/logs"></div>
1877
+ </a>
1878
+ </div>
1879
+ -->
1880
+ <div class='nested-menu selected blue'>
1881
+ <div class='btn header-item frame-link reveal'>
1882
+ <div class='tab'><i class="fa-solid fa-robot"></i> AI Recipes</div>
1883
+ <div class='loader'><i class="fa-solid fa-angle-down"></i><i class="fa-solid fa-angle-up hidden"></i></div>
1085
1884
  </div>
1086
- <% } %>
1885
+ <div class='submenu hidden' id='ai-prompts'>
1886
+ </div>
1887
+ </div>
1087
1888
  <% } %>
1088
- <% } %>
1089
- </div>
1090
- <% if (type === "browse") { %>
1091
- <div class='nested-menu selected git blue'>
1092
- <div class='btn header-item frame-link reveal'>
1093
- <div class='tab'><i class="fa-solid fa-clock-rotate-left"></i> Version History</div>
1094
- <div class='loader'><i class="fa-solid fa-angle-down"></i><i class="fa-solid fa-angle-up hidden"></i></div>
1095
- </div>
1096
- <div class='submenu' id='git-repos'>
1097
- </div>
1098
- </div>
1099
- <!--
1100
- <div class='nested-menu selected blue'>
1101
- <a target="<%=logs%>" href="<%=logs%>" class='btn header-item frame-link' data-index="1" data-mode="refresh">
1889
+ <div class='btn header-item frame-link revealer' data-group='.config-group'>
1102
1890
  <div class='tab'>
1103
- <i class="fa-solid fa-clock-rotate-left"></i> Logs
1891
+ <i class='fa-solid fa-gear'></i> Settings
1104
1892
  </div>
1105
- <div class='disk-usage' data-path="/logs"></div>
1106
- </a>
1107
- </div>
1108
- -->
1109
- <div class='nested-menu selected blue'>
1110
- <div class='btn header-item frame-link reveal'>
1111
- <div class='tab'><i class="fa-solid fa-robot"></i> AI Recipes</div>
1112
1893
  <div class='loader'><i class="fa-solid fa-angle-down"></i><i class="fa-solid fa-angle-up hidden"></i></div>
1113
1894
  </div>
1114
- <div class='submenu hidden' id='ai-prompts'>
1895
+ <div class='config-group submenu hidden'>
1896
+ <a target="/share/<%=name%>" href="/share/<%=name%>", class="btn header-item frame-link" data-index="2" data-mode="refresh">
1897
+ <div class='tab'><i class="fa-solid fa-wifi"></i> Share</div>
1898
+ </a>
1899
+ <a target="/env/api/<%=name%>" href="/env/api/<%=name%>", class="btn header-item frame-link" data-index="3" data-mode="refresh">
1900
+ <div class='tab'><i class="fa-solid fa-gear"></i> Configure</div>
1901
+ </a>
1115
1902
  </div>
1116
1903
  </div>
1117
- <% } %>
1118
- <div class='btn header-item frame-link revealer' data-group='.config-group'>
1119
- <div class='tab'>
1120
- <i class='fa-solid fa-gear'></i> Settings
1904
+ <div class='m h menu' data-type='h'>
1905
+ <% if (config.menu) { %>
1906
+ <%- include('./partials/menu', { menu: config.menu, }) %>
1907
+ <% } %>
1908
+ </div>
1909
+ <div class='m s temp-menu' data-type='s'>
1910
+ <%tabs.forEach((tab, i) => { %>
1911
+ <a target="s<%=i%>" href="<%=tab%>" class="btn header-item frame-link" data-index="<%=i%>">
1912
+ <div class='tab'>
1913
+ <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>
1914
+ </div>
1915
+ </a>
1916
+ <%})%>
1121
1917
  </div>
1122
- <div class='loader'><i class="fa-solid fa-angle-down"></i><i class="fa-solid fa-angle-up hidden"></i></div>
1123
- </div>
1124
- <div class='config-group submenu hidden'>
1125
- <a target="/share/<%=name%>" href="/share/<%=name%>", class="btn header-item frame-link" data-index="2" data-mode="refresh">
1126
- <div class='tab'><i class="fa-solid fa-wifi"></i> Share</div>
1127
- </a>
1128
- <a target="/env/api/<%=name%>" href="/env/api/<%=name%>", class="btn header-item frame-link" data-index="3" data-mode="refresh">
1129
- <div class='tab'><i class="fa-solid fa-gear"></i> Configure</div>
1130
- </a>
1131
- </div>
1132
- </div>
1133
- <div class='m h menu' data-type='h'>
1134
- <% if (config.menu) { %>
1135
- <%- include('./partials/menu', { menu: config.menu, }) %>
1136
- <% } %>
1137
- </div>
1138
- <div class='m s temp-menu' data-type='s'>
1139
- <%tabs.forEach((tab, i) => { %>
1140
- <a target="s<%=i%>" href="<%=tab%>" class="btn header-item frame-link" data-index="<%=i%>">
1141
- <div class='tab'>
1142
- <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>
1143
- </div>
1144
- </a>
1145
- <%})%>
1146
- </div>
1147
- <!--
1148
- <div class='tabmenu'>
1149
- <div id='new-tab' class='btn header-item'><div class='tab'><i class="fa-solid fa-plus"></i> New</div></div>
1150
- <button class='btn header-item' id='clone-tab'><div class='tab'><i class='fa-regular fa-copy'></i> Clone</div></button>
1151
- <a class='btn header-item' id='open-browser' target="_blank" href="" features="browser"><div class='tab'><i class="fa-solid fa-square-arrow-up-right"></i> Popout</div></a>
1152
- <button class='btn header-item' id='reload'><div class='tab'><i class="fa-solid fa-rotate-right"></i> Refresh</div></button>
1153
- </div>
1154
- -->
1155
- <!--
1156
- <div class='grid-btns'>
1157
- <a class='btn2' href="/?mode=explore" id='explore'><div><i class="fa-solid fa-globe"></i></div><div>Discover</div></a>
1158
- <a class='btn2' id='report'><div><i class="fa-solid fa-bug"></i></div><div>Report Bug</div></a>
1159
- <a class='btn2' href='https://discord.gg/TQdNwadtE4' target="_blank"><div><i class="fa-brands fa-discord"></i></div><div>Discord</div></a>
1160
- <a class='btn2' href='https://twitter.com/cocktailpeanut' target="_blank"><div><i class="fa-brands fa-twitter"></i></div><div>Twitter</div></a>
1161
- </div>
1162
- -->
1163
- </aside>
1164
- <% if (error) { %>
1165
- <div class='error-message'>
1166
- <div>
1167
- <h1><i class="fa-solid fa-triangle-exclamation"></i> Error</h1>
1168
- <div><%=error%></div>
1169
1918
  </div>
1170
- </div>
1171
- <% } else { %>
1919
+ </aside>
1920
+
1172
1921
  <div class='container'>
1173
- <!--
1174
- <nav>
1175
- <div class='bar'>
1176
- <button class='btn2' id='menu-mobile'><i class="fa-solid fa-bars"></i></button>
1177
- <button class='btn2' id='menu'>
1178
- <img src="<%=config.icon%>"/>
1179
- <i class="fa-solid fa-arrow-right-to-bracket fa-rotate-180"></i>
1922
+ <% if (type === "browse") { %>
1923
+ <div id='fs-status' data-history-uri="<%=git_history_url%>" data-uri="<%=git_monitor_url%>">
1924
+ <button id='fs-changes-btn' class='fs-status-btn'>
1925
+ <span>Changes</span>
1926
+ <div class='badge'>loading...</div>
1927
+ </button>
1928
+ <button id='fs-history-btn' class='fs-status-btn'>
1929
+ <span>History</span>
1180
1930
  </button>
1181
- <% if (agent === "electron") { %>
1182
- <button class='btn2' id='back'><i class="fa-solid fa-chevron-left"></i></button>
1183
- <button class='btn2' id='forward'><i class="fa-solid fa-chevron-right"></i></button>
1184
- <% } %>
1185
- <button class='btn2' id='reload'><i class="fa-solid fa-rotate-right"></i> Refresh</button>
1186
- <% if (agent === "electron") { %>
1187
- <a class='btn2' id='open-browser' target="_blank" href="" features="browser"><i class="fa-solid fa-square-arrow-up-right"></i> Popout</a>
1188
- <% } else { %>
1189
- <a class='btn2' id='open-browser' target="_blank" href=""><i class="fa-solid fa-square-arrow-up-right"></i> Popout</a>
1190
- <% } %>
1191
- <button class='btn2' id='clone-tab'><i class='fa-regular fa-copy'></i> Clone</button>
1192
- <div id='new-tab' class='btn2'><i class="fa-solid fa-plus"></i> Tab</div>
1193
- <input type='url' id='location' readonly>
1194
1931
  </div>
1195
- </nav>
1196
- -->
1932
+ <% } %>
1197
1933
  <main class='browserview'>
1198
1934
  </main>
1199
1935
  </div>
1200
- </div>
1936
+ </div>
1201
1937
  <script>
1202
1938
  let started_script
1203
1939
  let loaded = {}
1204
1940
  let cursorIndex = 0;
1205
1941
  let interacted = false
1942
+ let global_selector
1943
+ const iframe_onerror = (iframe) => {
1944
+ let originalSrc = iframe.src
1945
+ iframe.onload = function() {
1946
+ try {
1947
+ // Try to access the iframe's document
1948
+ const currentSrc = iframe.src
1949
+ // Check if it's a chrome error page or empty
1950
+ console.log({ currentSrc, originalSrc })
1951
+ if (currentSrc !== originalSrc &&
1952
+ (currentSrc.includes('chrome-error://') ||
1953
+ currentSrc === 'about:blank' ||
1954
+ currentSrc.includes('data:'))) {
1955
+ console.log('Iframe failed to load, switching to fallback');
1956
+ iframeDoc.classList.add("hidden")
1957
+ Swal.fire({
1958
+ html: `<i class="fa-solid fa-circle-notch fa-spin"></i> Loading...`,
1959
+ customClass: {
1960
+ container: "loader-container",
1961
+ popup: "loader-popup",
1962
+ htmlContainer: "loader-dialog",
1963
+ footer: "hidden",
1964
+ actions: "hidden"
1965
+ }
1966
+ });
1967
+ console.log("retrying after 3 seconds")
1968
+ setTimeout(() => {
1969
+ console.log("retrying")
1970
+ iframe.src = iframe.src
1971
+ }, 3000)
1972
+ }
1973
+ } catch (e) {
1974
+ iframe.classList.add("hidden")
1975
+ // Cross-origin restriction - assume it loaded successfully
1976
+ // if no error was thrown during the initial load
1977
+ console.log("ERROR", e)
1978
+ Swal.fire({
1979
+ html: `<i class="fa-solid fa-circle-notch fa-spin"></i> Loading...`,
1980
+ customClass: {
1981
+ container: "loader-container",
1982
+ popup: "loader-popup",
1983
+ htmlContainer: "loader-dialog",
1984
+ footer: "hidden",
1985
+ actions: "hidden"
1986
+ }
1987
+ });
1988
+ setTimeout(() => {
1989
+ location.href = location.href
1990
+ }, 3000);
1991
+ }
1992
+ }
1993
+ }
1994
+ const create_iframe = (name, href) => {
1995
+ let frame = document.createElement("iframe")
1996
+ frame.name = name
1997
+ frame.src = href
1998
+ iframe_onerror(frame)
1999
+ frame.setAttribute(
2000
+ "allow",
2001
+ "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 *;"
2002
+ )
2003
+ document.querySelector("main").appendChild(frame)
2004
+ loaded[name] = true
2005
+ }
2006
+ /*
2007
+ if (document.body.classList.contains("minimized")) {
2008
+ document.querySelector("#collapse i").className = "fa-solid fa-compress"
2009
+ } else {
2010
+ document.querySelector("#collapse i").className = "fa-solid fa-expand"
2011
+ }
2012
+ */
1206
2013
  document.querySelector("#collapse").addEventListener("click", (e) => {
1207
2014
  document.body.classList.toggle("minimized")
1208
- fetch("/state", {
1209
- method: "POST",
1210
- headers: {
1211
- "Content-Type": "application/json"
1212
- },
1213
- body: JSON.stringify({
1214
- name: "<%=name%>",
1215
- type: "<%=type%>",
1216
- method: "toggleMenu"
1217
- })
1218
- }).then((res) => {
1219
- })
2015
+ let frame_key = window.frameElement?.name || "";
2016
+ if (document.body.classList.contains("minimized")) {
2017
+ // document.querySelector("#collapse i").className = "fa-solid fa-compress"
2018
+ sessionStorage.setItem(frame_key + ":window_mode", "minimized")
2019
+ } else {
2020
+ // document.querySelector("#collapse i").className = "fa-solid fa-expand"
2021
+ sessionStorage.setItem(frame_key + ":window_mode", "full")
2022
+ }
1220
2023
  })
1221
2024
  document.addEventListener("click", (e) => {
1222
2025
  interacted = true
@@ -1233,6 +2036,8 @@ body.dark .mode-selector .btn2.selected {
1233
2036
  */
1234
2037
  <% } %>
1235
2038
  <% if (type === "run" && config.meta_required) { %>
2039
+
2040
+ /*
1236
2041
  setTimeout(() => {
1237
2042
  tippy('#switch-dev', {
1238
2043
  arrow: true,
@@ -1242,6 +2047,7 @@ body.dark .mode-selector .btn2.selected {
1242
2047
  content: "Switch to dev mode to make changes to the app"
1243
2048
  });
1244
2049
  }, 500)
2050
+ */
1245
2051
  <% } %>
1246
2052
  const n = new N()
1247
2053
  const getTarget = (href) => {
@@ -1327,13 +2133,44 @@ body.dark .mode-selector .btn2.selected {
1327
2133
  // */
1328
2134
  // }
1329
2135
  }
2136
+
2137
+ let timeout_id
1330
2138
  const renderSelection = async (e, force) => {
1331
2139
  let selection = null
1332
2140
  <% if (type === "run" && env.PINOKIO_SCRIPT_DEFAULT && env.PINOKIO_SCRIPT_DEFAULT.toString().toLowerCase() === "true") { %>
1333
2141
  selection = document.querySelector("[data-default]")
2142
+ if (selection && selection.href) {
2143
+ let selection_protocol = new URL(selection.href).protocol
2144
+ let page_protocol = new URL(location.href).protocol
2145
+ console.log({ selection_protocol, page_protocol })
2146
+ if (selection_protocol !== page_protocol) {
2147
+ console.log({ timeout_id })
2148
+ if (!timeout_id) {
2149
+ console.log("Retrying in 3 seconds")
2150
+ Swal.fire({
2151
+ html: `<i class="fa-solid fa-circle-notch fa-spin"></i> Loading...`,
2152
+ customClass: {
2153
+ container: "loader-container",
2154
+ popup: "loader-popup",
2155
+ htmlContainer: "loader-dialog",
2156
+ footer: "hidden",
2157
+ actions: "hidden"
2158
+ }
2159
+ });
2160
+ timeout_id = setTimeout(() => {
2161
+ location.href = location.href
2162
+ }, 3000)
2163
+ }
2164
+ return
2165
+ }
2166
+ }
1334
2167
  <% } %>
1335
- console.log({ selection })
1336
- console.log("renderSelection", selection)
2168
+ console.log("renderSelection", global_selector)
2169
+ if (global_selector) {
2170
+ selection = document.querySelector(global_selector)
2171
+ console.log("selection", selection)
2172
+ global_selector = null
2173
+ }
1337
2174
  if (selection || location.hash && location.hash.length > 0) {
1338
2175
  let target
1339
2176
  if (location.hash && location.hash.length > 0) {
@@ -1348,18 +2185,6 @@ body.dark .mode-selector .btn2.selected {
1348
2185
  console.log({ type, index })
1349
2186
  selector = `aside .${type} [data-index='${index}']`
1350
2187
  }
1351
-
1352
-
1353
- // select the selected link
1354
- // document.querySelectorAll(".frame-link").forEach((el) => {
1355
- // el.classList.remove("selected")
1356
- // })
1357
-
1358
-
1359
-
1360
- // const target = document.querySelector(`[target='${id}']`)
1361
-
1362
-
1363
2188
  if (e) {
1364
2189
  // If the renderSelection is triggered by an event, look for the frame-link that's triggering the event
1365
2190
  if (e.target.classList.contains("frame-link")) {
@@ -1430,21 +2255,8 @@ body.dark .mode-selector .btn2.selected {
1430
2255
  // save target.href
1431
2256
  <% if (type !== "run") { %>
1432
2257
  let _url = new URL(target.href)
1433
- fetch("/state", {
1434
- method: "POST",
1435
- headers: {
1436
- "Content-Type": "application/json"
1437
- },
1438
- body: JSON.stringify({
1439
- name: "<%=name%>",
1440
- type: "<%=type%>",
1441
- method: "select",
1442
- params: {
1443
- url: _url.pathname + _url.search + _url.hash,
1444
- }
1445
- })
1446
- }).then((res) => {
1447
- })
2258
+ let frame_key = window.frameElement?.name || "";
2259
+ sessionStorage.setItem(frame_key + ":url", _url.pathname + _url.search + _url.hash)
1448
2260
  <% } %>
1449
2261
 
1450
2262
  // hide all frames
@@ -1466,7 +2278,8 @@ body.dark .mode-selector .btn2.selected {
1466
2278
  for(let frame of hardFrames) {
1467
2279
  console.log({ src: frame.src, href: target.href, target: target.target })
1468
2280
  // Types of links in the tab
1469
- if (String(port) === "<%=port%>") {
2281
+ //if (String(port) === "<%=port%>") {
2282
+ if (String(port) === String(location.port) || /https:\/\/pinokio\..*localhost/.test(origin)) {
1470
2283
  // 1. running from <%=port%> => find a frame whose base URL matches the tab's base URL
1471
2284
  // Pinokio links
1472
2285
  let targetPath = new URL(target.href).pathname
@@ -1569,6 +2382,7 @@ body.dark .mode-selector .btn2.selected {
1569
2382
  let frame = document.createElement("iframe")
1570
2383
  frame.name = target.target
1571
2384
  frame.src = target.href
2385
+ iframe_onerror(frame)
1572
2386
  frame.setAttribute(
1573
2387
  "allow",
1574
2388
  "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 *;"
@@ -1680,6 +2494,7 @@ body.dark .mode-selector .btn2.selected {
1680
2494
  })
1681
2495
  <% } %>
1682
2496
 
2497
+
1683
2498
  const syncTabs = async () => {
1684
2499
  let tabs = []
1685
2500
  document.querySelectorAll(".temp-menu .frame-link").forEach((el) => {
@@ -1719,6 +2534,7 @@ body.dark .mode-selector .btn2.selected {
1719
2534
  let frame = document.createElement("iframe")
1720
2535
  frame.name = id
1721
2536
  frame.src = item.href
2537
+ iframe_onerror(frame)
1722
2538
  frame.setAttribute(
1723
2539
  "allow",
1724
2540
  "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 *;"
@@ -1776,6 +2592,14 @@ body.dark .mode-selector .btn2.selected {
1776
2592
  }
1777
2593
  })
1778
2594
  })
2595
+ document.querySelector("#popout-link").addEventListener("click", async (e) => {
2596
+ e.preventDefault()
2597
+ e.stopPropagation()
2598
+ let iframe = document.querySelector("iframe:not(.hidden)")
2599
+ if (iframe) {
2600
+ open_url(iframe.src, "_blank")
2601
+ }
2602
+ })
1779
2603
  document.querySelector("#edit").addEventListener("click", async (e) => {
1780
2604
  e.preventDefault()
1781
2605
  e.stopPropagation()
@@ -2283,7 +3107,6 @@ body.dark .mode-selector .btn2.selected {
2283
3107
  const ai = await fetch("<%=ai%>").then((res) => {
2284
3108
  return res.text()
2285
3109
  })
2286
- console.log({ ai })
2287
3110
  if (document.querySelector("#ai-prompts")) {
2288
3111
  document.querySelector("#ai-prompts").innerHTML = ai
2289
3112
  }
@@ -2293,7 +3116,7 @@ body.dark .mode-selector .btn2.selected {
2293
3116
  // render the selected frame only if not silent
2294
3117
  if (silent) {
2295
3118
  } else {
2296
- <% if (type === 'run') { %>
3119
+ <% if (type === 'run' || type === 'browse' ) { %>
2297
3120
  renderSelection()
2298
3121
  <% } %>
2299
3122
  }
@@ -2317,7 +3140,6 @@ body.dark .mode-selector .btn2.selected {
2317
3140
  })
2318
3141
  }
2319
3142
  let init = document.querySelector("[data-init]")
2320
- console.log("init")
2321
3143
  if (init) {
2322
3144
  init.click()
2323
3145
  }
@@ -2331,10 +3153,13 @@ body.dark .mode-selector .btn2.selected {
2331
3153
 
2332
3154
  // only process the event it's coming from pinokio
2333
3155
  let origin = event.origin
3156
+ console.log("origin", origin)
2334
3157
  if (origin) {
2335
3158
  let port = new URL(origin).port || 80
2336
3159
  console.log("port", port)
2337
- if (String(port) === "<%=port%>") {
3160
+ if (String(port) === String(location.port) || /https:\/\/pinokio\..*localhost/.test(origin)) {
3161
+ //if (String(port) === "<%=port%>" || /https:\/\/pinokio\..*localhost/.test(origin)) {
3162
+ //if (String(port) === "<%=port%>") {
2338
3163
  console.log("Message received from the child: ", event); // Message received from child
2339
3164
  if (event.data) {
2340
3165
  if (event.data.action) {
@@ -2356,6 +3181,14 @@ body.dark .mode-selector .btn2.selected {
2356
3181
  document.querySelector("#location").value = pathname
2357
3182
  }
2358
3183
  }
3184
+ } else if (event.data.launch) {
3185
+ debugger
3186
+ global_selector = `.frame-link[target="${event.data.launch.name}"]`
3187
+ let s = document.querySelector(global_selector)
3188
+ if (!s) {
3189
+ create_iframe(event.data.launch.name, event.data.launch.href)
3190
+ }
3191
+ refresh()
2359
3192
  } else if (event.data.type) {
2360
3193
  let asideHidden = (window.getComputedStyle(document.querySelector("aside")).display === "none")
2361
3194
  console.log("asideHidden", asideHidden)
@@ -2365,20 +3198,6 @@ body.dark .mode-selector .btn2.selected {
2365
3198
  if (event.data.type === 'disconnect') {
2366
3199
  console.log("Refresh 1")
2367
3200
  refresh()
2368
- // } else if (event.data.type === 'start') {
2369
- // // script start => select the tab
2370
- // started_script = event.data.id
2371
- // /*
2372
- // id := /Users/x/pinokio/quick/system/claude/pinokio.js
2373
- // need to transform to /run/Dia/system/claude/pinokio.js
2374
- // */
2375
- // let run_frame = document.querySelector(".browserview iframe[name=run_frame]")
2376
- // const url = run_frame.contentWindow.location.pathname;
2377
- // console.log("url", url)
2378
- // //run_frame.name = event.data.id
2379
- // run_frame.name = url
2380
- //// run_frame.remove()
2381
- // refresh()
2382
3201
  } else {
2383
3202
  console.log("Refresh 2")
2384
3203
  refresh()
@@ -2402,23 +3221,29 @@ body.dark .mode-selector .btn2.selected {
2402
3221
  });
2403
3222
  refresh_du()
2404
3223
  refresh_du("logs")
2405
- fetch("/state/<%=type%>/<%=name%>").then((res) => {
2406
- return res.json()
2407
- }).then((res) => {
2408
- console.log("STATE", res)
2409
- let selection
2410
- if (res && res.selected) {
2411
- selection = document.querySelector(`[href='${res.selected}']`)
2412
- } else {
2413
- selection = document.querySelector("#devtab")
2414
- }
2415
- console.log("CLICK", selection)
2416
- selection.click()
3224
+ let frame_key = window.frameElement?.name || "";
3225
+
3226
+ let selection_url = sessionStorage.getItem(frame_key + ":url")
3227
+ console.log({ frame_key, selection_url })
3228
+ let selection
3229
+ if (selection_url) {
3230
+ selection = document.querySelector(`[href='${selection_url}']`)
3231
+ } else {
3232
+ selection = document.querySelector("#devtab")
3233
+ }
3234
+ console.log("CLICK", selection)
3235
+ if (selection) {
2417
3236
  // setTimeout(() => {
2418
- // alert("click")
2419
- // document.querySelector(".selected").click()
2420
- // }, 500)
2421
- })
3237
+ selection.click()
3238
+ // }, 100)
3239
+ }
3240
+ let window_mode = sessionStorage.getItem(frame_key + ":window_mode")
3241
+ console.log({ window_mode })
3242
+ if (window_mode) {
3243
+ if (window_mode === "minimized") {
3244
+ document.body.classList.add("minimized")
3245
+ }
3246
+ }
2422
3247
  <% if (type !== 'run') { %>
2423
3248
  fetch("<%=repos%>").then((res) => {
2424
3249
  return res.text()
@@ -2469,6 +3294,577 @@ body.dark .mode-selector .btn2.selected {
2469
3294
  }
2470
3295
  });
2471
3296
  */
3297
+ <% if (type === "browse") { %>
3298
+ let currentChanges = []
3299
+ let gitCommitUrl = null
3300
+
3301
+ const check_git = () => {
3302
+ console.log('check_git called, fetching from:', "<%=git_monitor_url%>")
3303
+ fetch("<%=git_monitor_url%>").then((res) => {
3304
+ console.log('check_git response status:', res.status)
3305
+ return res.json()
3306
+ }).then((res) => {
3307
+ console.log('check_git response data:', res)
3308
+ currentChanges = res.changes || []
3309
+ gitCommitUrl = res.git_commit_url || null
3310
+ console.log('currentChanges updated to:', currentChanges)
3311
+ const badgeElement = document.querySelector("#fs-changes-btn .badge")
3312
+ console.log('Badge element found:', badgeElement)
3313
+ if (res.changes && res.changes.length > 0) {
3314
+ console.log('Setting badge to:', res.changes.length)
3315
+ badgeElement.innerHTML = `${res.changes.length}`
3316
+ } else {
3317
+ console.log('Clearing badge')
3318
+ badgeElement.innerHTML = ''
3319
+ }
3320
+ }).catch((error) => {
3321
+ console.error('check_git error:', error)
3322
+ })
3323
+ }
3324
+
3325
+ let messageListener = null
3326
+
3327
+ const showIframeView = (src) => {
3328
+ const iframeHtml = `<iframe src="${src}" style="width: 100%; height: 100%; border: none; display: block;" frameborder="0"></iframe>`
3329
+
3330
+ // Update the modal content to show iframe
3331
+ const htmlContainer = document.querySelector('.pinokio-git-diff-modal-wrapper .swal2-html-container')
3332
+ if (htmlContainer) {
3333
+ htmlContainer.innerHTML = iframeHtml
3334
+ }
3335
+
3336
+ // Add postMessage listener for iframe communication
3337
+ if (messageListener) {
3338
+ window.removeEventListener('message', messageListener)
3339
+ }
3340
+
3341
+ messageListener = (event) => {
3342
+ console.log('Received postMessage:', event.data)
3343
+
3344
+ // Check if event has callback data that starts with "$"
3345
+ if (event.data &&
3346
+ event.data.callback &&
3347
+ typeof event.data.callback === 'string' &&
3348
+ event.data.callback.startsWith('$')) {
3349
+
3350
+ console.log('Callback starts with $, closing modal')
3351
+
3352
+ // Close the modal
3353
+ Swal.close()
3354
+
3355
+ // Clean up the listener
3356
+ if (messageListener) {
3357
+ window.removeEventListener('message', messageListener)
3358
+ messageListener = null
3359
+ }
3360
+ }
3361
+ }
3362
+
3363
+ window.addEventListener('message', messageListener)
3364
+ }
3365
+
3366
+ const generateCommitMessage = (changes) => {
3367
+ if (!changes || changes.length === 0) return "Update files"
3368
+
3369
+ const addedFiles = []
3370
+ const modifiedFiles = []
3371
+ const deletedFiles = []
3372
+ const renamedFiles = []
3373
+
3374
+ changes.forEach(change => {
3375
+ const status = change.status.toLowerCase()
3376
+ const fileName = change.file
3377
+
3378
+ if (status.includes('new') || status.includes('add')) {
3379
+ addedFiles.push(fileName)
3380
+ } else if (status.includes('delete') || status.includes('remove')) {
3381
+ deletedFiles.push(fileName)
3382
+ } else if (status.includes('rename') || status.includes('moved')) {
3383
+ renamedFiles.push(fileName)
3384
+ } else {
3385
+ modifiedFiles.push(fileName)
3386
+ }
3387
+ })
3388
+
3389
+ const messages = []
3390
+
3391
+ if (addedFiles.length > 0) {
3392
+ if (addedFiles.length === 1) {
3393
+ messages.push(`Add ${addedFiles[0]}`)
3394
+ } else {
3395
+ messages.push(`Add ${addedFiles.length} files`)
3396
+ }
3397
+ }
3398
+
3399
+ if (modifiedFiles.length > 0) {
3400
+ if (modifiedFiles.length === 1) {
3401
+ messages.push(`Update ${modifiedFiles[0]}`)
3402
+ } else {
3403
+ messages.push(`Update ${modifiedFiles.length} files`)
3404
+ }
3405
+ }
3406
+
3407
+ if (deletedFiles.length > 0) {
3408
+ if (deletedFiles.length === 1) {
3409
+ messages.push(`Delete ${deletedFiles[0]}`)
3410
+ } else {
3411
+ messages.push(`Delete ${deletedFiles.length} files`)
3412
+ }
3413
+ }
3414
+
3415
+ if (renamedFiles.length > 0) {
3416
+ if (renamedFiles.length === 1) {
3417
+ messages.push(`Rename ${renamedFiles[0]}`)
3418
+ } else {
3419
+ messages.push(`Rename ${renamedFiles.length} files`)
3420
+ }
3421
+ }
3422
+
3423
+ if (messages.length === 0) {
3424
+ return changes.length === 1 ? `Update ${changes[0].file}` : `Update ${changes.length} files`
3425
+ }
3426
+
3427
+ return messages.join(', ')
3428
+ }
3429
+
3430
+ const showGitDiffModal = async (diffData = null, modalTitle = 'File Changes', showSaveButton = true) => {
3431
+ console.log('showGitDiffModal called with:', { diffData, modalTitle, showSaveButton })
3432
+ console.log('currentChanges:', currentChanges)
3433
+
3434
+ let changes = diffData ? (diffData.changes || []) : currentChanges
3435
+ let commitUrl = diffData ? (diffData.git_commit_url || null) : gitCommitUrl
3436
+
3437
+ console.log('Initial changes:', changes)
3438
+
3439
+ // If no diffData provided and currentChanges is empty, try to fetch fresh data
3440
+ if (!diffData && changes.length === 0) {
3441
+ console.log('Fetching fresh data because changes is empty')
3442
+ try {
3443
+ const response = await fetch("<%=git_monitor_url%>")
3444
+ const freshData = await response.json()
3445
+ console.log('Fresh data received:', freshData)
3446
+ changes = freshData.changes || []
3447
+ commitUrl = freshData.git_commit_url || null
3448
+ currentChanges = changes // Update the global variable
3449
+ gitCommitUrl = commitUrl
3450
+ } catch (error) {
3451
+ console.error('Failed to fetch current changes:', error)
3452
+ }
3453
+ }
3454
+
3455
+ console.log('Final changes before modal:', changes)
3456
+
3457
+ if (changes.length === 0) {
3458
+ console.log('No changes found - showing info dialog')
3459
+ Swal.fire({
3460
+ title: 'No Changes',
3461
+ text: 'No file changes detected.',
3462
+ icon: 'info'
3463
+ })
3464
+ return
3465
+ }
3466
+
3467
+ console.log('About to show modal with changes:', changes.length)
3468
+
3469
+ const modalHtml = `
3470
+ <div class="pinokio-git-diff-main-container">
3471
+ <div class="pinokio-git-diff-file-list-panel">
3472
+ ${changes.map((change, index) => `
3473
+ <div class="pinokio-git-diff-file-item-row" data-index="${index}" data-diffpath="${change.diffpath}">
3474
+ <div style="font-weight: bold; margin-bottom: 2px;">${change.file}</div>
3475
+ <div style="font-size: 10px; color: #656d76;">${change.status}</div>
3476
+ </div>
3477
+ `).join('')}
3478
+ </div>
3479
+ <div class="pinokio-git-diff-viewer-panel">
3480
+ <div class="pinokio-git-diff-empty-state">Select a file to view its changes</div>
3481
+ </div>
3482
+ </div>
3483
+ `
3484
+
3485
+ const modalConfig = {
3486
+ title: modalTitle,
3487
+ html: modalHtml,
3488
+ customClass: {
3489
+ popup: showSaveButton ? 'pinokio-git-diff-modal-wrapper' : 'pinokio-git-diff-modal-wrapper pinokio-git-commit-diff-overlay'
3490
+ },
3491
+ showConfirmButton: showSaveButton,
3492
+ showCancelButton: false,
3493
+ showCloseButton: true,
3494
+ footer: showSaveButton ? `
3495
+ <div class="pinokio-commit-message-container">
3496
+ <input type="text" class="pinokio-commit-message-input" placeholder="Enter commit message..." value="${generateCommitMessage(changes)}">
3497
+ <button type="button" class="pinokio-save-version-btn">Save version</button>
3498
+ </div>
3499
+ ` : null
3500
+ }
3501
+
3502
+ if (showSaveButton) {
3503
+ modalConfig.showConfirmButton = false // Hide default confirm button since we have custom footer
3504
+ }
3505
+
3506
+
3507
+ modalConfig.willClose = () => {
3508
+ // Clean up message listener when modal closes
3509
+ if (messageListener) {
3510
+ window.removeEventListener('message', messageListener)
3511
+ messageListener = null
3512
+ }
3513
+ }
3514
+
3515
+ modalConfig.didOpen = () => {
3516
+ const fileItems = document.querySelectorAll('.pinokio-git-diff-file-item-row')
3517
+ const diffViewer = document.querySelector('.pinokio-git-diff-viewer-panel')
3518
+
3519
+ // Handle custom save button click
3520
+ const saveBtn = document.querySelector('.pinokio-save-version-btn')
3521
+ if (saveBtn) {
3522
+ saveBtn.addEventListener('click', () => {
3523
+ const messageInput = document.querySelector('.pinokio-commit-message-input')
3524
+ const commitMessage = messageInput ? messageInput.value.trim() : ''
3525
+
3526
+ if (commitUrl) {
3527
+ // Add the message parameter to the URL
3528
+ const url = new URL(commitUrl, window.location.origin)
3529
+ if (commitMessage) {
3530
+ url.searchParams.set('message', commitMessage)
3531
+ }
3532
+ showIframeView(url.toString())
3533
+ } else {
3534
+ Swal.showValidationMessage('No commit URL available')
3535
+ }
3536
+ })
3537
+ }
3538
+
3539
+ Swal.getPopup().addEventListener('message', (event) => {
3540
+ console.log("EVENT", event)
3541
+ // if (event.data) {
3542
+ // if (event.data.callback) {
3543
+ // if (event.data.callback.startsWith("$")) {
3544
+ // location.href = eval(event.data.callback.slice(1))
3545
+ // } else {
3546
+ // location.href = event.data.callback
3547
+ // }
3548
+ // }
3549
+ // }
3550
+ })
3551
+
3552
+
3553
+
3554
+ fileItems.forEach(item => {
3555
+ item.addEventListener('click', async () => {
3556
+ // Remove active class from all items
3557
+ fileItems.forEach(i => i.classList.remove('pinokio-active-file-item'))
3558
+ // Add active class to clicked item
3559
+ item.classList.add('pinokio-active-file-item')
3560
+
3561
+ const diffpath = item.getAttribute('data-diffpath')
3562
+ diffViewer.innerHTML = '<div class="pinokio-git-diff-empty-state">Loading...</div>'
3563
+
3564
+ try {
3565
+ const diffRes = await fetch(diffpath)
3566
+ const diffData = await diffRes.json()
3567
+ renderDiff(diffData, diffViewer)
3568
+ } catch (error) {
3569
+ console.error('Error fetching diff:', error)
3570
+ diffViewer.innerHTML = '<div class="pinokio-git-diff-empty-state">Error loading diff</div>'
3571
+ }
3572
+ })
3573
+ })
3574
+ }
3575
+
3576
+ const result = await Swal.fire(modalConfig)
3577
+ }
3578
+
3579
+ const renderDiff = (diffData, container) => {
3580
+ if (!diffData.diff || diffData.diff.length === 0) {
3581
+ container.innerHTML = '<div class="pinokio-git-diff-empty-state">No changes to display</div>'
3582
+ return
3583
+ }
3584
+
3585
+ const diffHtml = diffData.diff.map(line => {
3586
+ const lineNumbers = line.lineOld && line.lineNew
3587
+ ? `${line.lineOld || ''} ${line.lineNew || ''}`
3588
+ : line.lineOld
3589
+ ? `${line.lineOld} `
3590
+ : ` ${line.lineNew || ''}`
3591
+
3592
+ let cssClass = 'pinokio-diff-code-line'
3593
+ if (line.type === 'add') {
3594
+ cssClass += ' pinokio-diff-code-line-addition'
3595
+ } else if (line.type === 'del') {
3596
+ cssClass += ' pinokio-diff-code-line-deletion'
3597
+ } else {
3598
+ cssClass += ' pinokio-diff-code-line-context'
3599
+ }
3600
+
3601
+ const escapedLine = line.line
3602
+ .replace(/&/g, '&amp;')
3603
+ .replace(/</g, '&lt;')
3604
+ .replace(/>/g, '&gt;')
3605
+
3606
+ return `<div class="${cssClass}">
3607
+ <span class="pinokio-diff-code-line-numbers">${lineNumbers}</span>
3608
+ <span class="pinokio-diff-code-line-content">${escapedLine}</span>
3609
+ </div>`
3610
+ }).join('')
3611
+
3612
+ container.innerHTML = diffHtml
3613
+ }
3614
+
3615
+ const showGitHistoryModal = async () => {
3616
+ const historyUri = document.querySelector('#fs-status').getAttribute('data-history-uri')
3617
+ if (!historyUri) {
3618
+ Swal.fire({
3619
+ title: 'Error',
3620
+ text: 'Git history URL not available.',
3621
+ icon: 'error'
3622
+ })
3623
+ return
3624
+ }
3625
+
3626
+ try {
3627
+ const response = await fetch(historyUri)
3628
+ if (!response.ok) {
3629
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`)
3630
+ }
3631
+
3632
+ const historyData = await response.json()
3633
+ displayGitHistory(historyData)
3634
+
3635
+ } catch (error) {
3636
+ console.error('Failed to load git history:', error)
3637
+ Swal.fire({
3638
+ title: 'Error',
3639
+ text: `Failed to load git history: ${error.message}`,
3640
+ icon: 'error'
3641
+ })
3642
+ }
3643
+ }
3644
+
3645
+ const displayGitHistory = (historyData) => {
3646
+ const commits = historyData.log || []
3647
+ const remote = historyData.remote || ''
3648
+ const currentRef = historyData.ref || 'HEAD'
3649
+
3650
+ const historyHtml = `
3651
+ <div class="pinokio-git-history-container">
3652
+ <div class="pinokio-git-history-header">
3653
+ <div class="pinokio-git-history-info">
3654
+ <div class="pinokio-git-history-ref">
3655
+ <i class="fa-solid fa-code-branch"></i>
3656
+ <span>${currentRef}</span>
3657
+ </div>
3658
+ ${remote ? `<div class="pinokio-git-history-remote">
3659
+ <i class="fa-solid fa-globe"></i>
3660
+ <span>${remote}</span>
3661
+ </div>` : ''}
3662
+ </div>
3663
+ </div>
3664
+ <div class="pinokio-git-history-list">
3665
+ ${commits.length === 0 ?
3666
+ '<div class="pinokio-git-history-empty">No commits found</div>' :
3667
+ commits.map(commit => createCommitItem(commit)).join('')
3668
+ }
3669
+ </div>
3670
+ </div>
3671
+ `
3672
+
3673
+ Swal.fire({
3674
+ title: `Git History (${commits.length} commits)`,
3675
+ html: historyHtml,
3676
+ customClass: {
3677
+ popup: 'pinokio-git-history-modal-wrapper'
3678
+ },
3679
+ showConfirmButton: false,
3680
+ showCloseButton: true,
3681
+ width: '90vw',
3682
+ heightAuto: false,
3683
+ didOpen: () => {
3684
+ // Add click handlers to commit items
3685
+ const commitItems = document.querySelectorAll('.pinokio-git-commit-item[data-commit-info]')
3686
+ commitItems.forEach(item => {
3687
+ item.addEventListener('click', async (e) => {
3688
+ const commitInfo = item.getAttribute('data-commit-info')
3689
+ if (commitInfo) {
3690
+ await showCommitDiffModal(commitInfo)
3691
+ }
3692
+ })
3693
+ })
3694
+ }
3695
+ })
3696
+ }
3697
+
3698
+ const showCommitDiffModal = async (commitInfoUrl) => {
3699
+ if (!commitInfoUrl) {
3700
+ Swal.fire({
3701
+ title: 'Error',
3702
+ text: 'Commit information URL not available.',
3703
+ icon: 'error'
3704
+ })
3705
+ return
3706
+ }
3707
+
3708
+ try {
3709
+ const response = await fetch(commitInfoUrl)
3710
+ if (!response.ok) {
3711
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`)
3712
+ }
3713
+
3714
+ const diffData = await response.json()
3715
+ await showCommitDiffModalQueued(diffData)
3716
+
3717
+ } catch (error) {
3718
+ console.error('Failed to load commit diff:', error)
3719
+ Swal.fire({
3720
+ title: 'Error',
3721
+ text: `Failed to load commit changes: ${error.message}`,
3722
+ icon: 'error'
3723
+ })
3724
+ }
3725
+ }
3726
+
3727
+ const showCommitDiffModalQueued = async (diffData) => {
3728
+ const changes = diffData.changes || []
3729
+
3730
+ if (changes.length === 0) {
3731
+ alert('No file changes detected.')
3732
+ return
3733
+ }
3734
+
3735
+ // Create custom overlay modal
3736
+ const overlay = document.createElement('div')
3737
+ overlay.className = 'pinokio-custom-commit-overlay'
3738
+ overlay.innerHTML = `
3739
+ <div class="pinokio-custom-commit-modal">
3740
+ <div class="pinokio-custom-commit-header">
3741
+ <h3>Commit Changes</h3>
3742
+ <button class="pinokio-custom-commit-close">×</button>
3743
+ </div>
3744
+ <div class="pinokio-custom-commit-content">
3745
+ <div class="pinokio-git-diff-main-container">
3746
+ <div class="pinokio-git-diff-file-list-panel">
3747
+ ${changes.map((change, index) => `
3748
+ <div class="pinokio-git-diff-file-item-row" data-index="${index}" data-diffpath="${change.diffpath}">
3749
+ <div style="font-weight: bold; margin-bottom: 2px;">${change.file}</div>
3750
+ <div style="font-size: 10px; color: #656d76;">${change.status}</div>
3751
+ </div>
3752
+ `).join('')}
3753
+ </div>
3754
+ <div class="pinokio-git-diff-viewer-panel">
3755
+ <div class="pinokio-git-diff-empty-state">Select a file to view its changes</div>
3756
+ </div>
3757
+ </div>
3758
+ </div>
3759
+ </div>
3760
+ `
3761
+
3762
+ document.body.appendChild(overlay)
3763
+
3764
+ // Add close handler
3765
+ const closeBtn = overlay.querySelector('.pinokio-custom-commit-close')
3766
+ closeBtn.addEventListener('click', () => {
3767
+ document.body.removeChild(overlay)
3768
+ })
3769
+
3770
+ // Close on overlay click
3771
+ overlay.addEventListener('click', (e) => {
3772
+ if (e.target === overlay) {
3773
+ document.body.removeChild(overlay)
3774
+ }
3775
+ })
3776
+
3777
+ // Add file click handlers
3778
+ const fileItems = overlay.querySelectorAll('.pinokio-git-diff-file-item-row')
3779
+ const diffViewer = overlay.querySelector('.pinokio-git-diff-viewer-panel')
3780
+
3781
+ fileItems.forEach(item => {
3782
+ item.addEventListener('click', async () => {
3783
+ // Remove active class from all items
3784
+ fileItems.forEach(i => i.classList.remove('pinokio-active-file-item'))
3785
+ // Add active class to clicked item
3786
+ item.classList.add('pinokio-active-file-item')
3787
+
3788
+ const diffpath = item.getAttribute('data-diffpath')
3789
+ diffViewer.innerHTML = '<div class="pinokio-git-diff-empty-state">Loading...</div>'
3790
+
3791
+ try {
3792
+ const diffRes = await fetch(diffpath)
3793
+ const diffData = await diffRes.json()
3794
+ renderDiff(diffData, diffViewer)
3795
+ } catch (error) {
3796
+ console.error('Error fetching diff:', error)
3797
+ diffViewer.innerHTML = '<div class="pinokio-git-diff-empty-state">Error loading diff</div>'
3798
+ }
3799
+ })
3800
+ })
3801
+
3802
+ // Handle escape key
3803
+ const escHandler = (e) => {
3804
+ if (e.key === 'Escape') {
3805
+ document.body.removeChild(overlay)
3806
+ document.removeEventListener('keydown', escHandler)
3807
+ }
3808
+ }
3809
+ document.addEventListener('keydown', escHandler)
3810
+ }
3811
+
3812
+ const createCommitItem = (commitData) => {
3813
+ const commit = commitData.commit
3814
+ const oid = commitData.oid
3815
+ const info = commitData.info || ''
3816
+ const author = commit.author
3817
+ const message = commit.message.trim()
3818
+ const shortMessage = message.split('\n')[0]
3819
+ const timestamp = new Date(author.timestamp * 1000)
3820
+ const timeAgo = (typeof timeago !== 'undefined' && timeago) ? timeago.format(timestamp) : timestamp.toLocaleDateString()
3821
+
3822
+ return `
3823
+ <div class="pinokio-git-commit-item" data-commit-info="${info}" style="cursor: pointer;">
3824
+ <div class="pinokio-git-commit-avatar">
3825
+ <i class="fa-solid fa-user"></i>
3826
+ </div>
3827
+ <div class="pinokio-git-commit-content">
3828
+ <div class="pinokio-git-commit-message" title="${message.replace(/"/g, '&quot;')}">${shortMessage}</div>
3829
+ <div class="pinokio-git-commit-meta">
3830
+ <span class="pinokio-git-commit-author">${author.name}</span>
3831
+ <span class="pinokio-git-commit-time">${timeAgo}</span>
3832
+ <span class="pinokio-git-commit-hash" title="${oid}">${oid.substring(0, 7)}</span>
3833
+ </div>
3834
+ </div>
3835
+ </div>
3836
+ `
3837
+ }
3838
+
3839
+ // Add click handlers for the buttons
3840
+ document.querySelector('#fs-changes-btn').addEventListener('click', () => showGitDiffModal())
3841
+ document.querySelector('#fs-history-btn').addEventListener('click', showGitHistoryModal)
3842
+
3843
+ check_git()
3844
+ setInterval(check_git, 10000)
3845
+ <% } %>
3846
+
3847
+ setInterval(() => {
3848
+ try {
3849
+ let foreground_frame = document.querySelector("iframe:not(.hidden)")
3850
+ let selected_tab = document.querySelector("aside .frame-link.selected")
3851
+ let needs_selection
3852
+ if (selected_tab) {
3853
+ if (selected_tab.target === foreground_frame.name) {
3854
+ needs_selection = false
3855
+ } else {
3856
+ needs_selection = true
3857
+ }
3858
+ } else {
3859
+ needs_selection = true
3860
+ }
3861
+ if (needs_selection) {
3862
+ let selection_tab = document.querySelector(`aside .frame-link[target="${foreground_frame.name}"]`)
3863
+ selection_tab.click()
3864
+ }
3865
+ } catch (e) {
3866
+ }
3867
+ }, 1000)
2472
3868
 
2473
3869
  </script>
2474
3870
  <% } %>