f45fccde9c90

Vault Health: hide platform scrollbar to match other popup surfaces, add shimmering skeleton (queue head + 3 rows) while GET_ADVANCED_HEALTH_REPORT computes; bump v2.11.9

time
2026-04-29 01:29 UTC · 6 months ago
machine
koh2
parent
7c1bd58db8ea
5 files manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/popup/styles/popup.css+102 −0src/popup/views/Health.tsx+42 −4
manifest.firefox.json+1 −1
@@ -1,7 +1,7 @@11 {22   "manifest_version": 3,33   "name": "Peach",4-  "version": "2.11.8",4+  "version": "2.11.9",55   "description": "Zero-knowledge password manager",66   "browser_specific_settings": {77     "gecko": {

view file

manifest.json+1 −1
@@ -1,7 +1,7 @@11 {22   "manifest_version": 3,33   "name": "Peach",4-  "version": "2.11.8",4+  "version": "2.11.9",55   "description": "Zero-knowledge password manager",66   "action": {77     "default_popup": "src/popup/index.html",

view file

package.json+1 −1
@@ -1,6 +1,6 @@11 {22   "name": "peach",3-  "version": "2.11.8",3+  "version": "2.11.9",44   "description": "Zero-knowledge password manager",55   "type": "module",66   "scripts": {

view file

src/popup/styles/popup.css+102 −0
@@ -4183,6 +4183,108 @@41834183   overflow-y: auto;41844184   display: flex;41854185   flex-direction: column;4186+  /* Hide the platform scrollbar — every other scrollable surface in the4187+   * popup hides it; the Health view was the one outlier and it ruined the4188+   * polish. Behavior unchanged, just no rail rendered. */4189+  scrollbar-width: none;4190+  -ms-overflow-style: none;4191+}4192+.peach-health-body::-webkit-scrollbar { display: none; }4193+4194+/* Loading skeleton for the remediation queue — shown while the background4195+ * computes the advanced health report on Pro vaults. Shimmer + pulse so the4196+ * view doesn't read as "empty/broken" during the (sometimes multi-second)4197+ * scan. */4198+.peach-health-skeleton {4199+  display: flex;4200+  flex-direction: column;4201+  gap: 10px;4202+  padding: 14px 16px;4203+}4204+4205+.peach-health-skeleton-head {4206+  display: flex;4207+  align-items: center;4208+  justify-content: space-between;4209+  margin-bottom: 4px;4210+}4211+4212+.peach-health-skeleton-title {4213+  font-family: "IBM Plex Mono", monospace;4214+  font-size: 9px;4215+  letter-spacing: 0.16em;4216+  text-transform: uppercase;4217+  color: var(--peach-accent);4218+  opacity: 0.85;4219+}4220+4221+.peach-health-skeleton-meta {4222+  font-family: "IBM Plex Mono", monospace;4223+  font-size: 10px;4224+  color: var(--peach-t2);4225+  display: inline-flex;4226+  align-items: center;4227+  gap: 6px;4228+}4229+4230+.peach-health-skeleton-dot {4231+  width: 6px;4232+  height: 6px;4233+  border-radius: 50%;4234+  background: var(--peach-accent);4235+  animation: peach-health-skeleton-pulse 1.2s ease-in-out infinite;4236+}4237+4238+.peach-health-skeleton-row {4239+  display: flex;4240+  align-items: center;4241+  gap: 12px;4242+  padding: 12px;4243+  border-radius: 10px;4244+  background: rgba(0, 0, 0, 0.18);4245+  animation: peach-health-skeleton-pulse 1.6s ease-in-out infinite;4246+}4247+4248+.peach-health-skeleton-row:nth-child(2) { animation-delay: 0.10s; }4249+.peach-health-skeleton-row:nth-child(3) { animation-delay: 0.20s; }4250+.peach-health-skeleton-row:nth-child(4) { animation-delay: 0.30s; }4251+4252+.peach-health-skeleton-bar {4253+  height: 10px;4254+  border-radius: 4px;4255+  background: linear-gradient(4256+    90deg,4257+    rgba(240, 232, 216, 0.04) 0%,4258+    rgba(240, 232, 216, 0.10) 50%,4259+    rgba(240, 232, 216, 0.04) 100%4260+  );4261+  background-size: 240% 100%;4262+  animation: peach-health-skeleton-shimmer 1.6s ease-in-out infinite;4263+}4264+4265+.peach-health-skeleton-bar--name { width: 55%; }4266+.peach-health-skeleton-bar--meta { width: 40%; height: 7px; opacity: 0.6; margin-top: 6px; }4267+.peach-health-skeleton-bar--cta { width: 90px; height: 26px; border-radius: 6px; flex-shrink: 0; margin-left: auto; }4268+4269+.peach-health-skeleton-row > div:first-child {4270+  flex: 1;4271+  min-width: 0;4272+}4273+4274+@keyframes peach-health-skeleton-pulse {4275+  0%, 100% { opacity: 0.55; }4276+  50%      { opacity: 1; }4277+}4278+4279+@keyframes peach-health-skeleton-shimmer {4280+  0%   { background-position: 200% 50%; }4281+  100% { background-position: -100% 50%; }4282+}4283+4284+[data-motion="reduce"] .peach-health-skeleton-row,4285+[data-motion="reduce"] .peach-health-skeleton-bar,4286+[data-motion="reduce"] .peach-health-skeleton-dot {4287+  animation: none;41864288 }41874289 41884290 .peach-health-summary {

view file

src/popup/views/Health.tsx+42 −4
@@ -66,13 +66,19 @@6666   });6767   const [viewingReusedGroup, setViewingReusedGroup] = createSignal<VaultEntry[] | null>(null);6868   const [advancedReport, setAdvancedReport] = createSignal<AdvancedHealthReport | null>(null);69+  const [advancedLoading, setAdvancedLoading] = createSignal<boolean>(false);6970   const [smartChangingId, setSmartChangingId] = createSignal<string | null>(null);7071 7172   onMount(async () => {7273     if (!props.isPro) return;73-    const res = await sendMessage({ type: "GET_ADVANCED_HEALTH_REPORT" });74-    if ((res as { ok: boolean }).ok) {75-      setAdvancedReport((res as { ok: boolean; data?: AdvancedHealthReport }).data ?? null);74+    setAdvancedLoading(true);75+    try {76+      const res = await sendMessage({ type: "GET_ADVANCED_HEALTH_REPORT" });77+      if ((res as { ok: boolean }).ok) {78+        setAdvancedReport((res as { ok: boolean; data?: AdvancedHealthReport }).data ?? null);79+      }80+    } finally {81+      setAdvancedLoading(false);7682     }7783   });7884 @@ -203,7 +209,39 @@203209         </div>204210 205211         <div class="peach-health-body">206-          <Show when={props.isPro && advancedReport() && advancedReport()!.remediation.length > 0}>212+          <Show when={props.isPro && advancedLoading()}>213+            <div class="peach-health-skeleton" aria-busy="true" aria-live="polite">214+              <div class="peach-health-skeleton-head">215+                <span class="peach-health-skeleton-title">Remediation queue</span>216+                <span class="peach-health-skeleton-meta">217+                  <span class="peach-health-skeleton-dot" aria-hidden="true"></span>218+                  Scanning vault…219+                </span>220+              </div>221+              <div class="peach-health-skeleton-row">222+                <div>223+                  <div class="peach-health-skeleton-bar peach-health-skeleton-bar--name"></div>224+                  <div class="peach-health-skeleton-bar peach-health-skeleton-bar--meta"></div>225+                </div>226+                <div class="peach-health-skeleton-bar peach-health-skeleton-bar--cta"></div>227+              </div>228+              <div class="peach-health-skeleton-row">229+                <div>230+                  <div class="peach-health-skeleton-bar peach-health-skeleton-bar--name"></div>231+                  <div class="peach-health-skeleton-bar peach-health-skeleton-bar--meta"></div>232+                </div>233+                <div class="peach-health-skeleton-bar peach-health-skeleton-bar--cta"></div>234+              </div>235+              <div class="peach-health-skeleton-row">236+                <div>237+                  <div class="peach-health-skeleton-bar peach-health-skeleton-bar--name"></div>238+                  <div class="peach-health-skeleton-bar peach-health-skeleton-bar--meta"></div>239+                </div>240+                <div class="peach-health-skeleton-bar peach-health-skeleton-bar--cta"></div>241+              </div>242+            </div>243+          </Show>244+          <Show when={props.isPro && !advancedLoading() && advancedReport() && advancedReport()!.remediation.length > 0}>207245             <div class="peach-health-pro-queue">208246               <div class="peach-health-pro-queue-head">209247                 <span class="peach-health-pro-queue-title">Remediation queue</span>

view file