pinokiod 8.0.75 → 8.0.76

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": "8.0.75",
3
+ "version": "8.0.76",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1678,6 +1678,40 @@ body.vault-page .vault-view-tabs {
1678
1678
  .vault-view-tabs .vault-nav-count {
1679
1679
  font-size: 10.5px;
1680
1680
  }
1681
+ .vault-view-tabs .vault-nav-row.attention {
1682
+ background: color-mix(in srgb, var(--vault-warning) 9%, var(--task-panel));
1683
+ color: var(--vault-warning);
1684
+ }
1685
+ .vault-view-tabs .vault-nav-row.attention:hover {
1686
+ background: color-mix(in srgb, var(--vault-warning) 14%, var(--task-panel));
1687
+ }
1688
+ .vault-view-tabs .vault-nav-row.attention.selected {
1689
+ border-bottom-color: var(--task-accent);
1690
+ }
1691
+ .vault-view-tabs .vault-nav-row.attention .vault-nav-name {
1692
+ color: var(--vault-warning);
1693
+ font-weight: 680;
1694
+ }
1695
+ .vault-view-tabs .vault-nav-row.attention .vault-nav-name::before {
1696
+ width: 6px;
1697
+ height: 6px;
1698
+ display: inline-block;
1699
+ margin-right: 6px;
1700
+ border-radius: 50%;
1701
+ background: var(--vault-warning);
1702
+ content: "";
1703
+ vertical-align: 1px;
1704
+ }
1705
+ .vault-view-tabs .vault-nav-row.attention .vault-nav-count {
1706
+ min-width: 20px;
1707
+ padding: 1px 5px;
1708
+ border-radius: 5px;
1709
+ background: color-mix(in srgb, var(--vault-warning) 72%, var(--task-panel));
1710
+ color: #241300;
1711
+ font-weight: 720;
1712
+ line-height: 1.35;
1713
+ text-align: center;
1714
+ }
1681
1715
  .vault-pane-action-note { color: var(--task-muted); font-size: 11.5px; white-space: nowrap; }
1682
1716
  .vault-toolbar {
1683
1717
  display: flex;
@@ -707,16 +707,20 @@ const eventLabels = {
707
707
  }
708
708
  const renderViews = () => {
709
709
  const counts = state.data.inventory.counts
710
+ const attentionViews = new Set(["duplicates", "reclaimable"])
710
711
  el("views-label").textContent = COPY.views
711
712
  const views = IS_APP_MODE
712
713
  ? ["all", "duplicates", "unavailable", "shared", "tracked", "activity"]
713
714
  : ["all", "duplicates", "unavailable", "shared", "tracked", "reclaimable", "activity"]
714
- el("vault-views").innerHTML = views.map((view) => `
715
- <button class="vault-nav-row ${state.view === view ? "selected" : ""}" type="button" data-view="${view}" ${state.view === view ? 'aria-current="page"' : ""}>
715
+ el("vault-views").innerHTML = views.map((view) => {
716
+ const needsAttention = attentionViews.has(view) && Number(counts[view]) > 0
717
+ return `
718
+ <button class="vault-nav-row ${state.view === view ? "selected" : ""} ${needsAttention ? "attention" : ""}" type="button" data-view="${view}" ${state.view === view ? 'aria-current="page"' : ""}>
716
719
  <i class="${viewIcon[view]}"></i>
717
720
  <span class="vault-nav-copy"><span class="vault-nav-name">${esc(viewLabel[view])}</span></span>
718
- <span class="vault-nav-count ${view === "duplicates" && counts[view] ? "attention" : ""}">${counts[view]}</span>
719
- </button>`).join("")
721
+ <span class="vault-nav-count ${needsAttention ? "attention" : ""}">${counts[view]}</span>
722
+ </button>`
723
+ }).join("")
720
724
  }
721
725
 
722
726
  const renderSourceNode = (source, depth, counts) => {
@@ -548,6 +548,14 @@ describe("Save Space interface", () => {
548
548
  /body\.vault-page \.vault-view-tabs\s*\{[^}]*padding:\s*0;/s)
549
549
  assert.match(vaultCss,
550
550
  /body\.dark\.vault-page\s*\{[^}]*--task-panel:\s*var\(--pinokio-sidebar-tabbar-bg\);/s)
551
+ assert.match(vaultCss,
552
+ /\.vault-view-tabs \.vault-nav-row\.attention\s*\{[^}]*background:/s)
553
+ assert.match(vaultCss,
554
+ /\.vault-view-tabs \.vault-nav-row\.attention \.vault-nav-name::before\s*\{[^}]*background:\s*var\(--vault-warning\);/s)
555
+ assert.match(vaultCss,
556
+ /\.vault-view-tabs \.vault-nav-row\.attention \.vault-nav-count\s*\{[^}]*background:/s)
557
+ assert.match(vaultCss,
558
+ /\.vault-view-tabs \.vault-nav-row\.attention\.selected\s*\{[^}]*border-bottom-color:\s*var\(--task-accent\);/s)
551
559
  assert.match(vaultCss,
552
560
  /\.vault-scan-control\s*>\s*\.vault-scan-action\s*\{[^}]*border-radius:\s*7px 0 0 7px;/s)
553
561
  assert.match(vaultCss,
@@ -2551,6 +2559,60 @@ describe("Save Space interface", () => {
2551
2559
  dom.window.close()
2552
2560
  })
2553
2561
 
2562
+ test("actionable tabs highlight nonzero cleanup opportunities", async () => {
2563
+ const actionableStatus = fixture([
2564
+ item({
2565
+ path: "/pinokio/api/app/duplicate.bin",
2566
+ relative_path: "duplicate.bin",
2567
+ status: "duplicate",
2568
+ shareable: true
2569
+ }),
2570
+ item({
2571
+ path: "/pinokio/api/app/unused.bin",
2572
+ relative_path: "unused.bin",
2573
+ orphan: true
2574
+ })
2575
+ ], {
2576
+ reclaimable: 4096
2577
+ })
2578
+ const { dom } = await makePage(actionableStatus)
2579
+
2580
+ for (const view of ["duplicates", "reclaimable"]) {
2581
+ const tab = dom.window.document.querySelector(`[data-view="${view}"]`)
2582
+ const count = tab.querySelector(".vault-nav-count")
2583
+ assert.equal(count.textContent.trim(), "1")
2584
+ assert.equal(tab.classList.contains("attention"), true)
2585
+ assert.equal(count.classList.contains("attention"), true)
2586
+ }
2587
+ dom.window.close()
2588
+
2589
+ const { dom: emptyDom } = await makePage(fixture([item()]))
2590
+
2591
+ for (const view of ["duplicates", "reclaimable"]) {
2592
+ const tab = emptyDom.window.document.querySelector(`[data-view="${view}"]`)
2593
+ const count = tab.querySelector(".vault-nav-count")
2594
+ assert.equal(count.textContent.trim(), "0")
2595
+ assert.equal(tab.classList.contains("attention"), false)
2596
+ assert.equal(count.classList.contains("attention"), false)
2597
+ }
2598
+ emptyDom.window.close()
2599
+
2600
+ const appStatus = fixture([item({
2601
+ status: "duplicate",
2602
+ shareable: true
2603
+ })])
2604
+ const { dom: appDom } = await makePage(appStatus, {
2605
+ appMode: true,
2606
+ scopeId: "app:app"
2607
+ })
2608
+
2609
+ assert.equal(appDom.window.document.querySelector(
2610
+ '[data-view="duplicates"]').classList.contains("attention"), true)
2611
+ assert.equal(appDom.window.document.querySelector(
2612
+ '[data-view="reclaimable"]'), null)
2613
+ appDom.window.close()
2614
+ })
2615
+
2554
2616
  test("app setup remains visible during global scan progress", async () => {
2555
2617
  let progressRequests = 0
2556
2618
  const status = fixture([], {