diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1b4411d..2de40f3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@ Diese Liste beschreibt in einfachen Worten, was sich von Version zu Version an T
### Behoben
+- Favoriten: Bei Logos mit durchsichtigem Hintergrund schien der graue Platzhalter-Buchstabe durch. Er verschwindet jetzt, sobald das Logo geladen ist.
- Linux-App: Links, die in einem neuen Fenster aufgehen (zum Beispiel aus Notizen oder Favoriten), übergibt die App jetzt an das Öffnen-Programm Ihres Systems statt an eine mitgebrachte Kopie. Auf manchen Systemen öffnete sich dadurch vorher kein Browser.
## 1.10.0 – 2026-10-06
diff --git a/apps/web/src/components/dashboard/widgets/favorites-widget.test.tsx b/apps/web/src/components/dashboard/widgets/favorites-widget.test.tsx
index 5393306..71119ad 100644
--- a/apps/web/src/components/dashboard/widgets/favorites-widget.test.tsx
+++ b/apps/web/src/components/dashboard/widgets/favorites-widget.test.tsx
@@ -414,6 +414,25 @@ describe('FavoritesWidget', () => {
expect(screen.getByTestId('letter-fallback-fav-id-1')).toHaveTextContent('G');
});
+ it('06.10.: geladenes Logo blendet den Buchstaben aus (durchsichtige Logos), Fehler zeigt ihn wieder', async () => {
+ render();
+ await waitFor(() => {
+ expect(screen.getByText('GitHub')).toBeInTheDocument();
+ });
+ const proxyImg = screen.getByTestId('icon-proxy-fav-id-1') as HTMLImageElement;
+ expect(screen.getByTestId('letter-fallback-fav-id-1')).toBeInTheDocument();
+
+ act(() => {
+ fireEvent.load(proxyImg);
+ });
+ expect(screen.queryByTestId('letter-fallback-fav-id-1')).not.toBeInTheDocument();
+
+ act(() => {
+ fireEvent.error(proxyImg);
+ });
+ expect(screen.getByTestId('letter-fallback-fav-id-1')).toHaveTextContent('G');
+ });
+
it('ausdrueckliche Symbol-Adresse (260929-lh3): Proxy -> Browser laedt iconUrl direkt (no-referrer) -> Origin-Favicon -> Buchstabe', async () => {
mockFetch.mockResolvedValue([
{
diff --git a/apps/web/src/components/dashboard/widgets/favorites-widget.tsx b/apps/web/src/components/dashboard/widgets/favorites-widget.tsx
index a14d5f5..3d3a1ee 100644
--- a/apps/web/src/components/dashboard/widgets/favorites-widget.tsx
+++ b/apps/web/src/components/dashboard/widgets/favorites-widget.tsx
@@ -543,7 +543,9 @@ function getDirectCandidates(iconUrl: string | null, url: string): string[] {
* Browser aber das Bild), dann `{origin}/favicon.ico` (entfaellt, wenn
* identisch). Erreicht auch interne Hosts, die der SSRF-Schutz des
* Servers absichtlich ablehnt.
- * 3. `none` — der Buchstaben-Platzhalter liegt IMMER darunter.
+ * 3. `none` — der Buchstaben-Platzhalter steht da, solange kein Bild
+ * geladen ist. Sobald ein Bild geladen ist, verschwindet er: bei Logos mit
+ * durchsichtigem Hintergrund schien er sonst durch (06.10.2026).
*
* Bewusst KEIN Drittanbieter-Favicon-Dienst: der wuerde Hostnamen nach
* aussen geben und interne Hosts ohnehin nicht kennen.
@@ -590,15 +592,18 @@ function FavoriteIcon({
// 'proxy' | Index in directSrcs | Ende der Kette (Buchstabe).
const [stage, setStage] = useState<'proxy' | number>(proxySrc ? 'proxy' : 0);
const directSrc = typeof stage === 'number' ? (directSrcs[stage] ?? null) : null;
+ const [imageLoaded, setImageLoaded] = useState(false);
return (
-
- {getFallbackLetter(fav.title)}
-
+ {!imageLoaded && (
+
+ {getFallbackLetter(fav.title)}
+
+ )}
{stage === 'proxy' && proxySrc && (
![]()
setStage(0)}
+ onLoad={() => setImageLoaded(true)}
+ onError={() => {
+ setImageLoaded(false);
+ setStage(0);
+ }}
/>
)}
{directSrc && (
@@ -623,7 +632,11 @@ function FavoriteIcon({
loading="lazy"
referrerPolicy="no-referrer"
className={`relative rounded-sm ${img}`}
- onError={() => setStage((prev) => (typeof prev === 'number' ? prev + 1 : 0))}
+ onLoad={() => setImageLoaded(true)}
+ onError={() => {
+ setImageLoaded(false);
+ setStage((prev) => (typeof prev === 'number' ? prev + 1 : 0));
+ }}
/>
)}