This commit is contained in:
onysd 2026-09-01 17:40:03 +03:00
parent 7cd1f64d0d
commit 62d8b53030
6 changed files with 51 additions and 6 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -23,8 +23,8 @@
})(); })();
</script> </script>
<script type="module" crossorigin src="/assets/index-BTi0-ijr.js"></script> <script type="module" crossorigin src="/assets/index-D-ZS9t5z.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-0MvM-hpw.css"> <link rel="stylesheet" crossorigin href="/assets/index-Bkel0mh-.css">
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

View file

@ -93,7 +93,50 @@ export function Shell({
link.rel = "icon"; link.rel = "icon";
document.head.appendChild(link); document.head.appendChild(link);
} }
link.href = identity?.iconExt && !brandIconFailed ? api.serverIconURL() : "/logo.png"; const linkEl = link;
const src = identity?.iconExt && !brandIconFailed ? api.serverIconURL() : "/logo.png";
// Browsers render the favicon file as-is -- they don't apply the
// sidebar's CSS border-radius to it, so a square-cornered source image
// (the default logo, or whatever shape an operator's uploaded icon
// happens to be) shows up square in the tab strip. Bake the circular
// mask into the actual pixels instead, the same way an app icon export
// would, so the tab matches the round mark everywhere else in the UI.
let cancelled = false;
const img = new Image();
img.crossOrigin = "anonymous";
img.onload = () => {
if (cancelled) {
return;
}
const size = 64;
const canvas = document.createElement("canvas");
canvas.width = size;
canvas.height = size;
const ctx = canvas.getContext("2d");
if (!ctx) {
linkEl.href = src;
return;
}
ctx.save();
ctx.beginPath();
ctx.arc(size / 2, size / 2, size / 2, 0, Math.PI * 2);
ctx.closePath();
ctx.clip();
ctx.drawImage(img, 0, 0, size, size);
ctx.restore();
linkEl.href = canvas.toDataURL("image/png");
};
img.onerror = () => {
// Cross-origin or load failure: fall back to the raw image rather
// than leaving the tab with no favicon at all.
if (!cancelled) {
linkEl.href = src;
}
};
img.src = src;
return () => {
cancelled = true;
};
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [identity?.iconExt, brandIconFailed]); }, [identity?.iconExt, brandIconFailed]);
// Third-party verification is additionally hidden by default (not fully // Third-party verification is additionally hidden by default (not fully

View file

@ -231,6 +231,8 @@ a {
display: grid; display: grid;
width: 34px; width: 34px;
height: 34px; height: 34px;
border-radius: 50%;
overflow: hidden;
place-items: center; place-items: center;
} }
@ -238,7 +240,7 @@ a {
display: block; display: block;
width: 100%; width: 100%;
height: 100%; height: 100%;
object-fit: contain; object-fit: cover;
} }
.brand strong { .brand strong {