pinokiod 7.5.44 → 7.5.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/server/index.js +403 -24
- package/server/public/style.css +0 -264
- package/server/public/urldropdown.css +7 -0
- package/server/views/app.ejs +2 -0
- package/server/views/app_search_test.ejs +2 -0
- package/server/views/connect/x.ejs +2 -0
- package/server/views/container.ejs +2 -0
- package/server/views/create.ejs +3 -0
- package/server/views/editor.ejs +3 -0
- package/server/views/env_editor.ejs +2 -0
- package/server/views/explore.ejs +2 -0
- package/server/views/general_editor.ejs +3 -0
- package/server/views/help.ejs +2 -0
- package/server/views/index.ejs +539 -2
- package/server/views/init/index.ejs +2 -0
- package/server/views/keys.ejs +2 -0
- package/server/views/mini.ejs +2 -0
- package/server/views/net.ejs +2 -0
- package/server/views/network.ejs +887 -94
- package/server/views/partials/app_navheader.ejs +2 -0
- package/server/views/partials/home_action_modal.ejs +22 -0
- package/server/views/partials/home_server_popover.ejs +21 -0
- package/server/views/partials/home_server_popover_assets.ejs +1262 -0
- package/server/views/partials/main_sidebar.ejs +1 -221
- package/server/views/pro.ejs +7 -0
- package/server/views/prototype/index.ejs +2 -0
- package/server/views/review.ejs +2 -0
- package/server/views/screenshots.ejs +2 -0
- package/server/views/settings.ejs +2 -0
- package/server/views/shell.ejs +7 -0
- package/server/views/task.ejs +2 -0
- package/server/views/terminal.ejs +2 -0
- package/server/views/terminals.ejs +2 -0
- package/test/launch-settings-ui.test.js +355 -0
- package/test/main-sidebar.test.js +26 -16
package/server/views/index.ejs
CHANGED
|
@@ -197,12 +197,14 @@ body.dark .context-menu-wrapper {
|
|
|
197
197
|
text-transform: none;
|
|
198
198
|
transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
|
|
199
199
|
}
|
|
200
|
-
.home-app-line .menu-btns > .open-actions-modal
|
|
200
|
+
.home-app-line .menu-btns > .open-actions-modal,
|
|
201
|
+
.home-app-line .menu-btns > .open-app-share-modal {
|
|
201
202
|
width: 28px;
|
|
202
203
|
min-width: 28px;
|
|
203
204
|
padding: 0 !important;
|
|
204
205
|
}
|
|
205
|
-
.home-app-line .menu-btns > .open-actions-modal i
|
|
206
|
+
.home-app-line .menu-btns > .open-actions-modal i,
|
|
207
|
+
.home-app-line .menu-btns > .open-app-share-modal i {
|
|
206
208
|
width: 14px;
|
|
207
209
|
font-size: 13px;
|
|
208
210
|
}
|
|
@@ -859,6 +861,291 @@ body.dark .menu-btns .home-icon-btn:focus-visible {
|
|
|
859
861
|
body.dark .menu-btns .toggle-star.is-starred {
|
|
860
862
|
color: #ffcc66 !important;
|
|
861
863
|
}
|
|
864
|
+
.home-app-share-swal.swal2-popup {
|
|
865
|
+
width: min(620px, calc(100vw - 32px));
|
|
866
|
+
padding: 0;
|
|
867
|
+
border-radius: 10px;
|
|
868
|
+
}
|
|
869
|
+
.home-app-share-swal .swal2-close {
|
|
870
|
+
width: 34px;
|
|
871
|
+
height: 34px;
|
|
872
|
+
margin: 16px 16px 0 0;
|
|
873
|
+
border-radius: 8px;
|
|
874
|
+
color: rgba(15, 23, 42, 0.48);
|
|
875
|
+
font-size: 28px;
|
|
876
|
+
line-height: 34px;
|
|
877
|
+
transition: background-color 120ms ease, color 120ms ease, box-shadow 120ms ease;
|
|
878
|
+
}
|
|
879
|
+
.home-app-share-swal .swal2-close:hover,
|
|
880
|
+
.home-app-share-swal .swal2-close:focus-visible {
|
|
881
|
+
background: rgba(15, 23, 42, 0.055);
|
|
882
|
+
color: rgba(15, 23, 42, 0.74);
|
|
883
|
+
box-shadow: none;
|
|
884
|
+
}
|
|
885
|
+
body.dark .home-app-share-swal .swal2-close {
|
|
886
|
+
color: rgba(226, 232, 240, 0.5);
|
|
887
|
+
}
|
|
888
|
+
body.dark .home-app-share-swal .swal2-close:hover,
|
|
889
|
+
body.dark .home-app-share-swal .swal2-close:focus-visible {
|
|
890
|
+
background: rgba(255, 255, 255, 0.07);
|
|
891
|
+
color: rgba(226, 232, 240, 0.78);
|
|
892
|
+
}
|
|
893
|
+
.home-app-share-swal .swal2-title {
|
|
894
|
+
display: flex !important;
|
|
895
|
+
align-items: flex-start;
|
|
896
|
+
gap: 11px;
|
|
897
|
+
margin: 0;
|
|
898
|
+
padding: 30px 64px 0 30px;
|
|
899
|
+
text-align: left;
|
|
900
|
+
font-size: 21px;
|
|
901
|
+
line-height: 1.22;
|
|
902
|
+
}
|
|
903
|
+
.home-app-share-swal .swal2-html-container {
|
|
904
|
+
margin: 0;
|
|
905
|
+
padding: 20px 30px 30px;
|
|
906
|
+
overflow: visible;
|
|
907
|
+
text-align: left;
|
|
908
|
+
}
|
|
909
|
+
.home-app-share-modal__title-icon {
|
|
910
|
+
width: 30px;
|
|
911
|
+
height: 30px;
|
|
912
|
+
min-width: 30px;
|
|
913
|
+
display: inline-flex;
|
|
914
|
+
align-items: center;
|
|
915
|
+
justify-content: center;
|
|
916
|
+
margin-top: 1px;
|
|
917
|
+
border: 1px solid rgba(15, 23, 42, 0.1);
|
|
918
|
+
border-radius: 7px;
|
|
919
|
+
background: rgba(248, 250, 252, 0.74);
|
|
920
|
+
color: rgba(15, 23, 42, 0.72);
|
|
921
|
+
font-size: 14px;
|
|
922
|
+
line-height: 1;
|
|
923
|
+
}
|
|
924
|
+
.home-app-share-modal__title-copy {
|
|
925
|
+
min-width: 0;
|
|
926
|
+
display: flex;
|
|
927
|
+
flex-direction: column;
|
|
928
|
+
gap: 5px;
|
|
929
|
+
}
|
|
930
|
+
.home-app-share-modal__title-text {
|
|
931
|
+
min-width: 0;
|
|
932
|
+
}
|
|
933
|
+
.home-app-share-modal__title-meta {
|
|
934
|
+
width: fit-content;
|
|
935
|
+
padding: 2px 7px;
|
|
936
|
+
border: 1px solid rgba(15, 23, 42, 0.08);
|
|
937
|
+
border-radius: 999px;
|
|
938
|
+
background: rgba(15, 23, 42, 0.035);
|
|
939
|
+
color: rgba(15, 23, 42, 0.56);
|
|
940
|
+
font-size: 11px;
|
|
941
|
+
font-weight: 700;
|
|
942
|
+
line-height: 1.35;
|
|
943
|
+
}
|
|
944
|
+
body.dark .home-app-share-modal__title-icon {
|
|
945
|
+
border-color: rgba(255, 255, 255, 0.09);
|
|
946
|
+
background: rgba(255, 255, 255, 0.05);
|
|
947
|
+
color: rgba(226, 232, 240, 0.78);
|
|
948
|
+
}
|
|
949
|
+
body.dark .home-app-share-modal__title-meta {
|
|
950
|
+
border-color: rgba(255, 255, 255, 0.08);
|
|
951
|
+
background: rgba(255, 255, 255, 0.05);
|
|
952
|
+
color: rgba(226, 232, 240, 0.58);
|
|
953
|
+
}
|
|
954
|
+
.home-app-share-modal {
|
|
955
|
+
display: flex;
|
|
956
|
+
flex-direction: column;
|
|
957
|
+
gap: 12px;
|
|
958
|
+
text-align: left;
|
|
959
|
+
}
|
|
960
|
+
.home-app-share-modal__intro {
|
|
961
|
+
color: rgba(15, 23, 42, 0.68);
|
|
962
|
+
font-size: 13px;
|
|
963
|
+
line-height: 1.45;
|
|
964
|
+
}
|
|
965
|
+
body.dark .home-app-share-modal__intro {
|
|
966
|
+
color: rgba(226, 232, 240, 0.7);
|
|
967
|
+
}
|
|
968
|
+
.home-app-share-modal__scan-card {
|
|
969
|
+
display: grid;
|
|
970
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
971
|
+
align-items: center;
|
|
972
|
+
gap: 18px;
|
|
973
|
+
padding: 14px;
|
|
974
|
+
border: 1px solid rgba(15, 23, 42, 0.08);
|
|
975
|
+
border-radius: 8px;
|
|
976
|
+
background: rgba(248, 250, 252, 0.72);
|
|
977
|
+
}
|
|
978
|
+
body.dark .home-app-share-modal__scan-card {
|
|
979
|
+
border-color: rgba(255, 255, 255, 0.08);
|
|
980
|
+
background: rgba(255, 255, 255, 0.045);
|
|
981
|
+
}
|
|
982
|
+
.home-app-share-modal__scan-copy {
|
|
983
|
+
display: flex;
|
|
984
|
+
flex-direction: column;
|
|
985
|
+
gap: 5px;
|
|
986
|
+
}
|
|
987
|
+
.home-app-share-modal__eyebrow {
|
|
988
|
+
color: rgba(15, 23, 42, 0.88);
|
|
989
|
+
font-size: 13px;
|
|
990
|
+
font-weight: 800;
|
|
991
|
+
line-height: 1.3;
|
|
992
|
+
}
|
|
993
|
+
body.dark .home-app-share-modal__eyebrow {
|
|
994
|
+
color: rgba(226, 232, 240, 0.9);
|
|
995
|
+
}
|
|
996
|
+
.home-app-share-modal__qr {
|
|
997
|
+
display: flex;
|
|
998
|
+
width: 178px;
|
|
999
|
+
height: 178px;
|
|
1000
|
+
align-items: center;
|
|
1001
|
+
justify-content: center;
|
|
1002
|
+
border: 1px solid rgba(15, 23, 42, 0.09);
|
|
1003
|
+
border-radius: 8px;
|
|
1004
|
+
background: #ffffff;
|
|
1005
|
+
}
|
|
1006
|
+
.home-app-share-modal__qr img {
|
|
1007
|
+
width: 154px;
|
|
1008
|
+
height: 154px;
|
|
1009
|
+
image-rendering: pixelated;
|
|
1010
|
+
}
|
|
1011
|
+
.home-app-share-modal__share-card {
|
|
1012
|
+
display: flex;
|
|
1013
|
+
flex-direction: column;
|
|
1014
|
+
gap: 10px;
|
|
1015
|
+
padding: 10px;
|
|
1016
|
+
border: 1px solid rgba(15, 23, 42, 0.08);
|
|
1017
|
+
border-radius: 8px;
|
|
1018
|
+
background: rgba(255, 255, 255, 0.62);
|
|
1019
|
+
}
|
|
1020
|
+
body.dark .home-app-share-modal__share-card {
|
|
1021
|
+
border-color: rgba(255, 255, 255, 0.08);
|
|
1022
|
+
background: rgba(255, 255, 255, 0.035);
|
|
1023
|
+
}
|
|
1024
|
+
.home-app-share-modal__url {
|
|
1025
|
+
display: flex;
|
|
1026
|
+
align-items: center;
|
|
1027
|
+
gap: 8px;
|
|
1028
|
+
min-width: 0;
|
|
1029
|
+
min-height: 38px;
|
|
1030
|
+
padding: 0 10px;
|
|
1031
|
+
border: 1px solid rgba(15, 23, 42, 0.09);
|
|
1032
|
+
border-radius: 7px;
|
|
1033
|
+
background: rgba(248, 250, 252, 0.82);
|
|
1034
|
+
color: rgba(15, 23, 42, 0.78);
|
|
1035
|
+
font-size: 12px;
|
|
1036
|
+
}
|
|
1037
|
+
body.dark .home-app-share-modal__url {
|
|
1038
|
+
border-color: rgba(255, 255, 255, 0.08);
|
|
1039
|
+
background: rgba(255, 255, 255, 0.045);
|
|
1040
|
+
color: rgba(226, 232, 240, 0.78);
|
|
1041
|
+
}
|
|
1042
|
+
.home-app-share-modal__url span {
|
|
1043
|
+
min-width: 0;
|
|
1044
|
+
overflow: hidden;
|
|
1045
|
+
text-overflow: ellipsis;
|
|
1046
|
+
white-space: nowrap;
|
|
1047
|
+
}
|
|
1048
|
+
.home-app-share-modal__actions {
|
|
1049
|
+
display: flex;
|
|
1050
|
+
flex-wrap: wrap;
|
|
1051
|
+
gap: 8px;
|
|
1052
|
+
}
|
|
1053
|
+
.home-app-share-modal__action {
|
|
1054
|
+
min-height: 36px;
|
|
1055
|
+
display: inline-flex;
|
|
1056
|
+
align-items: center;
|
|
1057
|
+
justify-content: center;
|
|
1058
|
+
gap: 7px;
|
|
1059
|
+
padding: 0 13px;
|
|
1060
|
+
border: 1px solid rgba(15, 23, 42, 0.09);
|
|
1061
|
+
border-radius: 7px;
|
|
1062
|
+
background: rgba(248, 250, 252, 0.72);
|
|
1063
|
+
color: inherit;
|
|
1064
|
+
font-size: 12px;
|
|
1065
|
+
font-weight: 700;
|
|
1066
|
+
text-decoration: none;
|
|
1067
|
+
cursor: pointer;
|
|
1068
|
+
}
|
|
1069
|
+
.home-app-share-modal__action.is-primary {
|
|
1070
|
+
border-color: var(--pinokio-chrome-primary-bg, #252b3a);
|
|
1071
|
+
background: var(--pinokio-chrome-primary-bg, #252b3a);
|
|
1072
|
+
color: #ffffff;
|
|
1073
|
+
}
|
|
1074
|
+
.home-app-share-modal__action:hover,
|
|
1075
|
+
.home-app-share-modal__action:focus-visible {
|
|
1076
|
+
border-color: rgba(15, 23, 42, 0.16);
|
|
1077
|
+
background: rgba(248, 250, 252, 0.95);
|
|
1078
|
+
}
|
|
1079
|
+
.home-app-share-modal__action.is-primary:hover,
|
|
1080
|
+
.home-app-share-modal__action.is-primary:focus-visible {
|
|
1081
|
+
background: rgba(15, 23, 42, 0.9);
|
|
1082
|
+
}
|
|
1083
|
+
body.dark .home-app-share-modal__action {
|
|
1084
|
+
border-color: rgba(255, 255, 255, 0.08);
|
|
1085
|
+
background: rgba(255, 255, 255, 0.045);
|
|
1086
|
+
}
|
|
1087
|
+
body.dark .home-app-share-modal__action.is-primary {
|
|
1088
|
+
border-color: rgba(248, 250, 252, 0.92);
|
|
1089
|
+
background: rgba(248, 250, 252, 0.92);
|
|
1090
|
+
color: rgba(15, 23, 42, 0.94);
|
|
1091
|
+
}
|
|
1092
|
+
body.dark .home-app-share-modal__action:hover,
|
|
1093
|
+
body.dark .home-app-share-modal__action:focus-visible {
|
|
1094
|
+
border-color: rgba(255, 255, 255, 0.14);
|
|
1095
|
+
background: rgba(255, 255, 255, 0.075);
|
|
1096
|
+
}
|
|
1097
|
+
body.dark .home-app-share-modal__action.is-primary:hover,
|
|
1098
|
+
body.dark .home-app-share-modal__action.is-primary:focus-visible {
|
|
1099
|
+
background: rgba(248, 250, 252, 0.84);
|
|
1100
|
+
}
|
|
1101
|
+
.home-app-share-modal__note {
|
|
1102
|
+
display: flex;
|
|
1103
|
+
align-items: center;
|
|
1104
|
+
gap: 7px;
|
|
1105
|
+
color: rgba(15, 23, 42, 0.54);
|
|
1106
|
+
font-size: 12px;
|
|
1107
|
+
line-height: 1.4;
|
|
1108
|
+
}
|
|
1109
|
+
body.dark .home-app-share-modal__note {
|
|
1110
|
+
color: rgba(226, 232, 240, 0.58);
|
|
1111
|
+
}
|
|
1112
|
+
.home-app-share-modal__setup-card {
|
|
1113
|
+
padding: 14px;
|
|
1114
|
+
border: 1px solid rgba(15, 23, 42, 0.08);
|
|
1115
|
+
border-radius: 8px;
|
|
1116
|
+
background: rgba(248, 250, 252, 0.72);
|
|
1117
|
+
}
|
|
1118
|
+
body.dark .home-app-share-modal__setup-card {
|
|
1119
|
+
border-color: rgba(255, 255, 255, 0.08);
|
|
1120
|
+
background: rgba(255, 255, 255, 0.045);
|
|
1121
|
+
}
|
|
1122
|
+
@media (max-width: 560px) {
|
|
1123
|
+
.home-app-share-swal.swal2-popup {
|
|
1124
|
+
width: calc(100vw - 24px);
|
|
1125
|
+
}
|
|
1126
|
+
.home-app-share-swal .swal2-title {
|
|
1127
|
+
padding: 24px 56px 0 22px;
|
|
1128
|
+
}
|
|
1129
|
+
.home-app-share-swal .swal2-html-container {
|
|
1130
|
+
padding: 18px 22px 24px;
|
|
1131
|
+
}
|
|
1132
|
+
.home-app-share-modal__scan-card {
|
|
1133
|
+
grid-template-columns: 1fr;
|
|
1134
|
+
justify-items: center;
|
|
1135
|
+
text-align: center;
|
|
1136
|
+
}
|
|
1137
|
+
.home-app-share-modal__scan-copy {
|
|
1138
|
+
align-items: center;
|
|
1139
|
+
}
|
|
1140
|
+
.home-app-share-modal__qr {
|
|
1141
|
+
width: 166px;
|
|
1142
|
+
height: 166px;
|
|
1143
|
+
}
|
|
1144
|
+
.home-app-share-modal__qr img {
|
|
1145
|
+
width: 144px;
|
|
1146
|
+
height: 144px;
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
862
1149
|
.home-actions-dialog {
|
|
863
1150
|
width: min(760px, calc(100vw - 32px));
|
|
864
1151
|
max-height: min(680px, calc(100vh - 32px));
|
|
@@ -1445,6 +1732,7 @@ body.dark aside .current.selected {
|
|
|
1445
1732
|
<div class='url-dropdown' id='url-dropdown'></div>
|
|
1446
1733
|
</div>
|
|
1447
1734
|
</form>
|
|
1735
|
+
<%- include('partials/home_server_popover') %>
|
|
1448
1736
|
<a class='btn2' href="/columns" data-tippy-content="split into 2 columns">
|
|
1449
1737
|
<div><i class="fa-solid fa-table-columns"></i></div>
|
|
1450
1738
|
</a>
|
|
@@ -1487,6 +1775,7 @@ body.dark aside .current.selected {
|
|
|
1487
1775
|
</div>
|
|
1488
1776
|
<% } %>
|
|
1489
1777
|
</header>
|
|
1778
|
+
<%- include('partials/home_server_popover_assets') %>
|
|
1490
1779
|
<main>
|
|
1491
1780
|
<div id='terminal' class='hidden'></div>
|
|
1492
1781
|
<div class='container'>
|
|
@@ -1547,6 +1836,20 @@ body.dark aside .current.selected {
|
|
|
1547
1836
|
<button class='btn open-menu open-actions-modal' type='button' data-dialog-id="home-actions-running-<%=index%>" aria-haspopup="dialog" aria-expanded="false" title="Open actions panel" aria-label="Open actions panel">
|
|
1548
1837
|
<i class="fa-solid fa-list-ul" aria-hidden="true"></i>
|
|
1549
1838
|
</button>
|
|
1839
|
+
<% const runningShareReadyUrl = typeof item.ready_url === "string" ? item.ready_url : "" %>
|
|
1840
|
+
<% const runningShareExternalReadyUrls = Array.isArray(item.external_ready_urls) ? item.external_ready_urls : [] %>
|
|
1841
|
+
<button
|
|
1842
|
+
class='btn home-icon-btn open-app-share-modal'
|
|
1843
|
+
type='button'
|
|
1844
|
+
data-app-id="<%= item.uri %>"
|
|
1845
|
+
data-app-name="<%= item.name %>"
|
|
1846
|
+
data-app-ready-url="<%= runningShareReadyUrl %>"
|
|
1847
|
+
data-app-external-ready-urls="<%= JSON.stringify(runningShareExternalReadyUrls) %>"
|
|
1848
|
+
title="Open on another device"
|
|
1849
|
+
aria-label="Open <%= item.name %> on another device"
|
|
1850
|
+
>
|
|
1851
|
+
<i class="fa-solid fa-wifi" aria-hidden="true"></i>
|
|
1852
|
+
</button>
|
|
1550
1853
|
<% const autolaunchRunningScript = !!(item.autolaunch_starting && item.autolaunch_script && item.running_scripts && item.running_scripts.some((s) => { const scriptName = s && (s.name || s.script_path || s.path || ""); return scriptName === item.autolaunch_script || String(scriptName).endsWith(`/${item.autolaunch_script}`) })) %>
|
|
1551
1854
|
<% if (item.autolaunch_starting && !autolaunchRunningScript) { %>
|
|
1552
1855
|
<% const autolaunchStatusLabel = item.autolaunch_status_label || "Starting automatically" %>
|
|
@@ -3058,6 +3361,228 @@ const showHomeStarToast = () => {
|
|
|
3058
3361
|
`
|
|
3059
3362
|
})
|
|
3060
3363
|
}
|
|
3364
|
+
const escapeHomeShareHtml = (value = "") => {
|
|
3365
|
+
return String(value || "").replace(/[&<>"']/g, (char) => {
|
|
3366
|
+
return ({
|
|
3367
|
+
"&": "&",
|
|
3368
|
+
"<": "<",
|
|
3369
|
+
">": ">",
|
|
3370
|
+
'"': """,
|
|
3371
|
+
"'": "'"
|
|
3372
|
+
})[char] || char
|
|
3373
|
+
})
|
|
3374
|
+
}
|
|
3375
|
+
const copyHomeShareText = async (text) => {
|
|
3376
|
+
if (navigator.clipboard && typeof navigator.clipboard.writeText === "function") {
|
|
3377
|
+
await navigator.clipboard.writeText(text)
|
|
3378
|
+
return
|
|
3379
|
+
}
|
|
3380
|
+
const textarea = document.createElement("textarea")
|
|
3381
|
+
textarea.value = text
|
|
3382
|
+
textarea.setAttribute("readonly", "")
|
|
3383
|
+
textarea.style.position = "fixed"
|
|
3384
|
+
textarea.style.opacity = "0"
|
|
3385
|
+
document.body.appendChild(textarea)
|
|
3386
|
+
textarea.select()
|
|
3387
|
+
document.execCommand("copy")
|
|
3388
|
+
textarea.remove()
|
|
3389
|
+
}
|
|
3390
|
+
const parseHomeShareExternalUrls = (raw = "") => {
|
|
3391
|
+
try {
|
|
3392
|
+
const parsed = JSON.parse(raw || "[]")
|
|
3393
|
+
return Array.isArray(parsed) ? parsed.filter((item) => item && typeof item.url === "string" && item.url.trim()) : []
|
|
3394
|
+
} catch (_) {
|
|
3395
|
+
return []
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3398
|
+
const requestHomeAppShareStatus = async (appId = "") => {
|
|
3399
|
+
const normalizedAppId = String(appId || "").trim()
|
|
3400
|
+
if (!normalizedAppId) {
|
|
3401
|
+
return null
|
|
3402
|
+
}
|
|
3403
|
+
try {
|
|
3404
|
+
const response = await fetch(`/apps/status/${encodeURIComponent(normalizedAppId)}`, { cache: "no-store" })
|
|
3405
|
+
if (!response.ok) {
|
|
3406
|
+
return null
|
|
3407
|
+
}
|
|
3408
|
+
return await response.json()
|
|
3409
|
+
} catch (_) {
|
|
3410
|
+
return null
|
|
3411
|
+
}
|
|
3412
|
+
}
|
|
3413
|
+
const updateHomeShareButtonStatus = (button, status) => {
|
|
3414
|
+
if (!button || !status || typeof status !== "object") {
|
|
3415
|
+
return
|
|
3416
|
+
}
|
|
3417
|
+
if (typeof status.ready_url === "string") {
|
|
3418
|
+
button.setAttribute("data-app-ready-url", status.ready_url)
|
|
3419
|
+
}
|
|
3420
|
+
if (Array.isArray(status.external_ready_urls)) {
|
|
3421
|
+
button.setAttribute("data-app-external-ready-urls", JSON.stringify(status.external_ready_urls))
|
|
3422
|
+
}
|
|
3423
|
+
if (typeof status.title === "string" && status.title.trim()) {
|
|
3424
|
+
button.setAttribute("data-app-name", status.title.trim())
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3427
|
+
const requestHomeNetworkSharingStatus = async () => {
|
|
3428
|
+
try {
|
|
3429
|
+
const response = await fetch("/info/network-sharing", { cache: "no-store" })
|
|
3430
|
+
if (!response.ok) {
|
|
3431
|
+
return null
|
|
3432
|
+
}
|
|
3433
|
+
return await response.json()
|
|
3434
|
+
} catch (_) {
|
|
3435
|
+
return null
|
|
3436
|
+
}
|
|
3437
|
+
}
|
|
3438
|
+
const homeShareSetupUrl = () => {
|
|
3439
|
+
const currentPath = `${window.location.pathname}${window.location.search}`
|
|
3440
|
+
return `/setup/network?callback=${encodeURIComponent(currentPath || "/home")}`
|
|
3441
|
+
}
|
|
3442
|
+
const homeShareActionHtml = (href, label, primary = true) => {
|
|
3443
|
+
return `<a class="home-app-share-modal__action${primary ? " is-primary" : ""}" href="${escapeHomeShareHtml(href)}">${escapeHomeShareHtml(label)}</a>`
|
|
3444
|
+
}
|
|
3445
|
+
const homeShareTitleHtml = (label) => {
|
|
3446
|
+
return `<span class="home-app-share-modal__title-icon" aria-hidden="true"><i class="fa-solid fa-wifi"></i></span><span class="home-app-share-modal__title-copy"><span class="home-app-share-modal__title-text">${escapeHomeShareHtml(label)}</span><span class="home-app-share-modal__title-meta">Home Server</span></span>`
|
|
3447
|
+
}
|
|
3448
|
+
const renderHomeShareAvailableHtml = ({ appName, wifiUrl }) => {
|
|
3449
|
+
const safeUrl = escapeHomeShareHtml(wifiUrl)
|
|
3450
|
+
const qr = `/qr?data=${encodeURIComponent(wifiUrl)}&s=4&m=0`
|
|
3451
|
+
return `
|
|
3452
|
+
<div class="home-app-share-modal">
|
|
3453
|
+
<div class="home-app-share-modal__scan-card">
|
|
3454
|
+
<div class="home-app-share-modal__scan-copy">
|
|
3455
|
+
<div class="home-app-share-modal__eyebrow">Scan to open</div>
|
|
3456
|
+
<div class="home-app-share-modal__intro">Use a phone, tablet, or another computer on this network.</div>
|
|
3457
|
+
</div>
|
|
3458
|
+
<div class="home-app-share-modal__qr">
|
|
3459
|
+
<img src="${escapeHomeShareHtml(qr)}" alt="QR code for ${escapeHomeShareHtml(appName)} Home Server link">
|
|
3460
|
+
</div>
|
|
3461
|
+
</div>
|
|
3462
|
+
<div class="home-app-share-modal__share-card">
|
|
3463
|
+
<div class="home-app-share-modal__url">
|
|
3464
|
+
<i class="fa-solid fa-wifi" aria-hidden="true"></i>
|
|
3465
|
+
<span>${safeUrl}</span>
|
|
3466
|
+
</div>
|
|
3467
|
+
<div class="home-app-share-modal__actions">
|
|
3468
|
+
<button type="button" class="home-app-share-modal__action is-primary" data-home-share-copy="${safeUrl}">
|
|
3469
|
+
<i class="fa-regular fa-copy" aria-hidden="true"></i>
|
|
3470
|
+
<span>Copy link</span>
|
|
3471
|
+
</button>
|
|
3472
|
+
<a class="home-app-share-modal__action" href="${safeUrl}" target="_blank" rel="noreferrer">
|
|
3473
|
+
<i class="fa-solid fa-arrow-up-right-from-square" aria-hidden="true"></i>
|
|
3474
|
+
<span>Open</span>
|
|
3475
|
+
</a>
|
|
3476
|
+
</div>
|
|
3477
|
+
</div>
|
|
3478
|
+
<div class="home-app-share-modal__note"><i class="fa-solid fa-circle-info" aria-hidden="true"></i><span>Only devices on this local network can open this link.</span></div>
|
|
3479
|
+
</div>`
|
|
3480
|
+
}
|
|
3481
|
+
const renderHomeShareSetupHtml = ({ detail, actions }) => {
|
|
3482
|
+
return `
|
|
3483
|
+
<div class="home-app-share-modal">
|
|
3484
|
+
<div class="home-app-share-modal__setup-card">
|
|
3485
|
+
<div class="home-app-share-modal__intro">${escapeHomeShareHtml(detail)}</div>
|
|
3486
|
+
</div>
|
|
3487
|
+
<div class="home-app-share-modal__actions">${actions.join("")}</div>
|
|
3488
|
+
</div>`
|
|
3489
|
+
}
|
|
3490
|
+
const showHomeAppShareModal = async (button) => {
|
|
3491
|
+
if (!button) {
|
|
3492
|
+
return
|
|
3493
|
+
}
|
|
3494
|
+
const appId = button.getAttribute("data-app-id") || ""
|
|
3495
|
+
let appName = button.getAttribute("data-app-name") || "This app"
|
|
3496
|
+
let readyUrl = button.getAttribute("data-app-ready-url") || ""
|
|
3497
|
+
let externalUrls = parseHomeShareExternalUrls(button.getAttribute("data-app-external-ready-urls") || "")
|
|
3498
|
+
const status = await requestHomeNetworkSharingStatus()
|
|
3499
|
+
const appStatus = await requestHomeAppShareStatus(appId)
|
|
3500
|
+
if (appStatus) {
|
|
3501
|
+
updateHomeShareButtonStatus(button, appStatus)
|
|
3502
|
+
if (typeof appStatus.title === "string" && appStatus.title.trim()) {
|
|
3503
|
+
appName = appStatus.title.trim()
|
|
3504
|
+
}
|
|
3505
|
+
if (typeof appStatus.ready_url === "string") {
|
|
3506
|
+
readyUrl = appStatus.ready_url
|
|
3507
|
+
}
|
|
3508
|
+
if (Array.isArray(appStatus.external_ready_urls)) {
|
|
3509
|
+
externalUrls = appStatus.external_ready_urls.filter((item) => item && typeof item.url === "string" && item.url.trim())
|
|
3510
|
+
}
|
|
3511
|
+
}
|
|
3512
|
+
const wifiUrl = externalUrls.length > 0 ? externalUrls[0].url : ""
|
|
3513
|
+
let title = `Open ${appName} on another device`
|
|
3514
|
+
let html
|
|
3515
|
+
if (status && status.installed === false) {
|
|
3516
|
+
title = "Install Home Server support"
|
|
3517
|
+
html = renderHomeShareSetupHtml({
|
|
3518
|
+
detail: "Pinokio needs its local networking component before it can share app Web UIs on your home network.",
|
|
3519
|
+
actions: [homeShareActionHtml(homeShareSetupUrl(), "Install Home Server support")]
|
|
3520
|
+
})
|
|
3521
|
+
} else if (status && status.active === false) {
|
|
3522
|
+
title = "Turn on Home Server"
|
|
3523
|
+
html = renderHomeShareSetupHtml({
|
|
3524
|
+
detail: "Home Server support is installed but turned off. Turn it on to open running app Web UIs from phones, tablets, and other computers on this network.",
|
|
3525
|
+
actions: [homeShareActionHtml("/network", "Open Home Server settings")]
|
|
3526
|
+
})
|
|
3527
|
+
} else if (status && status.running === false) {
|
|
3528
|
+
title = "Home Server is starting"
|
|
3529
|
+
html = renderHomeShareSetupHtml({
|
|
3530
|
+
detail: "Pinokio is preparing home-network links for app Web UIs. Open Home Server settings to check it.",
|
|
3531
|
+
actions: [homeShareActionHtml("/network", "Open Home Server settings")]
|
|
3532
|
+
})
|
|
3533
|
+
} else if (wifiUrl) {
|
|
3534
|
+
html = renderHomeShareAvailableHtml({ appName, wifiUrl })
|
|
3535
|
+
} else if (readyUrl) {
|
|
3536
|
+
title = "Home Server link is not ready yet"
|
|
3537
|
+
html = renderHomeShareSetupHtml({
|
|
3538
|
+
detail: `${appName} is running locally, but its Home Server link is not ready yet.`,
|
|
3539
|
+
actions: [
|
|
3540
|
+
`<button type="button" class="home-app-share-modal__action is-primary" data-home-share-retry>Retry</button>`,
|
|
3541
|
+
homeShareActionHtml("/network", "Open Home Server settings", false)
|
|
3542
|
+
]
|
|
3543
|
+
})
|
|
3544
|
+
} else {
|
|
3545
|
+
title = "No Web UI address yet"
|
|
3546
|
+
html = renderHomeShareSetupHtml({
|
|
3547
|
+
detail: `${appName} is running, but Pinokio has not found a Web UI URL to open from another device yet.`,
|
|
3548
|
+
actions: [`<button type="button" class="home-app-share-modal__action is-primary" data-home-share-retry>Retry</button>`]
|
|
3549
|
+
})
|
|
3550
|
+
}
|
|
3551
|
+
await Swal.fire({
|
|
3552
|
+
title: homeShareTitleHtml(title),
|
|
3553
|
+
html,
|
|
3554
|
+
showConfirmButton: false,
|
|
3555
|
+
showCloseButton: true,
|
|
3556
|
+
customClass: {
|
|
3557
|
+
popup: "home-app-share-swal"
|
|
3558
|
+
},
|
|
3559
|
+
didOpen: (popup) => {
|
|
3560
|
+
const copyButton = popup.querySelector("[data-home-share-copy]")
|
|
3561
|
+
if (copyButton) {
|
|
3562
|
+
copyButton.addEventListener("click", async () => {
|
|
3563
|
+
const url = copyButton.getAttribute("data-home-share-copy") || ""
|
|
3564
|
+
if (!url) {
|
|
3565
|
+
return
|
|
3566
|
+
}
|
|
3567
|
+
try {
|
|
3568
|
+
await copyHomeShareText(url)
|
|
3569
|
+
copyButton.innerHTML = '<i class="fa-solid fa-check" aria-hidden="true"></i><span>Copied</span>'
|
|
3570
|
+
window.setTimeout(() => {
|
|
3571
|
+
copyButton.innerHTML = '<i class="fa-regular fa-copy" aria-hidden="true"></i><span>Copy link</span>'
|
|
3572
|
+
}, 1200)
|
|
3573
|
+
} catch (_) {}
|
|
3574
|
+
})
|
|
3575
|
+
}
|
|
3576
|
+
const retryButton = popup.querySelector("[data-home-share-retry]")
|
|
3577
|
+
if (retryButton) {
|
|
3578
|
+
retryButton.addEventListener("click", () => {
|
|
3579
|
+
Swal.close()
|
|
3580
|
+
window.setTimeout(() => showHomeAppShareModal(button), 80)
|
|
3581
|
+
})
|
|
3582
|
+
}
|
|
3583
|
+
}
|
|
3584
|
+
})
|
|
3585
|
+
}
|
|
3061
3586
|
document.addEventListener("click", async (e) => {
|
|
3062
3587
|
let target
|
|
3063
3588
|
if (e.target.classList.contains("open-actions-modal")) {
|
|
@@ -3071,6 +3596,18 @@ document.addEventListener("click", async (e) => {
|
|
|
3071
3596
|
openHomeActionsDialog(target)
|
|
3072
3597
|
return
|
|
3073
3598
|
}
|
|
3599
|
+
if (e.target.classList.contains("open-app-share-modal") || e.target.classList.contains("home-share-app")) {
|
|
3600
|
+
target = e.target
|
|
3601
|
+
} else {
|
|
3602
|
+
target = e.target.closest(".open-app-share-modal, .home-share-app")
|
|
3603
|
+
}
|
|
3604
|
+
if (target) {
|
|
3605
|
+
e.preventDefault()
|
|
3606
|
+
e.stopPropagation()
|
|
3607
|
+
closeContextMenuForNode(target)
|
|
3608
|
+
await showHomeAppShareModal(target)
|
|
3609
|
+
return
|
|
3610
|
+
}
|
|
3074
3611
|
if (e.target.classList.contains("home-actions-close")) {
|
|
3075
3612
|
target = e.target
|
|
3076
3613
|
} else {
|
|
@@ -1537,6 +1537,7 @@ body.dark .ace-editor {
|
|
|
1537
1537
|
<button class='btn2' id='screenshot' data-tippy-content="screen capture"><i class="fa-solid fa-camera"></i></button>
|
|
1538
1538
|
<button class='btn2' id='inspector' data-tippy-content="X-ray mode"><i class="fa-solid fa-eye"></i></button>
|
|
1539
1539
|
<div class='flexible'></div>
|
|
1540
|
+
<%- include('../partials/home_server_popover') %>
|
|
1540
1541
|
<a class='btn2' href="/columns" data-tippy-content="split into 2 columns">
|
|
1541
1542
|
<div><i class="fa-solid fa-table-columns"></i></div>
|
|
1542
1543
|
</a>
|
|
@@ -1551,6 +1552,7 @@ body.dark .ace-editor {
|
|
|
1551
1552
|
</button>
|
|
1552
1553
|
</h1>
|
|
1553
1554
|
</header>
|
|
1555
|
+
<%- include('../partials/home_server_popover_assets') %>
|
|
1554
1556
|
<main>
|
|
1555
1557
|
<div class="container">
|
|
1556
1558
|
<div class="tab-container">
|
package/server/views/keys.ejs
CHANGED
|
@@ -264,6 +264,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
264
264
|
<h1>
|
|
265
265
|
<a class='home-button home' href="/home"><i class='fa-solid fa-home'></i></a>
|
|
266
266
|
<div class='flexible'></div>
|
|
267
|
+
<%- include('partials/home_server_popover') %>
|
|
267
268
|
<div class='nav-btns'>
|
|
268
269
|
<button class='btn' id='open-fs' data-filepath="<%=filepath%>" data-command="view"><i class="fa-solid fa-eye"></i> Open in File Explorer</button>
|
|
269
270
|
<div id='save' class='btn'>
|
|
@@ -272,6 +273,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
272
273
|
</div>
|
|
273
274
|
</h1>
|
|
274
275
|
</header>
|
|
276
|
+
<%- include('partials/home_server_popover_assets') %>
|
|
275
277
|
<% if (items.length > 0) { %>
|
|
276
278
|
<div id='editor'>
|
|
277
279
|
<% items.forEach((item) => { %>
|
package/server/views/mini.ejs
CHANGED
|
@@ -770,6 +770,7 @@ body.dark .appcanvas {
|
|
|
770
770
|
<button class='btn2' id='screenshot' data-tippy-content="screen capture"><i class="fa-solid fa-camera"></i></button>
|
|
771
771
|
<button class='btn2' id='inspector' data-tippy-content="X-ray mode"><i class="fa-solid fa-eye"></i></button>
|
|
772
772
|
<div class='flexible filler'></div>
|
|
773
|
+
<%- include('partials/home_server_popover') %>
|
|
773
774
|
<a class='btn2' href="/columns" data-tippy-content="split into 2 columns">
|
|
774
775
|
<div><i class="fa-solid fa-table-columns"></i></div>
|
|
775
776
|
</a>
|
|
@@ -784,6 +785,7 @@ body.dark .appcanvas {
|
|
|
784
785
|
</button>
|
|
785
786
|
</h1>
|
|
786
787
|
</header>
|
|
788
|
+
<%- include('partials/home_server_popover_assets') %>
|
|
787
789
|
<div class='appcanvas'>
|
|
788
790
|
<aside class='active'>
|
|
789
791
|
<div class='m n system' data-type="n">
|
package/server/views/net.ejs
CHANGED
|
@@ -1310,6 +1310,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
1310
1310
|
<div class='url-dropdown' id='url-dropdown'></div>
|
|
1311
1311
|
</div>
|
|
1312
1312
|
</form>
|
|
1313
|
+
<%- include('partials/home_server_popover') %>
|
|
1313
1314
|
<a class='btn2' href="/columns" data-tippy-content="split into 2 columns">
|
|
1314
1315
|
<div><i class="fa-solid fa-table-columns"></i></div>
|
|
1315
1316
|
</a>
|
|
@@ -1324,6 +1325,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
1324
1325
|
</button>
|
|
1325
1326
|
</h1>
|
|
1326
1327
|
</header>
|
|
1328
|
+
<%- include('partials/home_server_popover_assets') %>
|
|
1327
1329
|
<main>
|
|
1328
1330
|
<div class='task-container'>
|
|
1329
1331
|
<section class='task-shell net-shell'>
|