pinokiod 3.86.0 → 3.87.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.
- package/Dockerfile +61 -0
- package/docker-entrypoint.sh +75 -0
- package/kernel/api/hf/index.js +1 -1
- package/kernel/api/index.js +1 -1
- package/kernel/api/shell/index.js +6 -0
- package/kernel/api/terminal/index.js +166 -0
- package/kernel/bin/conda.js +3 -2
- package/kernel/bin/index.js +53 -2
- package/kernel/bin/setup.js +32 -0
- package/kernel/bin/vs.js +11 -2
- package/kernel/index.js +42 -2
- package/kernel/info.js +36 -0
- package/kernel/peer.js +42 -15
- package/kernel/router/index.js +23 -15
- package/kernel/router/localhost_static_router.js +0 -3
- package/kernel/router/pinokio_domain_router.js +333 -0
- package/kernel/shells.js +21 -1
- package/kernel/util.js +2 -2
- package/package.json +2 -1
- package/script/install-mode.js +33 -0
- package/script/pinokio.json +7 -0
- package/server/index.js +513 -173
- package/server/public/Socket.js +48 -0
- package/server/public/common.js +1441 -276
- package/server/public/fseditor.js +71 -12
- package/server/public/install.js +1 -1
- package/server/public/layout.js +740 -0
- package/server/public/modalinput.js +0 -1
- package/server/public/style.css +97 -105
- package/server/public/tab-idle-notifier.js +629 -0
- package/server/public/terminal_input_tracker.js +63 -0
- package/server/public/urldropdown.css +319 -53
- package/server/public/urldropdown.js +615 -159
- package/server/public/window_storage.js +97 -28
- package/server/socket.js +40 -9
- package/server/views/500.ejs +2 -2
- package/server/views/app.ejs +3136 -1367
- package/server/views/bookmarklet.ejs +1 -1
- package/server/views/bootstrap.ejs +1 -1
- package/server/views/columns.ejs +2 -13
- package/server/views/connect.ejs +3 -4
- package/server/views/container.ejs +1 -2
- package/server/views/d.ejs +223 -53
- package/server/views/editor.ejs +1 -1
- package/server/views/file_explorer.ejs +1 -1
- package/server/views/index.ejs +12 -11
- package/server/views/index2.ejs +4 -4
- package/server/views/init/index.ejs +4 -5
- package/server/views/install.ejs +1 -1
- package/server/views/layout.ejs +105 -0
- package/server/views/net.ejs +39 -7
- package/server/views/network.ejs +20 -6
- package/server/views/network2.ejs +1 -1
- package/server/views/old_network.ejs +2 -2
- package/server/views/partials/dynamic.ejs +3 -5
- package/server/views/partials/menu.ejs +3 -5
- package/server/views/partials/running.ejs +1 -1
- package/server/views/pro.ejs +1 -1
- package/server/views/prototype/index.ejs +1 -1
- package/server/views/review.ejs +11 -23
- package/server/views/rows.ejs +2 -13
- package/server/views/screenshots.ejs +293 -138
- package/server/views/settings.ejs +3 -4
- package/server/views/setup.ejs +1 -2
- package/server/views/shell.ejs +277 -26
- package/server/views/terminal.ejs +322 -49
- package/server/views/tools.ejs +448 -4
package/server/views/tools.ejs
CHANGED
|
@@ -679,6 +679,261 @@ body.dark .keys pre {
|
|
|
679
679
|
.swal2-title {
|
|
680
680
|
text-align: center !important;
|
|
681
681
|
}
|
|
682
|
+
|
|
683
|
+
.package-install-card {
|
|
684
|
+
background: rgba(0, 0, 0, 0.035);
|
|
685
|
+
border: 1px solid rgba(0, 0, 0, 0.06);
|
|
686
|
+
border-radius: 10px;
|
|
687
|
+
padding: 15px;
|
|
688
|
+
margin-bottom: 20px;
|
|
689
|
+
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
|
|
690
|
+
}
|
|
691
|
+
body.dark .package-install-card {
|
|
692
|
+
background: rgba(255, 255, 255, 0.05);
|
|
693
|
+
border-color: rgba(255, 255, 255, 0.08);
|
|
694
|
+
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
|
|
695
|
+
}
|
|
696
|
+
.package-install-form {
|
|
697
|
+
display: flex;
|
|
698
|
+
flex-wrap: wrap;
|
|
699
|
+
gap: 15px;
|
|
700
|
+
align-items: flex-end;
|
|
701
|
+
}
|
|
702
|
+
.package-install-field {
|
|
703
|
+
flex: 1 1 220px;
|
|
704
|
+
display: flex;
|
|
705
|
+
flex-direction: column;
|
|
706
|
+
gap: 6px;
|
|
707
|
+
}
|
|
708
|
+
.package-install-label {
|
|
709
|
+
font-size: 12px;
|
|
710
|
+
font-weight: 600;
|
|
711
|
+
text-transform: uppercase;
|
|
712
|
+
letter-spacing: 0.06em;
|
|
713
|
+
color: rgba(0, 0, 0, 0.55);
|
|
714
|
+
}
|
|
715
|
+
body.dark .package-install-label {
|
|
716
|
+
color: rgba(255, 255, 255, 0.65);
|
|
717
|
+
}
|
|
718
|
+
.package-install-input {
|
|
719
|
+
border-radius: 8px;
|
|
720
|
+
border: 1px solid rgba(0, 0, 0, 0.14);
|
|
721
|
+
padding: 10px 12px;
|
|
722
|
+
font-size: 14px;
|
|
723
|
+
background: rgba(255, 255, 255, 0.95);
|
|
724
|
+
color: rgba(15, 23, 42, 0.95);
|
|
725
|
+
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
|
726
|
+
}
|
|
727
|
+
body.dark .package-install-input {
|
|
728
|
+
background: rgba(15, 23, 42, 0.85);
|
|
729
|
+
border-color: rgba(148, 163, 184, 0.3);
|
|
730
|
+
color: rgba(226, 232, 240, 0.92);
|
|
731
|
+
}
|
|
732
|
+
.package-install-input:focus {
|
|
733
|
+
outline: none;
|
|
734
|
+
border-color: rgba(59, 130, 246, 0.6);
|
|
735
|
+
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
|
|
736
|
+
}
|
|
737
|
+
body.dark .package-install-input:focus {
|
|
738
|
+
border-color: rgba(96, 165, 250, 0.7);
|
|
739
|
+
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.25);
|
|
740
|
+
}
|
|
741
|
+
.package-install-submit {
|
|
742
|
+
flex: 0 0 auto;
|
|
743
|
+
display: inline-flex;
|
|
744
|
+
align-items: center;
|
|
745
|
+
gap: 8px;
|
|
746
|
+
padding: 11px 18px;
|
|
747
|
+
font-size: 14px;
|
|
748
|
+
font-weight: 600;
|
|
749
|
+
border-radius: 8px;
|
|
750
|
+
border: none;
|
|
751
|
+
cursor: pointer;
|
|
752
|
+
background: rgba(59, 130, 246, 0.9);
|
|
753
|
+
color: #ffffff;
|
|
754
|
+
transition: transform 0.15s ease, box-shadow 0.2s ease, background 0.2s ease;
|
|
755
|
+
}
|
|
756
|
+
.package-install-submit:hover {
|
|
757
|
+
background: rgba(37, 99, 235, 0.95);
|
|
758
|
+
box-shadow: 0 8px 18px rgba(37, 99, 235, 0.25);
|
|
759
|
+
transform: translateY(-1px);
|
|
760
|
+
}
|
|
761
|
+
.package-install-submit:active {
|
|
762
|
+
transform: translateY(0);
|
|
763
|
+
box-shadow: none;
|
|
764
|
+
}
|
|
765
|
+
.package-install-submit i {
|
|
766
|
+
font-size: 16px;
|
|
767
|
+
}
|
|
768
|
+
.package-install-description {
|
|
769
|
+
margin-top: 12px;
|
|
770
|
+
font-size: 12px;
|
|
771
|
+
color: rgba(0, 0, 0, 0.55);
|
|
772
|
+
}
|
|
773
|
+
body.dark .package-install-description {
|
|
774
|
+
color: rgba(255, 255, 255, 0.55);
|
|
775
|
+
}
|
|
776
|
+
.package-install-description code {
|
|
777
|
+
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
|
|
778
|
+
background: rgba(15, 23, 42, 0.06);
|
|
779
|
+
padding: 2px 6px;
|
|
780
|
+
border-radius: 6px;
|
|
781
|
+
color: inherit;
|
|
782
|
+
}
|
|
783
|
+
body.dark .package-install-description code {
|
|
784
|
+
background: rgba(255, 255, 255, 0.08);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
@media only screen and (max-width: 720px) {
|
|
788
|
+
.package-install-form {
|
|
789
|
+
flex-direction: column;
|
|
790
|
+
align-items: stretch;
|
|
791
|
+
}
|
|
792
|
+
.package-install-field {
|
|
793
|
+
width: 100%;
|
|
794
|
+
}
|
|
795
|
+
.package-install-submit {
|
|
796
|
+
width: 100%;
|
|
797
|
+
justify-content: center;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
:root {
|
|
802
|
+
--pinokio-modal-bg: #ffffff;
|
|
803
|
+
--pinokio-modal-text: #0f172a;
|
|
804
|
+
--pinokio-modal-title-color: #0f172a;
|
|
805
|
+
--pinokio-modal-subtitle-color: rgba(71, 85, 105, 0.75);
|
|
806
|
+
--pinokio-modal-body-bg: rgba(241, 245, 249, 0.9);
|
|
807
|
+
--pinokio-modal-body-border: rgba(203, 213, 225, 0.7);
|
|
808
|
+
--pinokio-modal-body-iframe-bg: #ffffff;
|
|
809
|
+
--pinokio-modal-label-color: rgba(71, 85, 105, 0.95);
|
|
810
|
+
--pinokio-modal-input-bg: #ffffff;
|
|
811
|
+
--pinokio-modal-input-border: rgba(148, 163, 184, 0.5);
|
|
812
|
+
--pinokio-modal-input-text: #0f172a;
|
|
813
|
+
--pinokio-modal-input-focus-border: rgba(59, 130, 246, 0.6);
|
|
814
|
+
--pinokio-modal-input-focus-shadow: rgba(59, 130, 246, 0.2);
|
|
815
|
+
--pinokio-modern-shadow: 0 32px 80px rgba(15, 23, 42, 0.25);
|
|
816
|
+
--pinokio-modern-close-color: rgba(71, 85, 105, 0.65);
|
|
817
|
+
--pinokio-modern-close-hover-bg: rgba(148, 163, 184, 0.18);
|
|
818
|
+
--pinokio-modern-close-hover-color: #0f172a;
|
|
819
|
+
}
|
|
820
|
+
body.dark {
|
|
821
|
+
--pinokio-modal-bg: #0f172a;
|
|
822
|
+
--pinokio-modal-text: #e2e8f0;
|
|
823
|
+
--pinokio-modal-title-color: #f8fafc;
|
|
824
|
+
--pinokio-modal-subtitle-color: rgba(148, 163, 184, 0.85);
|
|
825
|
+
--pinokio-modal-body-bg: rgba(15, 23, 42, 0.6);
|
|
826
|
+
--pinokio-modal-body-border: rgba(59, 130, 246, 0.18);
|
|
827
|
+
--pinokio-modal-body-iframe-bg: #0b1220;
|
|
828
|
+
--pinokio-modal-label-color: rgba(203, 213, 225, 0.9);
|
|
829
|
+
--pinokio-modal-input-bg: rgba(15, 23, 42, 0.85);
|
|
830
|
+
--pinokio-modal-input-border: rgba(148, 163, 184, 0.25);
|
|
831
|
+
--pinokio-modal-input-text: #f8fafc;
|
|
832
|
+
--pinokio-modal-input-focus-border: rgba(96, 165, 250, 0.7);
|
|
833
|
+
--pinokio-modal-input-focus-shadow: rgba(37, 99, 235, 0.25);
|
|
834
|
+
--pinokio-modern-shadow: 0 40px 120px rgba(2, 8, 23, 0.7);
|
|
835
|
+
--pinokio-modern-close-color: rgba(226, 232, 240, 0.6);
|
|
836
|
+
--pinokio-modern-close-hover-bg: rgba(148, 163, 184, 0.12);
|
|
837
|
+
--pinokio-modern-close-hover-color: #f8fafc;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
.pinokio-modern-modal.swal2-popup {
|
|
841
|
+
background: var(--pinokio-modal-bg) !important;
|
|
842
|
+
color: var(--pinokio-modal-text) !important;
|
|
843
|
+
border-radius: 18px;
|
|
844
|
+
padding: 0 !important;
|
|
845
|
+
box-shadow: var(--pinokio-modern-shadow) !important;
|
|
846
|
+
overflow: hidden;
|
|
847
|
+
}
|
|
848
|
+
.pinokio-modern-html {
|
|
849
|
+
padding: 0 !important;
|
|
850
|
+
}
|
|
851
|
+
.pinokio-modern-close {
|
|
852
|
+
color: var(--pinokio-modern-close-color) !important;
|
|
853
|
+
border-radius: 50%;
|
|
854
|
+
width: 34px;
|
|
855
|
+
height: 34px;
|
|
856
|
+
display: flex !important;
|
|
857
|
+
align-items: center;
|
|
858
|
+
justify-content: center;
|
|
859
|
+
transition: background 0.2s ease, color 0.2s ease;
|
|
860
|
+
}
|
|
861
|
+
.pinokio-modern-close:hover {
|
|
862
|
+
background: var(--pinokio-modern-close-hover-bg) !important;
|
|
863
|
+
color: var(--pinokio-modern-close-hover-color) !important;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
.pinokio-modal-surface {
|
|
867
|
+
display: flex;
|
|
868
|
+
flex-direction: column;
|
|
869
|
+
gap: 0;
|
|
870
|
+
color: var(--pinokio-modal-text);
|
|
871
|
+
}
|
|
872
|
+
.pinokio-modal-header {
|
|
873
|
+
display: flex;
|
|
874
|
+
align-items: center;
|
|
875
|
+
gap: 16px;
|
|
876
|
+
padding: 22px;
|
|
877
|
+
}
|
|
878
|
+
.pinokio-modal-icon {
|
|
879
|
+
width: 48px;
|
|
880
|
+
height: 48px;
|
|
881
|
+
border-radius: 16px;
|
|
882
|
+
display: flex;
|
|
883
|
+
align-items: center;
|
|
884
|
+
justify-content: center;
|
|
885
|
+
font-size: 22px;
|
|
886
|
+
background: rgba(59, 130, 246, 0.12);
|
|
887
|
+
color: rgba(37, 99, 235, 0.95);
|
|
888
|
+
}
|
|
889
|
+
body.dark .pinokio-modal-icon {
|
|
890
|
+
background: rgba(59, 130, 246, 0.18);
|
|
891
|
+
color: rgba(147, 197, 253, 0.95);
|
|
892
|
+
}
|
|
893
|
+
.pinokio-modal-heading {
|
|
894
|
+
text-align: left;
|
|
895
|
+
flex: 1;
|
|
896
|
+
display: flex;
|
|
897
|
+
flex-direction: column;
|
|
898
|
+
gap: 6px;
|
|
899
|
+
}
|
|
900
|
+
.pinokio-modal-title {
|
|
901
|
+
font-size: 20px;
|
|
902
|
+
font-weight: 700;
|
|
903
|
+
color: var(--pinokio-modal-title-color);
|
|
904
|
+
}
|
|
905
|
+
.pinokio-modal-subtitle {
|
|
906
|
+
font-size: 13px;
|
|
907
|
+
color: var(--pinokio-modal-subtitle-color);
|
|
908
|
+
line-height: 1.4;
|
|
909
|
+
}
|
|
910
|
+
.pinokio-modal-subtitle code {
|
|
911
|
+
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
|
|
912
|
+
font-size: 12px;
|
|
913
|
+
padding: 2px 6px;
|
|
914
|
+
border-radius: 6px;
|
|
915
|
+
background: rgba(15, 23, 42, 0.08);
|
|
916
|
+
color: inherit;
|
|
917
|
+
}
|
|
918
|
+
body.dark .pinokio-modal-subtitle code {
|
|
919
|
+
background: rgba(15, 23, 42, 0.4);
|
|
920
|
+
}
|
|
921
|
+
.pinokio-modal-body {
|
|
922
|
+
background: var(--pinokio-modal-body-bg);
|
|
923
|
+
border-top: 1px solid var(--pinokio-modal-body-border);
|
|
924
|
+
padding: 0;
|
|
925
|
+
}
|
|
926
|
+
.pinokio-modal-body--iframe {
|
|
927
|
+
height: 520px;
|
|
928
|
+
background: var(--pinokio-modal-body-iframe-bg);
|
|
929
|
+
}
|
|
930
|
+
.pinokio-modal-body--iframe iframe {
|
|
931
|
+
width: 100%;
|
|
932
|
+
height: 100%;
|
|
933
|
+
border: none;
|
|
934
|
+
background: transparent;
|
|
935
|
+
}
|
|
936
|
+
|
|
682
937
|
main {
|
|
683
938
|
display: flex;
|
|
684
939
|
}
|
|
@@ -845,7 +1100,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
845
1100
|
<header class='navheader grabbable'>
|
|
846
1101
|
<h1>
|
|
847
1102
|
<a class='home' href="/"><img class='icon' src="/pinokio-black.png"></a>
|
|
848
|
-
<button id='collapse' class='btn2' data-tippy-content="toggle fullscreen view"><i class="fa-solid fa-bars"></i></button>
|
|
849
1103
|
<button class='btn2' id='back' data-tippy-content="back"><div><i class="fa-solid fa-chevron-left"></i></div></button>
|
|
850
1104
|
<button class='btn2' id='forward' data-tippy-content="forward"><div><i class="fa-solid fa-chevron-right"></i></div></button>
|
|
851
1105
|
<button class='btn2' id='refresh-page' data-tippy-content="refresh"><div><i class="fa-solid fa-rotate-right"></i></div></button>
|
|
@@ -853,7 +1107,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
853
1107
|
<button class='btn2 mobile-link-button' id='mobile-link-button' data-tippy-content="enter url"><i class="fa-solid fa-link"></i></button>
|
|
854
1108
|
<form class='urlbar'>
|
|
855
1109
|
<div class='url-input-container'>
|
|
856
|
-
<input type='url' placeholder='enter
|
|
1110
|
+
<input type='url' placeholder='enter any local url to open in pinokio'>
|
|
857
1111
|
<div class='url-dropdown' id='url-dropdown'></div>
|
|
858
1112
|
</div>
|
|
859
1113
|
</form>
|
|
@@ -961,6 +1215,38 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
961
1215
|
<div><%= install.modules.length %> modules installed</div>
|
|
962
1216
|
</div>
|
|
963
1217
|
</div>
|
|
1218
|
+
<div class="package-install-card">
|
|
1219
|
+
<form class="package-install-form" data-manager="<%= install.package_manager %>">
|
|
1220
|
+
<% if (install.package_manager === 'conda') { %>
|
|
1221
|
+
<div class="package-install-field">
|
|
1222
|
+
<label class="package-install-label" for="install-channel-<%= install.package_manager %>">Channel</label>
|
|
1223
|
+
<input id="install-channel-<%= install.package_manager %>" class="package-install-input" name="channel" type="text" value="conda-forge" placeholder="Enter channel" autocomplete="off">
|
|
1224
|
+
</div>
|
|
1225
|
+
<div class="package-install-field">
|
|
1226
|
+
<label class="package-install-label" for="install-name-<%= install.package_manager %>">Package name</label>
|
|
1227
|
+
<input id="install-name-<%= install.package_manager %>" class="package-install-input" name="name" type="text" placeholder="Enter package name" autocomplete="off" required>
|
|
1228
|
+
</div>
|
|
1229
|
+
<% } else { %>
|
|
1230
|
+
<div class="package-install-field">
|
|
1231
|
+
<label class="package-install-label" for="install-name-<%= install.package_manager %>">Package name</label>
|
|
1232
|
+
<input id="install-name-<%= install.package_manager %>" class="package-install-input" name="name" type="text" placeholder="Enter package name" autocomplete="off" required>
|
|
1233
|
+
</div>
|
|
1234
|
+
<% } %>
|
|
1235
|
+
<button type="submit" class="package-install-submit">
|
|
1236
|
+
<i class="fa-solid fa-terminal"></i>
|
|
1237
|
+
<span>Install</span>
|
|
1238
|
+
</button>
|
|
1239
|
+
</form>
|
|
1240
|
+
<div class="package-install-description">
|
|
1241
|
+
<% if (install.package_manager === 'conda') { %>
|
|
1242
|
+
Runs <code>conda install -y --channel <channel> <name></code>
|
|
1243
|
+
<% } else if (install.package_manager === 'brew') { %>
|
|
1244
|
+
Runs <code>brew install <name></code>
|
|
1245
|
+
<% } else { %>
|
|
1246
|
+
Runs <code><%= install.package_manager %> install <name></code>
|
|
1247
|
+
<% } %>
|
|
1248
|
+
</div>
|
|
1249
|
+
</div>
|
|
964
1250
|
<div class='tab-content'>
|
|
965
1251
|
<% install.modules.forEach((module) => { %>
|
|
966
1252
|
<div class="tab">
|
|
@@ -979,7 +1265,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
979
1265
|
<aside>
|
|
980
1266
|
<div class='btn-tab'>
|
|
981
1267
|
<button type='button' class='btn' id='create-launcher-button'><i class="fa-solid fa-plus"></i><div class='caption'>Create</div></button>
|
|
982
|
-
<a class='btn' id='explore' href="
|
|
1268
|
+
<a class='btn' id='explore' href="/home?mode=explore"><i class="fa-solid fa-globe"></i><div class='caption'>Discover</div></a>
|
|
983
1269
|
</div>
|
|
984
1270
|
<a href="/" class='tab'><i class='fas fa-laptop-code'></i><div class='caption'>This machine</div></a>
|
|
985
1271
|
<a href="/network" class='tab'><i class="fa-solid fa-wifi"></i><div class='caption'>Local network</div></a>
|
|
@@ -995,10 +1281,166 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
995
1281
|
<a id='downloadlogs' download class='hidden btn2' href="/pinokio/logs.zip"><i class="fa-solid fa-download"></i><div class='caption'>Download logs</div></a>
|
|
996
1282
|
<a class='tab' href="/screenshots"><i class="fa-solid fa-camera"></i><div class='caption'>Screenshots</div></a>
|
|
997
1283
|
<a class='tab selected' href="/tools"><i class="fa-solid fa-toolbox"></i><div class='caption'>Installed Tools</div></a>
|
|
998
|
-
<a class='tab' href="
|
|
1284
|
+
<a class='tab' href="/home?mode=settings"><i class="fa-solid fa-gear"></i><div class='caption'>Settings</div></a>
|
|
999
1285
|
</aside>
|
|
1000
1286
|
</main>
|
|
1001
1287
|
<script>
|
|
1288
|
+
const PACKAGE_MANAGER_ICONS = {
|
|
1289
|
+
conda: 'fa-solid fa-flask',
|
|
1290
|
+
brew: 'fa-solid fa-beer-mug-empty',
|
|
1291
|
+
pip: 'fa-brands fa-python',
|
|
1292
|
+
npm: 'fa-brands fa-npm',
|
|
1293
|
+
yarn: 'fa-brands fa-yarn',
|
|
1294
|
+
cargo: 'fa-solid fa-box-open'
|
|
1295
|
+
};
|
|
1296
|
+
|
|
1297
|
+
const escapeHtml = (value) => {
|
|
1298
|
+
if (!value) return '';
|
|
1299
|
+
return value
|
|
1300
|
+
.replace(/&/g, '&')
|
|
1301
|
+
.replace(/</g, '<')
|
|
1302
|
+
.replace(/>/g, '>')
|
|
1303
|
+
.replace(/"/g, '"')
|
|
1304
|
+
.replace(/'/g, ''');
|
|
1305
|
+
};
|
|
1306
|
+
|
|
1307
|
+
const toDisplayName = (manager) => {
|
|
1308
|
+
if (!manager) return 'Package Manager';
|
|
1309
|
+
return manager
|
|
1310
|
+
.split(/[\s_-]+/)
|
|
1311
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
1312
|
+
.join(' ');
|
|
1313
|
+
};
|
|
1314
|
+
|
|
1315
|
+
const buildInstallPayload = (manager, formData) => {
|
|
1316
|
+
const trimmedManager = (manager || '').trim();
|
|
1317
|
+
const normalizedManager = trimmedManager.toLowerCase();
|
|
1318
|
+
const name = (formData.get('name') || '').trim();
|
|
1319
|
+
if (!normalizedManager || !name) {
|
|
1320
|
+
return null;
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
if (normalizedManager === 'conda') {
|
|
1324
|
+
let channel = (formData.get('channel') || '').trim();
|
|
1325
|
+
if (!channel) {
|
|
1326
|
+
channel = 'conda-forge';
|
|
1327
|
+
}
|
|
1328
|
+
return {
|
|
1329
|
+
manager: normalizedManager,
|
|
1330
|
+
command: `conda install -y --channel ${channel} ${name}`,
|
|
1331
|
+
name,
|
|
1332
|
+
channel
|
|
1333
|
+
};
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
if (normalizedManager === 'brew') {
|
|
1337
|
+
return {
|
|
1338
|
+
manager: normalizedManager,
|
|
1339
|
+
command: `brew install ${name}`,
|
|
1340
|
+
name
|
|
1341
|
+
};
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
return {
|
|
1345
|
+
manager: normalizedManager,
|
|
1346
|
+
command: `${normalizedManager} install ${name}`,
|
|
1347
|
+
name
|
|
1348
|
+
};
|
|
1349
|
+
};
|
|
1350
|
+
|
|
1351
|
+
const openPackageInstallModal = ({ manager, command, name, channel }) => {
|
|
1352
|
+
if (typeof Swal === 'undefined') {
|
|
1353
|
+
console.error('SweetAlert2 is not available.');
|
|
1354
|
+
return;
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
const icon = PACKAGE_MANAGER_ICONS[manager] || 'fa-solid fa-box-open';
|
|
1358
|
+
const displayName = toDisplayName(manager);
|
|
1359
|
+
const safeName = escapeHtml(name);
|
|
1360
|
+
const safeChannel = channel ? escapeHtml(channel) : null;
|
|
1361
|
+
const commandHtml = escapeHtml(command);
|
|
1362
|
+
const detailParts = [];
|
|
1363
|
+
if (safeChannel) {
|
|
1364
|
+
detailParts.push(`Channel: ${safeChannel}`);
|
|
1365
|
+
}
|
|
1366
|
+
if (safeName) {
|
|
1367
|
+
detailParts.push(`Package: ${safeName}`);
|
|
1368
|
+
}
|
|
1369
|
+
const detailsHtml = detailParts.join(' · ');
|
|
1370
|
+
|
|
1371
|
+
const sanitizedManager = (manager || 'pkg').replace(/[^a-z0-9_-]/gi, '-').toLowerCase();
|
|
1372
|
+
const shellId = encodeURIComponent(`pkg-install-${sanitizedManager}-${Date.now()}`);
|
|
1373
|
+
const params = new URLSearchParams();
|
|
1374
|
+
params.set('path', '~/');
|
|
1375
|
+
params.set('message', command);
|
|
1376
|
+
params.set('target', '_top');
|
|
1377
|
+
const shellUrl = `/shell/${shellId}?${params.toString()}`;
|
|
1378
|
+
|
|
1379
|
+
const modalHtml = `
|
|
1380
|
+
<div class="pinokio-modal-surface">
|
|
1381
|
+
<div class="pinokio-modal-header">
|
|
1382
|
+
<div class="pinokio-modal-icon"><i class="${icon}"></i></div>
|
|
1383
|
+
<div class="pinokio-modal-heading">
|
|
1384
|
+
<div class="pinokio-modal-title">${escapeHtml(displayName)} install</div>
|
|
1385
|
+
<div class="pinokio-modal-subtitle">
|
|
1386
|
+
${detailsHtml ? `${detailsHtml} · ` : ''}Running <code>${commandHtml}</code>
|
|
1387
|
+
</div>
|
|
1388
|
+
</div>
|
|
1389
|
+
</div>
|
|
1390
|
+
<div class="pinokio-modal-body pinokio-modal-body--iframe">
|
|
1391
|
+
<iframe src="${shellUrl}"></iframe>
|
|
1392
|
+
</div>
|
|
1393
|
+
</div>
|
|
1394
|
+
`;
|
|
1395
|
+
|
|
1396
|
+
Swal.fire({
|
|
1397
|
+
html: modalHtml,
|
|
1398
|
+
customClass: {
|
|
1399
|
+
popup: 'pinokio-modern-modal',
|
|
1400
|
+
htmlContainer: 'pinokio-modern-html',
|
|
1401
|
+
closeButton: 'pinokio-modern-close'
|
|
1402
|
+
},
|
|
1403
|
+
backdrop: 'rgba(9,11,15,0.65)',
|
|
1404
|
+
width: 'min(760px, 90vw)',
|
|
1405
|
+
showConfirmButton: false,
|
|
1406
|
+
showCloseButton: true,
|
|
1407
|
+
buttonsStyling: false,
|
|
1408
|
+
focusConfirm: false
|
|
1409
|
+
});
|
|
1410
|
+
};
|
|
1411
|
+
|
|
1412
|
+
const registerPackageInstallForms = () => {
|
|
1413
|
+
const forms = document.querySelectorAll('.package-install-form');
|
|
1414
|
+
forms.forEach((form) => {
|
|
1415
|
+
form.addEventListener('submit', (event) => {
|
|
1416
|
+
event.preventDefault();
|
|
1417
|
+
if (typeof form.reportValidity === 'function' && !form.reportValidity()) {
|
|
1418
|
+
return;
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
const manager = (form.dataset.manager || '').toLowerCase();
|
|
1422
|
+
const formData = new FormData(form);
|
|
1423
|
+
const payload = buildInstallPayload(manager, formData);
|
|
1424
|
+
if (!payload) {
|
|
1425
|
+
const nameInput = form.querySelector('input[name="name"]');
|
|
1426
|
+
if (nameInput) {
|
|
1427
|
+
nameInput.focus();
|
|
1428
|
+
}
|
|
1429
|
+
return;
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
openPackageInstallModal(payload);
|
|
1433
|
+
form.reset();
|
|
1434
|
+
if (manager === 'conda') {
|
|
1435
|
+
const channelInput = form.querySelector('input[name="channel"]');
|
|
1436
|
+
if (channelInput && !channelInput.value) {
|
|
1437
|
+
channelInput.value = 'conda-forge';
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
});
|
|
1441
|
+
});
|
|
1442
|
+
};
|
|
1443
|
+
|
|
1002
1444
|
document.addEventListener('DOMContentLoaded', function() {
|
|
1003
1445
|
const isPending = <%= pending ? 'true' : 'false' %>;
|
|
1004
1446
|
|
|
@@ -1014,6 +1456,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
1014
1456
|
const tabs = document.querySelectorAll('.package-tab');
|
|
1015
1457
|
const panels = document.querySelectorAll('.tab-panel');
|
|
1016
1458
|
|
|
1459
|
+
registerPackageInstallForms();
|
|
1460
|
+
|
|
1017
1461
|
tabs.forEach(tab => {
|
|
1018
1462
|
tab.addEventListener('click', function() {
|
|
1019
1463
|
const targetTab = this.getAttribute('data-tab');
|