feat : fix anything you can think
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
/* eslint-disable @next/next/no-img-element */
|
||||
import { join } from "node:path";
|
||||
|
||||
import { ImageResponse } from "next/og";
|
||||
import sharp from "sharp";
|
||||
|
||||
@@ -53,6 +55,14 @@ async function pngDataUrl(url: string): Promise<string | null> {
|
||||
}
|
||||
}
|
||||
|
||||
async function localPngDataUrl(fileName: string): Promise<string> {
|
||||
const png = await sharp(join(process.cwd(), "public", "icon", fileName))
|
||||
.resize(96, 96, { fit: "contain" })
|
||||
.png()
|
||||
.toBuffer();
|
||||
return `data:image/png;base64,${png.toString("base64")}`;
|
||||
}
|
||||
|
||||
export async function GET(
|
||||
_request: Request,
|
||||
context: RouteContext<"/api/guides/[guideId]/artifacts/image">,
|
||||
@@ -61,7 +71,7 @@ export async function GET(
|
||||
const guide = await getPublicArtifactExport(guideId);
|
||||
if (!guide) return new Response("Guide not found", { status: 404 });
|
||||
|
||||
const [fonts, groupAssets] = await Promise.all([
|
||||
const [fonts, groupAssets, statIconAssets] = await Promise.all([
|
||||
anuphanFonts,
|
||||
Promise.all(
|
||||
guide.groups.map((group) =>
|
||||
@@ -73,6 +83,11 @@ export async function GET(
|
||||
),
|
||||
),
|
||||
),
|
||||
Promise.all([
|
||||
localPngDataUrl("Icon_Sands_of_Eon.webp"),
|
||||
localPngDataUrl("Icon_Goblet_of_Eonothem.webp"),
|
||||
localPngDataUrl("Icon_Circlet_of_Logos.webp"),
|
||||
]),
|
||||
]);
|
||||
|
||||
const profile = guide.profile;
|
||||
@@ -81,7 +96,7 @@ export async function GET(
|
||||
? 60 + profile.targets.length * 30 + (profile.talents.length ? 58 : 0) + (profile.note ? Math.max(48, Math.ceil(profile.note.length / 58) * 24) : 0)
|
||||
: 0;
|
||||
const rightHeight = profile
|
||||
? 194 + (profile.substats.length ? 76 : 0) + (detailHeight > 60 ? detailHeight + 16 : 0) + 32
|
||||
? 242 + (profile.substats.length ? 76 : 0) + (detailHeight > 60 ? detailHeight + 16 : 0) + 32
|
||||
: 0;
|
||||
const height = Math.max(520, 190 + Math.max(leftHeight, rightHeight));
|
||||
const talents = normalizeTalentPriorities(profile?.talents ?? []);
|
||||
@@ -162,11 +177,12 @@ export async function GET(
|
||||
</div>
|
||||
<div style={{ display: "flex" }}>
|
||||
{[
|
||||
{ label: "Sands", values: profile.sands },
|
||||
{ label: "Goblet", values: profile.goblet },
|
||||
{ label: "Circlet", values: profile.circlet },
|
||||
{ label: "Sands", values: profile.sands, icon: statIconAssets[0] },
|
||||
{ label: "Goblet", values: profile.goblet, icon: statIconAssets[1] },
|
||||
{ label: "Circlet", values: profile.circlet, icon: statIconAssets[2] },
|
||||
].map((stat) => (
|
||||
<div key={stat.label} style={{ display: "flex", flex: 1, minHeight: 128, flexDirection: "column", alignItems: "center", gap: 10, borderRight: stat.label === "Circlet" ? "none" : "1px solid #34343d", padding: "16px 10px", textAlign: "center" }}>
|
||||
<div key={stat.label} style={{ display: "flex", flex: 1, minHeight: 176, flexDirection: "column", alignItems: "center", gap: 10, borderRight: stat.label === "Circlet" ? "none" : "1px solid #34343d", padding: "16px 10px", textAlign: "center" }}>
|
||||
<img src={stat.icon} alt="" width={48} height={48} style={{ objectFit: "contain" }} />
|
||||
<div style={{ display: "flex", fontSize: 17, fontWeight: 700 }}>{stat.label}</div>
|
||||
<div style={{ display: "flex", width: "100%", borderTop: "1px solid #34343d" }} />
|
||||
<div style={{ display: "flex", fontSize: 14, lineHeight: 1.45 }}>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -18,6 +18,7 @@ import { SiteHeader } from "@/components/public/site-header";
|
||||
import { ElementIcon } from "@/components/public/element-icon";
|
||||
import { LiveRefresh } from "@/components/live-refresh";
|
||||
import { rarityGradientClass, rarityTextClass } from "@/lib/catalog/rarity";
|
||||
import { isCustomTravelerKey } from "@/lib/catalog/travelers";
|
||||
import type { PublicGuide } from "@/lib/guides/queries";
|
||||
import { normalizeTalentPriorities, talentRelationSymbol } from "@/lib/guides/talents";
|
||||
import { cn } from "@/lib/utils";
|
||||
@@ -517,12 +518,12 @@ function TeamSection({ guide }: { guide: PublicGuide }) {
|
||||
{team.members.map((member) => (
|
||||
<div key={member.position} className="min-w-0 text-center">
|
||||
<div className="grid grid-cols-[minmax(0,1fr)_2.75rem] gap-2">
|
||||
<div className="relative aspect-square overflow-hidden rounded-lg bg-muted">
|
||||
<div className={cn("relative aspect-square overflow-hidden rounded-lg bg-muted", rarityGradientClass(isCustomTravelerKey(member.character.key) ? 5 : member.character.rarity))}>
|
||||
<Image
|
||||
src={member.character.imageUrl}
|
||||
alt={member.character.name}
|
||||
fill
|
||||
className="object-cover"
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
|
||||
+1
-1
@@ -173,7 +173,7 @@ export async function processCatalogSyncJob(jobId: string): Promise<void> {
|
||||
key: traveler.key,
|
||||
name: traveler.name,
|
||||
element: traveler.element,
|
||||
rarity: 5,
|
||||
rarity: traveler.rarity,
|
||||
weaponType: "WEAPON_SWORD_ONE_HAND",
|
||||
imageKey: `catalog/${version}/characters/${traveler.asset}`,
|
||||
elementIconKey: `catalog/${version}/elements/${traveler.element.toLowerCase()}.png`,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { readFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { customTravelers, isUpstreamTraveler } from "@/lib/catalog/travelers";
|
||||
import { customTravelers, isCustomTravelerKey, isUpstreamTraveler } from "@/lib/catalog/travelers";
|
||||
|
||||
describe("custom Traveler catalog", () => {
|
||||
it("blacklists every upstream Traveler spelling variant", () => {
|
||||
@@ -24,4 +24,10 @@ describe("custom Traveler catalog", () => {
|
||||
}));
|
||||
expect(new Set(hashes).size).toBe(7);
|
||||
});
|
||||
|
||||
it("marks every custom Traveler as a five-star character", () => {
|
||||
expect(customTravelers.every((traveler) => traveler.rarity === 5)).toBe(true);
|
||||
expect(isCustomTravelerKey("traveler_hydro")).toBe(true);
|
||||
expect(isCustomTravelerKey("traveler_unknown")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
export const customTravelers = [
|
||||
{ key: "traveler_pyro", name: "Traveler Pyro", element: "Pyro", detailKey: "10000005_PYRO", asset: "traveler_pyro.webp" },
|
||||
{ key: "traveler_hydro", name: "Traveler Hydro", element: "Hydro", detailKey: "10000005_HYDRO", asset: "traveler_hydro.webp" },
|
||||
{ key: "traveler_anemo", name: "Traveler Anemo", element: "Anemo", detailKey: "10000005_ANEMO", asset: "traveler_anemo.webp" },
|
||||
{ key: "traveler_geo", name: "Traveler Geo", element: "Geo", detailKey: "10000005_GEO", asset: "traveler_geo.webp" },
|
||||
{ key: "traveler_electro", name: "Traveler Electro", element: "Electro", detailKey: "10000005_ELECTRO", asset: "traveler_electro.webp" },
|
||||
{ key: "traveler_dendro", name: "Traveler Dendro", element: "Dendro", detailKey: "10000005_DENDRO", asset: "traveler_dendro.webp" },
|
||||
{ key: "traveler_cryo", name: "Traveler Cryo", element: "Cryo", detailKey: "10000005_CRYO", asset: "traveler_cyro.webp" },
|
||||
{ key: "traveler_pyro", name: "Traveler Pyro", element: "Pyro", rarity: 5, detailKey: "10000005_PYRO", asset: "traveler_pyro.webp" },
|
||||
{ key: "traveler_hydro", name: "Traveler Hydro", element: "Hydro", rarity: 5, detailKey: "10000005_HYDRO", asset: "traveler_hydro.webp" },
|
||||
{ key: "traveler_anemo", name: "Traveler Anemo", element: "Anemo", rarity: 5, detailKey: "10000005_ANEMO", asset: "traveler_anemo.webp" },
|
||||
{ key: "traveler_geo", name: "Traveler Geo", element: "Geo", rarity: 5, detailKey: "10000005_GEO", asset: "traveler_geo.webp" },
|
||||
{ key: "traveler_electro", name: "Traveler Electro", element: "Electro", rarity: 5, detailKey: "10000005_ELECTRO", asset: "traveler_electro.webp" },
|
||||
{ key: "traveler_dendro", name: "Traveler Dendro", element: "Dendro", rarity: 5, detailKey: "10000005_DENDRO", asset: "traveler_dendro.webp" },
|
||||
{ key: "traveler_cryo", name: "Traveler Cryo", element: "Cryo", rarity: 5, detailKey: "10000005_CRYO", asset: "traveler_cyro.webp" },
|
||||
] as const;
|
||||
|
||||
export function isCustomTravelerKey(key: string): boolean {
|
||||
return customTravelers.some((traveler) => traveler.key === key);
|
||||
}
|
||||
|
||||
export function isUpstreamTraveler(name: string): boolean {
|
||||
return name.trim().toLocaleLowerCase("en-US") === "traveler";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user