pinokiod 7.3.9 → 7.3.11

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 (52) hide show
  1. package/kernel/bin/brew.js +12 -2
  2. package/kernel/bin/caddy.js +24 -20
  3. package/kernel/bin/huggingface.js +2 -2
  4. package/kernel/bin/setup.js +2 -3
  5. package/kernel/bin/uv.js +13 -6
  6. package/kernel/connect/index.js +5 -1
  7. package/kernel/connect/providers/huggingface/index.js +213 -75
  8. package/kernel/environment.js +16 -1
  9. package/kernel/router/localhost_home_router.js +7 -0
  10. package/kernel/shell.js +1 -5
  11. package/kernel/util.js +1 -0
  12. package/package.json +1 -1
  13. package/server/index.js +75 -33
  14. package/server/public/common.js +52 -88
  15. package/server/public/install.js +20 -2
  16. package/server/public/layout.js +1 -1
  17. package/server/public/nav.js +3 -1
  18. package/server/public/style.css +1455 -521
  19. package/server/public/tab-link-popover.css +162 -18
  20. package/server/public/tab-link-popover.js +230 -21
  21. package/server/public/task-launcher.css +182 -91
  22. package/server/public/terminal-settings.js +227 -50
  23. package/server/public/universal-launcher.css +42 -33
  24. package/server/public/urldropdown.css +284 -0
  25. package/server/views/app.ejs +1718 -352
  26. package/server/views/autolaunch.ejs +4 -5
  27. package/server/views/checkpoints.ejs +223 -50
  28. package/server/views/connect/huggingface.ejs +406 -325
  29. package/server/views/connect.ejs +0 -1
  30. package/server/views/github.ejs +277 -324
  31. package/server/views/index.ejs +65 -8
  32. package/server/views/install.ejs +134 -65
  33. package/server/views/logs.ejs +9 -8
  34. package/server/views/net.ejs +341 -64
  35. package/server/views/network.ejs +85 -63
  36. package/server/views/partials/main_sidebar.ejs +249 -24
  37. package/server/views/plugins.ejs +141 -3
  38. package/server/views/settings.ejs +103 -7
  39. package/server/views/setup.ejs +0 -5
  40. package/server/views/skills.ejs +0 -1
  41. package/server/views/task_list.ejs +0 -1
  42. package/server/views/terminal.ejs +285 -60
  43. package/server/views/terminals.ejs +346 -6
  44. package/server/views/tools.ejs +828 -1691
  45. package/test/caddy-install.test.js +53 -0
  46. package/test/connect-setup.test.js +16 -0
  47. package/test/github-connection.test.js +1 -1
  48. package/test/huggingface-bin.test.js +4 -4
  49. package/test/huggingface-connect.test.js +73 -0
  50. package/test/main-sidebar.test.js +31 -0
  51. package/test/shell-run-template.test.js +5 -1
  52. package/test/uv-bin.test.js +29 -0
@@ -606,10 +606,10 @@ body.dark .net .mark {
606
606
  }
607
607
  }
608
608
  body.net-page .task-container {
609
- padding: 20px 28px 32px;
609
+ padding: 0;
610
610
  }
