web: app avatar no longer shows a prior photo behind its logo (#1025)
appGlyphInto set innerHTML/textContent but never cleared a leftover inline background-image, so switching from a photo-avatar app-thread (Eleven, wears the app icon) to a logo app-thread (Admin, ICON_WRENCH) in the reused header element left Eleven's tile painted BEHIND the wrench. Clear the background first. Pre-existing (appGlyphInto and paintHeaderAvatar are unchanged by the modularisation); Paul spotted it. Regression test added — the boot smoke can't drive an app-to-app switch.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2 files changed,
+21
−0
test/apps.test.mjs+16 −0web/static/apps.js+5 −0
diff --git a/test/apps.test.mjs b/test/apps.test.mjs| index 1f17c79..4795054 100644 |
| --- a/test/apps.test.mjs |
| +++ b/test/apps.test.mjs |
| @@ -47,6 +47,7 @@ import { |
| groupAppsApp, |
| groupAppsFor, |
| groupCapableApps, |
| + appGlyphInto, |
| parseAppMention, |
| parseHere, |
| runAppMention, |
| @@ -2109,5 +2110,20 @@ await test("vault: lock/request/grant surface in previews and the group chat pro |
| assert.equal(vaultApp.chatMessage({ type: "vault.enroll" }, { group: true }), null); |
| }); |
| +await test("appGlyphInto clears a leftover photo background before painting the mark", () => { |
| + // The header/avatar elements are reused across thread switches: paintAvatar |
| + // leaves a photo as an inline background-image (e.g. Eleven's app icon), and |
| + // switching to a logo-app (Admin) must not leave it showing behind the mark. |
| + const el = { style: { backgroundImage: "url(eleven.png)" }, classList: { add() {} }, innerHTML: "", textContent: "" }; |
| + appGlyphInto(el, { logo: "<svg><!--wrench--></svg>" }); |
| + assert.equal(el.style.backgroundImage, "", "logo path must clear the leftover background"); |
| + assert.match(el.innerHTML, /wrench/); |
| + // The glyph fallback path clears it too. |
| + const g = { style: { backgroundImage: "url(eleven.png)" }, classList: { add() {} }, innerHTML: "", textContent: "" }; |
| + appGlyphInto(g, { glyph: "🛠️" }); |
| + assert.equal(g.style.backgroundImage, "", "glyph path must clear the leftover background"); |
| + assert.equal(g.textContent, "🛠️"); |
| +}); |
| + |
| console.log(failures === 0 ? "\nALL PASSED" : `\n${failures} FAILED`); |
| process.exit(failures === 0 ? 0 : 1); |
diff --git a/web/static/apps.js b/web/static/apps.js| index 197b392..bfd34dc 100644 |
| --- a/web/static/apps.js |
| +++ b/web/static/apps.js |
| @@ -182,6 +182,11 @@ injectStyles( |
| // one, else its emoji glyph. The glyph stays the app's cross-surface fallback |
| // (native palettes and plain-string contexts can't render an SVG). |
| export function appGlyphInto(el, app) { |
| + // These avatar elements are reused across thread switches, and paintAvatar |
| + // may have left a photo behind as an inline background-image (e.g. Eleven's |
| + // app icon). innerHTML/textContent below replace the children but NOT that |
| + // style, so clear it first or the old photo shows through behind the mark. |
| + el.style.backgroundImage = ""; |
| if (app && app.logo) { |
| el.classList.add("app-logo"); |
| el.innerHTML = app.logo; // static in-code SVG, never user content |