pinokiod 7.5.52 → 7.5.54

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "7.5.52",
3
+ "version": "7.5.54",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1433,6 +1433,17 @@ body.main-sidebar-page main > .task-container {
1433
1433
  margin-top: 8px;
1434
1434
  padding-top: 0;
1435
1435
  }
1436
+ .main-sidebar .main-sidebar-section-connect {
1437
+ margin-top: auto;
1438
+ padding-top: 10px;
1439
+ }
1440
+ .main-sidebar .main-sidebar-section-connect::before {
1441
+ content: "";
1442
+ display: block;
1443
+ height: 1px;
1444
+ margin-bottom: 10px;
1445
+ background: var(--pinokio-sidebar-separator);
1446
+ }
1436
1447
  .main-sidebar-section-title {
1437
1448
  min-height: 20px;
1438
1449
  display: flex;
@@ -85,7 +85,7 @@ body.app-page > header.navheader h1 > .home-server-popover {
85
85
  }
86
86
  .app-header-info {
87
87
  position: relative;
88
- flex: 1 1 auto;
88
+ flex: 0 1 auto;
89
89
  min-width: 0;
90
90
  display: inline-flex;
91
91
  align-items: center;
@@ -100,8 +100,7 @@ body.app-page > header.navheader h1 > .home-server-popover {
100
100
  align-items: center;
101
101
  gap: 8px;
102
102
  min-width: 0;
103
- width: 100%;
104
- max-width: none;
103
+ max-width: min(260px, 32vw);
105
104
  height: 22px;
106
105
  padding: 0 4px;
107
106
  margin: 0 -4px;
@@ -325,11 +324,20 @@ body.app-page header.navheader .nav-labeled-action:focus-visible {
325
324
  color: var(--pinokio-sidebar-tab-active-color);
326
325
  }
327
326
  @media only screen and (max-width: 1180px) {
328
- body.app-page header.navheader .app-header-identity-separator,
329
- body.app-page header.navheader .resource-chip--cpu,
330
- body.app-page header.navheader .resource-chip--ram,
331
- body.app-page header.navheader .resource-chip--vram {
332
- display: none !important;
327
+ body.app-page header.navheader .app-header-identity {
328
+ gap: 5px;
329
+ padding: 0 9px;
330
+ margin-right: 6px;
331
+ }
332
+ body.app-page header.navheader .app-header-info-trigger {
333
+ gap: 6px;
334
+ }
335
+ body.app-page header.navheader .resource-usage-trigger {
336
+ gap: 4px;
337
+ padding: 0 3px;
338
+ }
339
+ body.app-page header.navheader .resource-chip {
340
+ font-size: 10px;
333
341
  }
334
342
  }
335
343
  .resource-usage {
@@ -6884,10 +6892,10 @@ header.navheader h1 {
6884
6892
  min-width: 0;
6885
6893
  display: flex;
6886
6894
  align-items: center;
6887
- justify-content: flex-start;
6895
+ justify-content: center;
6888
6896
  align-content: center;
6889
6897
  flex-wrap: wrap;
6890
- gap: 2px 5px;
6898
+ gap: 2px 8px;
6891
6899
  min-height: 38px;
6892
6900
  padding: 2px 9px;
6893
6901
  border-radius: 999px;
@@ -6901,11 +6909,11 @@ header.navheader h1 {
6901
6909
  background: rgba(255,255,255,0.06);
6902
6910
  }
6903
6911
  .mobile-bottom-nav .app-header-info {
6904
- flex: 0 1 42%;
6905
- max-width: 42%;
6912
+ flex: 0 1 auto;
6913
+ max-width: min(260px, 42%);
6906
6914
  }
6907
6915
  .mobile-bottom-nav .app-header-info-trigger {
6908
- width: 100%;
6916
+ width: auto;
6909
6917
  max-width: 100%;
6910
6918
  height: auto;
6911
6919
  min-height: 18px;
@@ -6976,14 +6984,15 @@ header.navheader h1 {
6976
6984
  display: none;
6977
6985
  }
6978
6986
  .mobile-bottom-nav .resource-usage {
6979
- flex: 1 1 120px;
6980
- max-width: none;
6987
+ flex: 0 1 auto;
6988
+ max-width: 100%;
6981
6989
  }
6982
6990
  .mobile-bottom-nav .resource-usage-trigger {
6983
- width: 100%;
6991
+ width: auto;
6992
+ max-width: 100%;
6984
6993
  height: auto;
6985
6994
  min-height: 0;
6986
- justify-content: flex-start;
6995
+ justify-content: center;
6987
6996
  align-content: center;
6988
6997
  flex-wrap: wrap;
6989
6998
  gap: 2px 5px;
@@ -40,6 +40,12 @@
40
40
  <a href="/connect" class="tab <%= sidebarSelected === 'connect' ? 'selected' : '' %>"><i class="fa-solid fa-plug"></i><div class='caption'>Login</div></a>
41
41
  <a class="tab <%= sidebarSelected === 'settings' ? 'selected' : '' %>" href="/home?mode=settings"><i class="fa-solid fa-gear"></i><div class='caption'>Settings</div></a>
42
42
  </div>
43
+
44
+ <div class='main-sidebar-section main-sidebar-section-connect' aria-label="Community links">
45
+ <a class="tab" href="https://x.com/cocktailpeanut" target="_blank" rel="noopener noreferrer" data-main-sidebar-browser-link><i class="fa-brands fa-x-twitter"></i><div class='caption'>Updates</div></a>
46
+ <a class="tab" href="https://discord.gg/TQdNwadtE4" target="_blank" rel="noopener noreferrer" data-main-sidebar-browser-link><i class="fa-brands fa-discord"></i><div class='caption'>Discord</div></a>
47
+ <a class="tab" href="https://pinokiocomputer.substack.com" target="_blank" rel="noopener noreferrer" data-main-sidebar-browser-link><i class="fa-solid fa-newspaper"></i><div class='caption'>Newsletter</div></a>
48
+ </div>
43
49
  </nav>
44
50
  </aside>
45
51
  <script>
@@ -50,6 +56,23 @@
50
56
  const sidebar = document.getElementById("main-sidebar");
51
57
  const peekTrigger = document.querySelector("[data-main-sidebar-peek-trigger]");
52
58
 
59
+ if (sidebar) {
60
+ sidebar.querySelectorAll("[data-main-sidebar-browser-link]").forEach((link) => {
61
+ link.addEventListener("click", (event) => {
62
+ const href = link.getAttribute("href") || "";
63
+ if (!href) return;
64
+ event.preventDefault();
65
+ event.stopPropagation();
66
+ const agent = document.body ? document.body.getAttribute("data-agent") : null;
67
+ if (agent === "electron") {
68
+ window.open(href, "_blank", "browser");
69
+ } else {
70
+ window.open(href, "_blank");
71
+ }
72
+ });
73
+ });
74
+ }
75
+
53
76
  const initHomeServerStatusBadge = () => {
54
77
  if (!sidebar || window.__pinokioMainSidebarHomeServerStatusInit) return;
55
78
  const homeServerTab = sidebar.querySelector("[data-main-sidebar-home-server-tab]");
@@ -643,8 +643,23 @@ test("app navbar stays on one line when app identity and Home Server controls ar
643
643
  assert.match(appView, /body\.app-page > header\.navheader h1 > \.home,[\s\S]*body\.app-page > header\.navheader h1 > \.home-server-popover\s*\{[\s\S]*flex:\s*0 0 auto;/)
644
644
  assert.match(appView, /body\.app-page header\.navheader h1 > \.flexible\s*\{[\s\S]*flex:\s*0 0 0;[\s\S]*margin-left:\s*auto;/)
645
645
  assert.match(appView, /\.app-header-identity\s*\{[\s\S]*flex:\s*1 1 auto;[\s\S]*max-width:\s*none;/)
646
+ assert.match(appView, /\.app-header-info\s*\{[\s\S]*flex:\s*0 1 auto;/)
647
+ const identityTriggerStart = appView.indexOf(".app-header-info-trigger {")
648
+ const identityTriggerEnd = appView.indexOf("\n}", identityTriggerStart)
649
+ const identityTriggerRule = identityTriggerStart >= 0 && identityTriggerEnd > identityTriggerStart
650
+ ? appView.slice(identityTriggerStart, identityTriggerEnd)
651
+ : ""
652
+ assert.match(identityTriggerRule, /max-width:\s*min\(260px, 32vw\);/)
653
+ assert.doesNotMatch(identityTriggerRule, /width:\s*100%;/)
654
+ assert.doesNotMatch(identityTriggerRule, /max-width:\s*none;/)
646
655
  assert.doesNotMatch(appView, /nav-action-label\s*\{[^}]*display:\s*none;/)
647
- assert.match(appView, /@media only screen and \(max-width: 1180px\)\s*\{[\s\S]*\.resource-chip--vram[\s\S]*display:\s*none !important;/)
656
+ const compactRuleStart = appView.indexOf("@media only screen and (max-width: 1180px)")
657
+ const compactRuleEnd = appView.indexOf(".resource-usage {", compactRuleStart)
658
+ const compactHeaderRule = compactRuleStart >= 0 && compactRuleEnd > compactRuleStart
659
+ ? appView.slice(compactRuleStart, compactRuleEnd)
660
+ : ""
661
+ assert.match(compactHeaderRule, /body\.app-page header\.navheader \.resource-usage-trigger\s*\{[\s\S]*gap:\s*4px;[\s\S]*padding:\s*0 3px;/)
662
+ assert.doesNotMatch(compactHeaderRule, /\.resource-chip--(?:cpu|ram|vram)[\s\S]*display:\s*none !important;/)
648
663
  })
649
664
 
650
665
  test("download page header does not show browser chrome controls", async () => {
@@ -970,7 +985,30 @@ test("app identity popover exposes launcher metadata and remote browser link", a
970
985
  assert.match(appView, /window\.open\(href, "_blank"\)/)
971
986
  assert.match(appView, /window\.open\(href, "_blank", "browser"\)/)
972
987
  assert.match(appView, /No browser-openable remote detected/)
973
- assert.match(appView, /\.mobile-bottom-nav__identity \{[\s\S]*overflow: visible;/)
988
+ const cssRule = (selector) => {
989
+ const start = appView.indexOf(`${selector} {`)
990
+ if (start < 0) return ""
991
+ const tail = appView.slice(start)
992
+ const end = tail.match(/\n\s*\}/)
993
+ return end ? tail.slice(0, end.index + end[0].length) : ""
994
+ }
995
+ const mobileIdentityRule = cssRule(".mobile-bottom-nav__identity")
996
+ const mobileInfoRule = cssRule(".mobile-bottom-nav .app-header-info")
997
+ const mobileInfoTriggerRule = cssRule(".mobile-bottom-nav .app-header-info-trigger")
998
+ const mobileResourceRule = cssRule(".mobile-bottom-nav .resource-usage")
999
+ const mobileResourceTriggerRule = cssRule(".mobile-bottom-nav .resource-usage-trigger")
1000
+ assert.match(mobileIdentityRule, /justify-content:\s*center;/)
1001
+ assert.match(mobileIdentityRule, /overflow:\s*visible;/)
1002
+ assert.doesNotMatch(mobileIdentityRule, /justify-content:\s*flex-start;/)
1003
+ assert.match(mobileInfoRule, /flex:\s*0 1 auto;/)
1004
+ assert.match(mobileInfoRule, /max-width:\s*min\(260px, 42%\);/)
1005
+ assert.match(mobileInfoTriggerRule, /width:\s*auto;/)
1006
+ assert.doesNotMatch(mobileInfoTriggerRule, /(^|\n)\s*width:\s*100%;/)
1007
+ assert.match(mobileResourceRule, /flex:\s*0 1 auto;/)
1008
+ assert.match(mobileResourceRule, /max-width:\s*100%;/)
1009
+ assert.match(mobileResourceTriggerRule, /width:\s*auto;/)
1010
+ assert.match(mobileResourceTriggerRule, /justify-content:\s*center;/)
1011
+ assert.doesNotMatch(mobileResourceTriggerRule, /justify-content:\s*flex-start;/)
974
1012
  assert.match(appView, /\.mobile-bottom-nav \.app-header-info-popover \{[\s\S]*bottom: calc\(100% \+ 9px\);[\s\S]*width: min\(390px, calc\(100vw - 24px\)\);/)
975
1013
  assert.match(appView, /\.mobile-bottom-nav \.resource-usage-popover \{[\s\S]*bottom: calc\(100% \+ 9px\);/)
976
1014
  assert.doesNotMatch(appView, /Open launcher remote/)
@@ -59,3 +59,26 @@ test('main sidebar no longer renders peer rows or phone access modal', async ()
59
59
  assert.doesNotMatch(source, /peer_access_router_installed/)
60
60
  assert.doesNotMatch(style, /main-sidebar-peer/)
61
61
  })
62
+
63
+ test('main sidebar renders external community links as a footer cluster', async () => {
64
+ const source = await fs.readFile(sidebarFile, 'utf8')
65
+ const style = await fs.readFile(path.resolve(__dirname, '..', 'server', 'public', 'style.css'), 'utf8')
66
+
67
+ const configureIndex = source.indexOf('aria-label="Configure"')
68
+ const connectedIndex = source.indexOf('aria-label="Community links"')
69
+
70
+ assert.notEqual(configureIndex, -1)
71
+ assert.notEqual(connectedIndex, -1)
72
+ assert.ok(configureIndex < connectedIndex)
73
+ assert.match(source, /main-sidebar-section-connect/)
74
+ assert.doesNotMatch(source, /main-sidebar-section-connect[\s\S]*main-sidebar-section-title/)
75
+ assert.doesNotMatch(source, />Stay Connected</)
76
+ assert.match(source, /href="https:\/\/x\.com\/cocktailpeanut"[^>]*target="_blank"[^>]*rel="noopener noreferrer"[^>]*data-main-sidebar-browser-link[\s\S]*fa-brands fa-x-twitter[\s\S]*Updates/)
77
+ assert.match(source, /href="https:\/\/discord\.gg\/TQdNwadtE4"[^>]*target="_blank"[^>]*rel="noopener noreferrer"[^>]*data-main-sidebar-browser-link[\s\S]*fa-brands fa-discord[\s\S]*Discord/)
78
+ assert.match(source, /href="https:\/\/pinokiocomputer\.substack\.com"[^>]*target="_blank"[^>]*rel="noopener noreferrer"[^>]*data-main-sidebar-browser-link[\s\S]*fa-solid fa-newspaper[\s\S]*Newsletter/)
79
+ assert.doesNotMatch(source, /main-sidebar-section-connect[\s\S]*features="browser"/)
80
+ assert.match(source, /window\.open\(href, "_blank", "browser"\)/)
81
+ assert.match(source, /window\.open\(href, "_blank"\)/)
82
+ assert.match(style, /\.main-sidebar \.main-sidebar-section-connect \{[\s\S]*margin-top:\s*auto/)
83
+ assert.match(style, /\.main-sidebar \.main-sidebar-section-connect::before \{[\s\S]*background:\s*var\(--pinokio-sidebar-separator\)/)
84
+ })