611
611
  body.net-page .task-shell {
612
- width: min(1180px, 100%);
612
+ width: 100%;
613
613
  margin: 0;
614
614
  background: transparent;
615
615
  border: 0;
@@ -619,57 +619,124 @@ body.net-page .task-shell {
619
619
  backdrop-filter: none;
620
620
  }
621
621
  body.net-page .task-shell-header {
622
- padding-bottom: 18px;
622
+ padding: 12px 18px 14px;
623
+ }
624
+ body.net-page .task-shell-header-main {
625
+ max-width: 720px;
626
+ }
627
+ body.net-page .task-description {
628
+ max-width: 70ch;
623
629
  }
624
630
  body.net-page .task-shell-body {
625
- padding: 28px 28px 28px;
631
+ padding: 0 28px 28px;
626
632
  }
627
633
  body.net-page .container {
628
634
  margin: 0;
629
635
  }
636
+ body.net-page .net-routes-section {
637
+ padding-top: 12px;
638
+ }
639
+ body.net-page .net-section-head {
640
+ display: none;
641
+ }
630
642
  body.net-page .search {
631
- padding: 0 0 18px;
643
+ margin: 0;
644
+ padding: 0;
645
+ width: 100%;
632
646
  }
633
- body.net-page .search input[type=search] {
647
+ body.net-page .net-search-field {
634
648
  width: 100%;
635
- height: 46px;
636
- border-radius: 14px;
637
- border: 1px solid var(--task-border-strong);
638
- background: color-mix(in srgb, var(--task-panel) 98%, white);
639
- color: var(--task-text);
640
- padding: 0 14px;
641
649
  box-sizing: border-box;
650
+ margin: 0;
651
+ min-height: 34px;
652
+ padding: 0;
653
+ border: 0;
654
+ border-radius: 0;
655
+ border-bottom: 1px solid var(--task-border);
656
+ background: transparent;
657
+ gap: 8px;
658
+ }
659
+ body.net-page .net-search-field:focus-within {
660
+ border-color: var(--task-border-strong);
661
+ box-shadow: none;
662
+ background: transparent;
663
+ }
664
+ body.net-page .net-search-field > i {
665
+ font-size: 11px;
666
+ }
667
+ body.net-page .net-search-field .task-library-search-input {
668
+ min-height: 34px;
669
+ font-size: 13px;
670
+ border: 0 !important;
671
+ border-radius: 0 !important;
672
+ background: transparent !important;
673
+ box-shadow: none !important;
674
+ }
675
+ body.net-page .net-summary {
676
+ display: flex;
677
+ align-items: center;
678
+ gap: 10px;
679
+ flex-wrap: wrap;
680
+ justify-content: flex-end;
681
+ color: var(--task-muted);
682
+ font-size: 12px;
683
+ }
684
+ body.net-page .net-summary-item {
685
+ display: inline-flex;
686
+ align-items: center;
687
+ min-height: 0;
688
+ box-sizing: border-box;
689
+ padding: 0;
690
+ border: 0;
691
+ border-radius: 0;
692
+ color: var(--task-muted);
693
+ font-size: 12px;
694
+ line-height: 1;
695
+ white-space: nowrap;
642
696
  }
643
- body.dark.net-page .search input[type=search] {
644
- background: color-mix(in srgb, var(--task-panel) 96%, black);
697
+ body.net-page .net-summary-item + .net-summary-item::before {
698
+ content: "";
699
+ width: 1px;
700
+ height: 12px;
701
+ margin-right: 10px;
702
+ background: var(--task-border);
703
+ }
704
+ body.net-page .net-summary-item strong {
705
+ color: var(--task-text);
706
+ font-weight: 700;
645
707
  }
646
708
  body.net-page .running-apps {
647
709
  display: grid;
648
- gap: 14px;
710
+ gap: 0;
649
711
  }
650
712
  body.net-page .line {
651
- padding: 14px 18px 16px;
652
- border: 1px solid var(--task-border);
653
- border-radius: 18px;
654
- background: color-mix(in srgb, var(--task-panel) 94%, var(--task-soft));
713
+ padding: 11px 0;
714
+ border-top: 1px solid var(--task-border);
715
+ border-radius: 0;
716
+ background: transparent;
717
+ }
718
+ body.net-page .line.selected {
719
+ background: color-mix(in srgb, var(--task-soft) 34%, transparent);
655
720
  }
656
721
  body.net-page .line.align-top h3 {
657
722
  margin: 0;
658
723
  display: grid;
659
- grid-template-columns: 42px minmax(0, 1fr);
660
- gap: 14px;
724
+ grid-template-columns: 24px minmax(0, 1fr);
725
+ gap: 9px;
661
726
  align-items: start;
727
+ font-size: inherit;
662
728
  }
663
729
  body.net-page .placeholder-icon,
664
730
  body.net-page .line img {
665
- width: 42px;
666
- height: 42px;
667
- border-radius: 12px;
668
- background: color-mix(in srgb, var(--task-accent) 12%, transparent);
731
+ width: 22px;
732
+ height: 22px;
733
+ border-radius: 5px;
734
+ background: transparent;
669
735
  display: flex;
670
736
  align-items: center;
671
737
  justify-content: center;
672
- color: var(--task-accent);
738
+ color: var(--task-muted);
739
+ font-size: 11px;
673
740
  }
674
741
  body.net-page .line img {
675
742
  object-fit: cover;
@@ -678,32 +745,63 @@ body.net-page .line img {
678
745
  body.net-page .line .col {
679
746
  min-width: 0;
680
747
  padding-left: 0;
748
+ display: grid;
749
+ grid-template-columns: minmax(160px, 240px) minmax(0, 1fr);
750
+ gap: 4px 18px;
751
+ align-items: start;
681
752
  }
682
753
  body.net-page .line .title {
683
754
  display: flex;
684
755
  align-items: center;
685
- gap: 10px;
756
+ gap: 8px;
686
757
  color: var(--task-text);
687
- font-size: 17px;
758
+ font-size: 14px;
688
759
  font-weight: 700;
689
- margin-bottom: 8px;
760
+ line-height: 1.25;
761
+ margin: 0;
762
+ min-width: 0;
690
763
  }
691
764
  body.net-page .line .title i {
692
- font-size: 10px;
765
+ flex: 0 0 auto;
766
+ font-size: 8px;
693
767
  color: color-mix(in srgb, #84cc16 75%, var(--task-panel));
694
768
  }
769
+ body.net-page .line .title span {
770
+ min-width: 0;
771
+ overflow-wrap: anywhere;
772
+ }
773
+ body.net-page .line .description {
774
+ grid-column: 1;
775
+ margin: 2px 0 0;
776
+ padding: 0;
777
+ color: var(--task-muted);
778
+ font-size: 12px;
779
+ line-height: 1.45;
780
+ }
781
+ body.net-page .line .grid-3,
782
+ body.net-page .line .grid-1,
783
+ body.net-page .line .launch-btns {
784
+ grid-column: 2;
785
+ grid-row: 1 / span 3;
786
+ }
695
787
  body.net-page .grid-3 {
788
+ display: grid;
789
+ grid-template-columns: minmax(0, 1.15fr) repeat(2, minmax(0, 1fr));
696
790
  padding: 4px 0 0;
697
- gap: 0;
791
+ gap: 14px;
792
+ background: none !important;
793
+ margin-top: 0;
794
+ }
795
+ body.net-page .grid-1 {
796
+ padding: 0;
698
797
  background: none !important;
699
798
  margin-top: 0;
700
799
  }
701
800
  body.net-page .line .grid-3 .section,
702
801
  body.net-page .line .grid-1 .section {
703
802
  border: 0;
704
- border-left: 1px solid var(--task-border);
705
803
  border-radius: 0;
706
- padding: 4px 20px 0;
804
+ padding: 0;
707
805
  background: none;
708
806
  min-height: 0;
709
807
  }
@@ -718,27 +816,54 @@ body.net-page .line .grid-1 .section:last-child {
718
816
  }
719
817
  body.net-page .grid-3 .section h2,
720
818
  body.net-page .grid-1 .section h2 {
721
- margin: 0 0 8px;
722
- font-size: 12px;
819
+ display: flex;
820
+ align-items: center;
821
+ gap: 6px;
822
+ margin: 0 0 5px;
823
+ font-size: 11px;
723
824
  font-weight: 700;
724
825
  color: var(--task-text);
826
+ line-height: 1.2;
827
+ letter-spacing: 0;
828
+ }
829
+ body.net-page .grid-3 .section h2 i,
830
+ body.net-page .grid-1 .section h2 i {
831
+ width: 14px;
832
+ color: var(--task-muted);
833
+ text-align: center;
725
834
  }
726
835
  body.net-page .net {
836
+ display: flex;
837
+ align-items: center;
838
+ gap: 6px;
839
+ max-width: 100%;
840
+ width: fit-content;
727
841
  margin: 0 0 4px;
728
842
  padding: 0;
729
843
  border: 0;
730
844
  color: var(--task-muted);
731
845
  line-height: 1.45;
846
+ text-decoration: none;
847
+ overflow-wrap: anywhere;
732
848
  }
733
849
  body.dark.net-page .net {
734
850
  color: var(--task-muted);
735
851
  }
852
+ body.net-page .net:hover,
853
+ body.dark.net-page .net:hover {
854
+ color: var(--task-text);
855
+ }
736
856
  body.net-page .access-point-list {
857
+ list-style: none;
858
+ padding: 0;
737
859
  margin-top: 0;
860
+ display: grid;
861
+ gap: 4px;
738
862
  }
739
863
  body.net-page .access-point-list li {
740
864
  align-items: center;
741
865
  gap: 8px;
866
+ min-width: 0;
742
867
  }
743
868
  body.net-page .access-point-list .net {
744
869
  margin: 0;
@@ -752,26 +877,33 @@ body.net-page .net .mark {
752
877
  }
753
878
  body.net-page .badge,
754
879
  body.dark.net-page .badge {
755
- background: color-mix(in srgb, var(--task-accent) 18%, transparent);
756
- color: var(--task-accent);
880
+ min-height: 18px;
881
+ padding: 0 6px;
882
+ border: 1px solid color-mix(in srgb, var(--task-accent) 22%, var(--task-border));
883
+ border-radius: 999px;
884
+ background: color-mix(in srgb, var(--task-accent) 10%, var(--task-panel));
885
+ color: color-mix(in srgb, var(--task-accent) 62%, var(--task-text));
757
886
  display: inline-flex;
758
887
  align-items: center;
759
888
  justify-content: center;
889
+ font-size: 10px;
890
+ font-weight: 650;
760
891
  line-height: 1;
761
892
  transform: translateY(-1px);
762
893
  }
763
894
  body.net-page .get-dns-btn,
764
895
  body.net-page .btn {
765
- border-radius: 12px;
896
+ border-radius: 6px;
766
897
  }
767
898
  body.net-page .get-dns-btn {
768
- margin-top: 10px;
899
+ margin-top: 6px;
769
900
  max-width: fit-content;
770
- background: color-mix(in srgb, var(--task-panel) 90%, var(--task-soft));
901
+ background: transparent;
771
902
  border: 1px solid var(--task-border);
772
903
  color: var(--task-text);
773
- padding: 7px 12px;
774
- min-height: 38px;
904
+ padding: 0 10px;
905
+ min-height: 30px;
906
+ font-weight: 650;
775
907
  }
776
908
  body.net-page .line.align-top h3 .col .get-dns-btn {
777
909
  width: auto;
@@ -780,13 +912,13 @@ body.net-page .line.align-top h3 .col .get-dns-btn {
780
912
  body.net-page .section-container {
781
913
  display: grid;
782
914
  grid-template-columns: repeat(3, minmax(0, 1fr));
783
- gap: 18px;
915
+ gap: 24px;
784
916
  }
785
917
  body.net-page .section-col {
786
918
  min-width: 0;
787
919
  }
788
920
  body.net-page .section-header {
789
- padding: 0 0 14px;
921
+ padding: 0 0 10px;
790
922
  background: transparent;
791
923
  }
792
924
  body.dark.net-page .section-header {
@@ -800,9 +932,10 @@ body.net-page .section-header .grid-1 {
800
932
  background: transparent !important;
801
933
  }
802
934
  body.net-page .section-header .section {
803
- border: 1px solid var(--task-border);
804
- border-radius: 16px;
805
- background: color-mix(in srgb, var(--task-panel) 94%, var(--task-soft));
935
+ border: 0;
936
+ border-radius: 0;
937
+ background: transparent;
938
+ padding: 0;
806
939
  text-align: left;
807
940
  }
808
941
  body.net-page .section-header .section div {
@@ -810,9 +943,9 @@ body.net-page .section-header .section div {
810
943
  text-align: left;
811
944
  }
812
945
  body.net-page .section-header .section h2 {
813
- margin: 0 0 8px;
946
+ margin: 0 0 4px;
814
947
  text-align: left;
815
- font-size: 17px;
948
+ font-size: 14px;
816
949
  font-weight: 700;
817
950
  color: var(--task-text);
818
951
  }
@@ -932,6 +1065,19 @@ body.net-page .dns-modal-terminal-container {
932
1065
  body.net-page .section-container {
933
1066
  grid-template-columns: 1fr;
934
1067
  }
1068
+ body.net-page .line .col {
1069
+ grid-template-columns: 1fr;
1070
+ }
1071
+ body.net-page .line .grid-3,
1072
+ body.net-page .line .grid-1,
1073
+ body.net-page .line .launch-btns {
1074
+ grid-column: 1;
1075
+ grid-row: auto;
1076
+ }
1077
+ body.net-page .grid-3 {
1078
+ grid-template-columns: repeat(3, minmax(0, 1fr));
1079
+ padding-top: 6px;
1080
+ }
935
1081
  }
936
1082
  @media only screen and (max-width: 820px) {
937
1083
  body.net-page .task-shell-header,
@@ -944,14 +1090,17 @@ body.net-page .dns-modal-terminal-container {
944
1090
  body.net-page .task-container {
945
1091
  padding: 16px 14px 24px;
946
1092
  }
1093
+ body.net-page .net-summary {
1094
+ justify-content: flex-start;
1095
+ }
947
1096
  body.net-page .task-shell-body {
948
1097
  padding-bottom: 22px;
949
1098
  }
950
1099
  body.net-page .line.align-top h3 {
951
- grid-template-columns: 1fr;
1100
+ grid-template-columns: 28px minmax(0, 1fr);
952
1101
  }
953
1102
  body.net-page .line .title {
954
- margin-bottom: 6px;
1103
+ margin-bottom: 2px;
955
1104
  }
956
1105
  body.net-page .grid-3 {
957
1106
  grid-template-columns: 1fr;
@@ -959,7 +1108,7 @@ body.net-page .dns-modal-terminal-container {
959
1108
  body.net-page .line .grid-3 .section,
960
1109
  body.net-page .line .grid-1 .section {
961
1110
  border-left: 0;
962
- padding: 10px 0 0;
1111
+ padding: 0;
963
1112
  }
964
1113
  body.net-page .dns-modal-popup,
965
1114
  body.net-page .dns-progress-popup {
@@ -977,6 +1126,110 @@ body.net-page .dns-modal-terminal-container {
977
1126
  padding: 12px 14px;
978
1127
  }
979
1128
  }
1129
+
1130
+ /* Minimal modal normalization for DNS creation/progress dialogs. */
1131
+ body.net-page .dns-modal-popup,
1132
+ body.net-page .dns-progress-popup {
1133
+ width: min(560px, calc(100vw - 32px)) !important;
1134
+ padding: 18px !important;
1135
+ border-radius: 8px !important;
1136
+ border: 1px solid var(--task-border) !important;
1137
+ background: var(--task-panel) !important;
1138
+ color: var(--task-text) !important;
1139
+ box-shadow: 0 18px 50px rgba(15, 23, 42, 0.16) !important;
1140
+ }
1141
+
1142
+ body.dark.net-page .dns-modal-popup,
1143
+ body.dark.net-page .dns-progress-popup {
1144
+ background: #1b1c1d !important;
1145
+ box-shadow: 0 24px 72px rgba(0, 0, 0, 0.42) !important;
1146
+ }
1147
+
1148
+ body.net-page .dns-modal-popup .swal2-title,
1149
+ body.net-page .dns-progress-popup .swal2-title {
1150
+ margin: 0 0 12px !important;
1151
+ font-size: 18px !important;
1152
+ line-height: 1.25 !important;
1153
+ font-weight: 600 !important;
1154
+ letter-spacing: 0 !important;
1155
+ color: var(--task-text) !important;
1156
+ text-align: left !important;
1157
+ }
1158
+
1159
+ body.net-page .dns-modal-popup .swal2-actions,
1160
+ body.net-page .dns-progress-popup .swal2-actions {
1161
+ gap: 8px !important;
1162
+ justify-content: flex-end !important;
1163
+ margin-top: 16px !important;
1164
+ }
1165
+
1166
+ body.net-page .dns-modal-popup .swal2-confirm.swal2-styled,
1167
+ body.net-page .dns-progress-popup .swal2-confirm.swal2-styled,
1168
+ body.net-page .dns-modal-popup .swal2-cancel.swal2-styled,
1169
+ body.net-page .dns-progress-popup .swal2-cancel.swal2-styled {
1170
+ min-height: 32px !important;
1171
+ padding: 0 12px !important;
1172
+ border-radius: 6px !important;
1173
+ box-shadow: none !important;
1174
+ font-size: 13px !important;
1175
+ font-weight: 600 !important;
1176
+ }
1177
+
1178
+ body.net-page .dns-modal-popup .swal2-confirm.swal2-styled,
1179
+ body.net-page .dns-progress-popup .swal2-confirm.swal2-styled {
1180
+ border-color: var(--task-text) !important;
1181
+ background: var(--task-text) !important;
1182
+ color: var(--task-panel) !important;
1183
+ }
1184
+
1185
+ body.dark.net-page .dns-modal-popup .swal2-confirm.swal2-styled,
1186
+ body.dark.net-page .dns-progress-popup .swal2-confirm.swal2-styled {
1187
+ color: #1b1c1d !important;
1188
+ }
1189
+
1190
+ body.net-page .dns-modal-popup .swal2-cancel.swal2-styled,
1191
+ body.net-page .dns-progress-popup .swal2-cancel.swal2-styled {
1192
+ border-color: var(--task-border) !important;
1193
+ background: transparent !important;
1194
+ color: var(--task-muted) !important;
1195
+ }
1196
+
1197
+ body.net-page .dns-modal-popup .swal2-cancel.swal2-styled:hover,
1198
+ body.net-page .dns-progress-popup .swal2-cancel.swal2-styled:hover,
1199
+ body.net-page .dns-modal-popup .swal2-cancel.swal2-styled:focus-visible,
1200
+ body.net-page .dns-progress-popup .swal2-cancel.swal2-styled:focus-visible {
1201
+ background: var(--task-soft) !important;
1202
+ color: var(--task-text) !important;
1203
+ outline: none !important;
1204
+ }
1205
+
1206
+ body.net-page .dns-modal .address-section input {
1207
+ height: 36px !important;
1208
+ border-radius: 6px !important;
1209
+ border-color: var(--task-border-strong) !important;
1210
+ background: var(--task-panel) !important;
1211
+ color: var(--task-text) !important;
1212
+ }
1213
+
1214
+ body.net-page .dns-modal .address-section input:focus {
1215
+ border-color: var(--task-text) !important;
1216
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--task-text) 12%, transparent) !important;
1217
+ }
1218
+
1219
+ body.net-page .dns-modal .address-result,
1220
+ body.net-page .dns-modal-terminal-container {
1221
+ border-radius: 7px !important;
1222
+ border-color: var(--task-border) !important;
1223
+ background: var(--task-soft) !important;
1224
+ }
1225
+
1226
+ @media only screen and (max-width: 560px) {
1227
+ body.net-page .dns-modal-popup,
1228
+ body.net-page .dns-progress-popup {
1229
+ width: calc(100vw - 24px) !important;
1230
+ padding: 16px !important;
1231
+ }
1232
+ }
980
1233
  </style>
981
1234
  <script src="/window_storage.js"></script>
982
1235
  <script src="/popper.min.js"></script>
@@ -1017,6 +1270,11 @@ document.addEventListener('DOMContentLoaded', function() {
1017
1270
  <script src="/fscreator.js"></script>
1018
1271
  <script src="/fseditor.js"></script>
1019
1272
  </head>
1273
+ <%
1274
+ const routeCount = current_host === host
1275
+ ? processes.length + static_routes.length
1276
+ : processes.length + installed.length + serverless.length
1277
+ %>
1020
1278
  <body class='<%=theme%> main-sidebar-page task-launcher-page task-page net-page' data-agent="<%=agent%>">
1021
1279
  <% if (error) { %>
1022
1280
  <nav class='error-message'>
@@ -1063,24 +1321,42 @@ document.addEventListener('DOMContentLoaded', function() {
1063
1321
  <section class='task-shell net-shell'>
1064
1322
  <header class='task-shell-header'>
1065
1323
  <div class='task-shell-header-main'>
1066
- <p class='task-eyebrow'>Network</p>
1067
1324
  <h1 class='task-title'><%= peer && peer.name ? peer.name : host %></h1>
1068
1325
  <p class='task-description'><%= current_host === host ? 'Live local, LAN, and peer routes for apps on this machine.' : 'Available routes and installed apps exposed by this peer.' %></p>
1069
1326
  </div>
1327
+ <div class='task-header-actions net-summary'>
1328
+ <span class='net-summary-item'><strong><%= routeCount %></strong>&nbsp;<%= routeCount === 1 ? 'entry' : 'entries' %></span>
1329
+ <% if (current_host === host) { %>
1330
+ <span class='net-summary-item'><strong><%= processes.length %></strong>&nbsp;live</span>
1331
+ <span class='net-summary-item'><strong><%= static_routes.length %></strong>&nbsp;static</span>
1332
+ <% } else { %>
1333
+ <span class='net-summary-item'><strong><%= processes.length %></strong>&nbsp;online</span>
1334
+ <span class='net-summary-item'><strong><%= installed.length %></strong>&nbsp;installed</span>
1335
+ <% } %>
1336
+ </div>
1070
1337
  </header>
1071
1338
  <div class='task-shell-body net-shell-body'>
1072
- <div class='container'>
1073
- <form class='search'>
1074
- <!--
1075
- <div class='app-btns'>
1076
- <a class='btn create-new' href="/init"><i class="fa-solid fa-plus"></i> Build</a>
1077
- <a class='btn' id='explore' href="/home?mode=explore"><i class="fa-solid fa-magnifying-glass"></i> Community</a>
1339
+ <section class='task-section net-routes-section' aria-label="<%= current_host === host ? 'App routes' : 'Peer access' %>">
1340
+ <div class='task-section-head net-section-head'>
1341
+ <div>
1342
+ <h2 class='task-section-title'><%= current_host === host ? 'App routes' : 'Peer access' %></h2>
1343
+ <p class='task-section-subcopy'><%= current_host === host ? 'Open local, LAN, or peer URLs for each running app.' : 'Open online routes or launch apps exposed by this peer.' %></p>
1078
1344
  </div>
1079
- -->
1080
- <input type='search' class="flexible" placeholder='Search apps'>
1081
- </form>
1082
- <div class='running-apps'>
1345
+ </div>
1346
+ <div class='container'>
1347
+ <form class='search net-toolbar' role='search'>
1348
+ <label class='task-library-search-field net-search-field'>
1349
+ <i class='fa-solid fa-magnifying-glass' aria-hidden='true'></i>
1350
+ <input type='search' class="task-library-search-input flexible" placeholder='Search apps' autocomplete='off'>
1351
+ </label>
1352
+ </form>
1353
+ <div class='running-apps net-route-list'>
1083
1354
  <% if (current_host === host) { %>
1355
+ <% if (processes.length === 0 && static_routes.length === 0) { %>
1356
+ <div class='task-empty-state'>
1357
+ <p class='task-empty-copy'>No live app routes are available on this machine.</p>
1358
+ </div>
1359
+ <% } %>
1084
1360
  <% processes.forEach((item, index) => { %>
1085
1361
  <div class='index line align-top' data-index="<%=index%>" data-name="<%=item.name%>" data-title="<%=item.name%>" data-description="<%=item.internal_router.join(' ')%> <%=item.port%>">
1086
1362
  <h3>
@@ -1353,8 +1629,9 @@ document.addEventListener('DOMContentLoaded', function() {
1353
1629
  </div>
1354
1630
  </div>
1355
1631
  <% } %>
1632
+ </div>
1356
1633
  </div>
1357
- </div>
1634
+ </section>
1358
1635
  </div>
1359
1636
  </section>
1360
1637
  </div>