fix : buzz feedback
CI / Verify (push) Successful in 1m17s
CI / Build immutable images and deploy (push) Successful in 1m22s

This commit is contained in:
2026-09-01 23:02:28 +07:00 Unverified
parent 69530932f2
commit 2c069bf5e9
3 changed files with 10 additions and 219 deletions
@@ -18,42 +18,11 @@ export default async function Image({
await connection();
const guide = await getCachedPublicGuide((await params).character);
if (!guide) return new Response("Not found", { status: 404 });
const recommendedWeapon = guide.weapons[0];
const recommendedArtifact = guide.artifactGroups.find(
(group) => group.rows.length > 0,
)?.rows[0];
return generateOgImage({
title: guide.name,
sub: guide.overview,
characterCard: true,
backgroundUrl: guide.character.nameCardImageUrl,
characterUrl: guide.character.splashImageUrl ?? guide.character.imageUrl,
recommendedWeapon: recommendedWeapon
? {
name: recommendedWeapon.weapon.name,
refinement: recommendedWeapon.refinement,
rarity: recommendedWeapon.weapon.rarity,
imageUrl: recommendedWeapon.weapon.imageUrl,
overallPercent: recommendedWeapon.overallPercent,
personalPercent: recommendedWeapon.personalPercent,
}
: null,
recommendedArtifact: recommendedArtifact
? {
first: {
name: recommendedArtifact.first.name,
rarity: recommendedArtifact.first.rarity,
imageUrl: recommendedArtifact.first.imageUrl,
},
second: recommendedArtifact.second
? {
name: recommendedArtifact.second.name,
rarity: recommendedArtifact.second.rarity,
imageUrl: recommendedArtifact.second.imageUrl,
}
: null,
percent: recommendedArtifact.percent,
}
: null,
});
}
-31
View File
@@ -18,42 +18,11 @@ export default async function Image({
await connection();
const guide = await getCachedPublicGuide((await params).character);
if (!guide) return new Response("Not found", { status: 404 });
const recommendedWeapon = guide.weapons[0];
const recommendedArtifact = guide.artifactGroups.find(
(group) => group.rows.length > 0,
)?.rows[0];
return generateOgImage({
title: guide.name,
sub: guide.overview,
characterCard: true,
backgroundUrl: guide.character.nameCardImageUrl,
characterUrl: guide.character.splashImageUrl ?? guide.character.imageUrl,
recommendedWeapon: recommendedWeapon
? {
name: recommendedWeapon.weapon.name,
refinement: recommendedWeapon.refinement,
rarity: recommendedWeapon.weapon.rarity,
imageUrl: recommendedWeapon.weapon.imageUrl,
overallPercent: recommendedWeapon.overallPercent,
personalPercent: recommendedWeapon.personalPercent,
}
: null,
recommendedArtifact: recommendedArtifact
? {
first: {
name: recommendedArtifact.first.name,
rarity: recommendedArtifact.first.rarity,
imageUrl: recommendedArtifact.first.imageUrl,
},
second: recommendedArtifact.second
? {
name: recommendedArtifact.second.name,
rarity: recommendedArtifact.second.rarity,
imageUrl: recommendedArtifact.second.imageUrl,
}
: null,
percent: recommendedArtifact.percent,
}
: null,
});
}
+10 -157
View File
@@ -6,9 +6,6 @@ import { join } from "node:path";
import { ImageResponse } from "next/og";
import sharp from "sharp";
import { rarityGradient } from "@/lib/catalog/rarity";
import { artifactTierCount } from "@/lib/guides/artifacts";
export const ogImageSize = { width: 1222, height: 560 } as const;
export const ogImageContentType = "image/png";
@@ -24,12 +21,6 @@ function shorten(value: string, length: number): string {
: normalized;
}
function metric(value: string): string {
return Number(value) === 0
? "-%"
: `${Number(value).toLocaleString("en-US", { maximumFractionDigits: 2 })}%`;
}
function getBannerData(): Promise<string> {
bannerDataPromise ??= readFile(
join(process.cwd(), "public/web_banner.png"),
@@ -71,35 +62,12 @@ export async function generateOgImage({
characterCard = false,
backgroundUrl,
characterUrl,
recommendedWeapon,
recommendedArtifact,
}: {
title: string;
sub: string;
characterCard?: boolean;
backgroundUrl?: string | null;
characterUrl?: string | null;
recommendedWeapon?: {
name: string;
refinement: number;
rarity: number | null;
imageUrl: string;
overallPercent: string | null;
personalPercent: string | null;
} | null;
recommendedArtifact?: {
first: {
name: string;
rarity: number | null;
imageUrl: string;
};
second: {
name: string;
rarity: number | null;
imageUrl: string;
} | null;
percent: string;
} | null;
}) {
const [
bannerSrc,
@@ -107,22 +75,13 @@ export async function generateOgImage({
characterFont,
characterBackgroundSrc,
characterArtSrc,
weaponArtSrc,
firstArtifactArtSrc,
secondArtifactArtSrc,
] =
await Promise.all([
characterCard ? null : getBannerData(),
getFontData(),
characterCard ? getCharacterFontData() : null,
characterCard && backgroundUrl ? getImageData(backgroundUrl) : null,
characterCard && characterUrl ? getImageData(characterUrl) : null,
recommendedWeapon ? getImageData(recommendedWeapon.imageUrl) : null,
recommendedArtifact ? getImageData(recommendedArtifact.first.imageUrl) : null,
recommendedArtifact?.second
? getImageData(recommendedArtifact.second.imageUrl)
: null,
]);
] = await Promise.all([
characterCard ? null : getBannerData(),
getFontData(),
characterCard ? getCharacterFontData() : null,
characterCard && backgroundUrl ? getImageData(backgroundUrl) : null,
characterCard && characterUrl ? getImageData(characterUrl) : null,
]);
const backgroundSrc = characterCard ? characterBackgroundSrc : bannerSrc;
return new ImageResponse(
@@ -172,16 +131,13 @@ export async function generateOgImage({
<div
style={{
position: "absolute",
top: characterCard
? recommendedWeapon || recommendedArtifact
? "36px"
: "52px"
: "160px",
left: "60px",
display: "flex",
flexDirection: "column",
right: characterCard ? "560px" : "450px",
bottom: recommendedWeapon || recommendedArtifact ? "28px" : "100px",
...(characterCard
? { top: 0, bottom: 0, justifyContent: "center" }
: { top: "160px", bottom: "100px" }),
}}
>
{characterCard ? (
@@ -202,109 +158,6 @@ export async function generateOgImage({
>
{shorten(sub, 92)}
</div>
{recommendedWeapon ? (
<div
style={{
display: "flex",
width: "500px",
height: "72px",
marginTop: "14px",
overflow: "hidden",
borderRadius: "12px",
background: "#fff",
}}
>
<div
style={{
display: "flex",
flex: recommendedWeapon.overallPercent || recommendedWeapon.personalPercent ? 3 : 1,
alignItems: "center",
gap: "10px",
overflow: "hidden",
borderBottomRightRadius: "48px",
background: rarityGradient(recommendedWeapon.rarity),
color: "#fff",
padding: "2px 28px 2px 6px",
}}
>
{weaponArtSrc ? (
<img
src={weaponArtSrc}
alt=""
width={68}
height={68}
style={{ width: "68px", height: "68px", objectFit: "contain" }}
/>
) : null}
<div style={{ display: "flex", fontSize: 20 }}>
{shorten(recommendedWeapon.name, 19)} R{recommendedWeapon.refinement}
</div>
</div>
{recommendedWeapon.overallPercent || recommendedWeapon.personalPercent ? (
<div
style={{
display: "flex",
flex: 1,
alignItems: "center",
justifyContent: "space-around",
color: "#17171c",
textShadow: "none",
padding: "6px",
}}
>
{recommendedWeapon.overallPercent ? (
<div style={{ display: "flex", flexDirection: "column", alignItems: "center" }}>
<div style={{ display: "flex", fontSize: 22, fontWeight: 700 }}>{metric(recommendedWeapon.overallPercent)}</div>
<div style={{ display: "flex", fontSize: 8 }}>ทั้งทีม</div>
</div>
) : null}
{recommendedWeapon.personalPercent ? (
<div style={{ display: "flex", flexDirection: "column", alignItems: "center" }}>
<div style={{ display: "flex", fontSize: 22, fontWeight: 700 }}>{metric(recommendedWeapon.personalPercent)}</div>
<div style={{ display: "flex", fontSize: 8 }}>ส่วนตัว</div>
</div>
) : null}
</div>
) : null}
</div>
) : null}
{recommendedArtifact ? (
<div
style={{
display: "flex",
width: "500px",
height: "72px",
marginTop: recommendedWeapon ? "10px" : "14px",
alignItems: "center",
gap: "10px",
overflow: "hidden",
border: "1px solid #ffffff1f",
borderRadius: "12px",
background: "#34343d",
padding: "2px 8px",
}}
>
<div style={{ display: "flex", width: recommendedArtifact.second ? "108px" : "68px" }}>
{firstArtifactArtSrc ? (
<div style={{ display: "flex", width: "68px", height: "68px", overflow: "hidden", border: "1px solid #ffffff2a", borderRadius: "10px", background: rarityGradient(recommendedArtifact.first.rarity) }}>
<img src={firstArtifactArtSrc} alt="" width={68} height={68} style={{ objectFit: "contain" }} />
</div>
) : null}
{recommendedArtifact.second && secondArtifactArtSrc ? (
<div style={{ display: "flex", width: "68px", height: "68px", overflow: "hidden", marginLeft: "-28px", border: "1px solid #ffffff2a", borderRadius: "10px", background: rarityGradient(recommendedArtifact.second.rarity) }}>
<img src={secondArtifactArtSrc} alt="" width={68} height={68} style={{ objectFit: "contain" }} />
</div>
) : null}
</div>
<div style={{ display: "flex", flex: 1, fontSize: 18 }}>
{artifactTierCount(recommendedArtifact.second?.name ?? null)} {shorten(recommendedArtifact.first.name, recommendedArtifact.second ? 12 : 21)}
{recommendedArtifact.second ? ` + ${shorten(recommendedArtifact.second.name, 12)}` : ""}
</div>
<div style={{ display: "flex", color: "#f0b72f", fontSize: 22 }}>
{metric(recommendedArtifact.percent)}
</div>
</div>
) : null}
</div>
</div>,
{