feat: massive layout resemblence improvement
This commit is contained in:
@@ -5,10 +5,12 @@ import { version } from "../package.json";
|
||||
import { pm } from "./lib/auth";
|
||||
import { createLogger } from "./lib/logger";
|
||||
import { v1routes } from "./v1";
|
||||
import { cors } from '@elysia/cors'
|
||||
|
||||
const log = createLogger("APP");
|
||||
|
||||
const app = new Elysia()
|
||||
.use(cors())
|
||||
.use(
|
||||
openapi({
|
||||
exclude: { paths: ["/", "/health"] },
|
||||
|
||||
+45
-37
@@ -29,6 +29,7 @@ import { assets, batchStore, getAvatar } from "@/lib/assets";
|
||||
import { Deduplicator } from "@/lib/dedupe";
|
||||
import { cardErrorLocs } from "@/lib/locs";
|
||||
import { createLogger } from "@/lib/logger";
|
||||
import { resolveShowcaseChar } from "@/lib/matcher";
|
||||
import { mockData } from "@/lib/mock";
|
||||
import { buildDisplayRows } from "@/lib/rows";
|
||||
import type { RollSummary } from "@/lib/substat";
|
||||
@@ -36,9 +37,9 @@ import { buildRollSummary } from "@/lib/substat";
|
||||
import { Locale, SubstatOrder } from "@/type/enka";
|
||||
import { FightPropMapEnum, PercentageFightProp } from "@/type/fightPropMap";
|
||||
|
||||
import DiffPage from "../../hmr.html";
|
||||
import { ErrorPage } from "./error";
|
||||
import { ListPage } from "./list";
|
||||
import { resolveShowcaseChar } from "@/lib/matcher";
|
||||
|
||||
const cache = new TTLCache();
|
||||
const deduper = new Deduplicator();
|
||||
@@ -51,6 +52,7 @@ queueMicrotask(() => {
|
||||
if (Date.now() - purging < 60000) return;
|
||||
purging = Date.now();
|
||||
assets.getMissingAssets();
|
||||
if (!feature("API")) batchStore("locs", "affixes");
|
||||
|
||||
setInterval(async () => {
|
||||
if (Date.now() - purging < 60000) return;
|
||||
@@ -70,6 +72,7 @@ export function card(app: App) {
|
||||
.derive(() => ({
|
||||
log: createLogger(),
|
||||
}))
|
||||
.get("/genshin/diff", DiffPage)
|
||||
.head(
|
||||
"/genshin/:uid/preload",
|
||||
async ({ params: { uid }, log }) => {
|
||||
@@ -464,7 +467,7 @@ export function card(app: App) {
|
||||
|
||||
// #region Rendering
|
||||
const res = new ImageResponse(
|
||||
<div tw={`text-[${32.25 * (height / 1024)}px]`}>
|
||||
<div tw={`text-[${30.466 * (height / 1024)}px]`}>
|
||||
<SubstatDisplay
|
||||
substat={rollSummary}
|
||||
element={avatar.Element}
|
||||
@@ -479,34 +482,38 @@ export function card(app: App) {
|
||||
tw="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
{/* avatar */}
|
||||
<img
|
||||
src="/avatar"
|
||||
alt="char"
|
||||
tw="object-cover h-full aspect-[1024/1124] absolute top-0 -left-[2.0em] scale-x-[1.1] scale-y-[1.15]"
|
||||
/>
|
||||
|
||||
{/* left section */}
|
||||
<div
|
||||
tw="absolute top-0 bottom-0 left-0 w-[27.4em] p-[1.4em] flex flex-col"
|
||||
tw="absolute top-0 bottom-0 left-0 w-[30.4em] p-[1.6em] flex flex-col"
|
||||
style={{
|
||||
textShadow:
|
||||
"0 .08em .1em black,0 .1em .3em rgba(0,0,0,.4)",
|
||||
}}
|
||||
>
|
||||
{/* avatar */}
|
||||
<img
|
||||
src="/avatar"
|
||||
alt="char"
|
||||
tw="absolute h-full aspect-[1024/1124] left-[50%] top-[55%] max-w-none"
|
||||
style={{
|
||||
transform: "translate(-50%, -50%) scale(1.3)",
|
||||
transformOrigin: "50% 50%",
|
||||
}}
|
||||
/>
|
||||
{/* left side vignette */}
|
||||
<div tw="absolute top-0 left-0 bottom-0 right-0">
|
||||
<img src="shade" alt="shade" tw="opacity-40" />
|
||||
</div>
|
||||
{/* char and username */}
|
||||
<div tw="text-[130%] pb-[.5%] flex items-center gap-[.5em]">
|
||||
<div tw="text-[120%] pb-[.5%] flex items-center gap-[.5em]">
|
||||
{char.avatar.name}
|
||||
<div tw="text-[0.75em] opacity-80 flex items-center gap-[.4em]">
|
||||
<div tw="text-[0.75em] opacity-80 flex items-center gap-[.7em] pl-[.4em] pt-[.5em]">
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 100 100"
|
||||
tw="mt-[.2em] h-[.5em] w-[.5em]"
|
||||
tw="mt-[.2em] h-[.7em] w-[.7em]"
|
||||
>
|
||||
<title>doritos</title>
|
||||
<polygon
|
||||
@@ -518,12 +525,12 @@ export function card(app: App) {
|
||||
</div>
|
||||
</div>
|
||||
{/* level */}
|
||||
<div>
|
||||
Lv. <span tw="px-1">{charLvl}</span> /
|
||||
<span tw="opacity-60 pl-1">{charLvlMax}</span>
|
||||
<div tw="text-[105%] pt-[.2em]">
|
||||
Lv. <span tw="px-[.3em]">{charLvl}</span> /
|
||||
<span tw="opacity-60 pl-[.3em]">{charLvlMax}</span>
|
||||
</div>
|
||||
{/* fren */}
|
||||
<div tw="flex gap-1 items-end">
|
||||
<div tw="text-[110%] flex gap-[.3em] items-end pt-[.3em]">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
tw="h-[1.2em] mb-[.2em]"
|
||||
@@ -572,7 +579,7 @@ export function card(app: App) {
|
||||
style={{
|
||||
display: "flex",
|
||||
position: "absolute",
|
||||
right: "5%",
|
||||
right: "4.1%",
|
||||
bottom: "6%",
|
||||
flexDirection: "column",
|
||||
alignItems: "flex-end",
|
||||
@@ -595,7 +602,7 @@ export function card(app: App) {
|
||||
return (
|
||||
<div tw="mt-[.6em] relative">
|
||||
<TalentSvg
|
||||
tw={`Talents ${avatar.Element} w-[3.6em] opacity-60 text-black`}
|
||||
tw={`Talents ${avatar.Element} w-[3.6em] opacity-60 text-black mb-[.5em] -mt-[.25em]`}
|
||||
/>
|
||||
<img
|
||||
key={key}
|
||||
@@ -614,12 +621,12 @@ export function card(app: App) {
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div tw="mb-[-1em] ml-[-.3em] mt-[.3em] text-[.9em] opacity-80">
|
||||
<div tw="mb-[-1em] ml-[-.3em] mt-[.3em] text-[.96em] opacity-80">
|
||||
{data.uid}
|
||||
</div>
|
||||
</div>
|
||||
{/* middle section */}
|
||||
<div tw="absolute top-0 bottom-0 right-[23.1em] w-[25.5em] p-[1.4em_.7em_.7em] flex flex-col">
|
||||
<div tw="absolute top-0 bottom-0 right-[24.2em] w-[25.5em] p-[1.5em_.7em_.7em] flex flex-col text-[1.01em]">
|
||||
{/* weapon */}
|
||||
<div tw="flex flex-start" className="Weapon">
|
||||
{/* display */}
|
||||
@@ -637,9 +644,9 @@ export function card(app: App) {
|
||||
}[weapon.store.Rarity],
|
||||
}}
|
||||
/>
|
||||
<div tw="absolute w-full top-[93%] flex justify-center">
|
||||
<div tw="absolute w-full top-[91%] flex justify-center">
|
||||
{Array.from({ length: weapon.store.Rarity }, () =>
|
||||
star(".7em"),
|
||||
star(".8em"),
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -657,7 +664,7 @@ export function card(app: App) {
|
||||
</span>
|
||||
</div>
|
||||
{/* stats */}
|
||||
<div tw="pt-[3%] pb-[2%] flex gap-[.5em]">
|
||||
<div tw="pt-[3%] pb-[2%] flex gap-[.7em]">
|
||||
{weapon.flat.weaponStats?.map((s) => (
|
||||
<div tw="rounded-[.2em] leading-none bg-white/15 px-[.34em] py-[0.25em] flex items-center gap-[.7em] text-[1.03em]">
|
||||
<EnkaIcon
|
||||
@@ -677,7 +684,7 @@ export function card(app: App) {
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div tw="flex gap-[.5em]">
|
||||
<div tw="flex gap-[.85em]">
|
||||
<div tw="rounded-[.2em] leading-none bg-black/50 px-[.34em] py-[0.175em] flex items-center gap-[.7em] text-[1.03em] text-[wheat]">
|
||||
<div tw="pt-[.1em]">
|
||||
R
|
||||
@@ -688,7 +695,8 @@ export function card(app: App) {
|
||||
</div>
|
||||
<div tw="rounded-[.2em] leading-none bg-black/50 px-[.34em] py-[0.175em] flex items-center gap-[.7em] text-[1.03em]">
|
||||
<div tw="pt-[.1em]">
|
||||
Lv. {weapon.data.level} /
|
||||
<span tw="pr-[.5em]">Lv.</span>{" "}
|
||||
{weapon.data.level} /
|
||||
<span tw="opacity-60 pl-1">
|
||||
{
|
||||
promoteMaxLevelMap[
|
||||
@@ -702,7 +710,7 @@ export function card(app: App) {
|
||||
</div>
|
||||
</div>
|
||||
{/* stats table */}
|
||||
<div tw="text-white flex flex-col pt-[2em] pr-[3%] grow text-[.95em] justify-between">
|
||||
<div tw="text-white flex flex-col pt-[2.2em] pr-[4.7%] -mb-[.8em] grow text-[.96em] justify-between">
|
||||
{/* <div /> justify spacer */}
|
||||
{statRows.map((r, i) => (
|
||||
<div tw="flex items-center whitespace-nowrap">
|
||||
@@ -721,7 +729,7 @@ export function card(app: App) {
|
||||
>
|
||||
)[r.key] || (r.key as keyof typeof library)
|
||||
}
|
||||
tw="h-[1em] mr-[.8em] -mt-[.05em]"
|
||||
tw="h-[1em] mr-[1em] -mt-[.05em]"
|
||||
/>
|
||||
<div tw="flex grow justify-between">
|
||||
<div>{r.name}</div>
|
||||
@@ -745,7 +753,7 @@ export function card(app: App) {
|
||||
))}
|
||||
{artifactSets.length && (
|
||||
<div
|
||||
tw="flex w-full pt-[.5em] gap-[.5em]"
|
||||
tw="flex w-full gap-[.5em]"
|
||||
style={{
|
||||
textShadow:
|
||||
"0 .08em .1em rgba(0,0,0,.4),0 .1em .3em rgba(0,0,0,.4)",
|
||||
@@ -771,23 +779,23 @@ export function card(app: App) {
|
||||
</div>
|
||||
</div>
|
||||
{/* right section */}
|
||||
<div tw="absolute top-0 bottom-0 right-0 w-[23.1em] p-[.7em] flex flex-col justify-between">
|
||||
<div tw="absolute top-0 bottom-0 right-0 w-[25.7em] p-[.7em] flex flex-col justify-between">
|
||||
{artifacts.map((a) =>
|
||||
a ? (
|
||||
<div tw="h-[18%] bg-[#0000004f] rounded-[.3em] relative text-white overflow-hidden flex justify-between gap-[.5em]">
|
||||
<img
|
||||
src={a.icon}
|
||||
alt="artifact"
|
||||
tw="absolute top-1/2 -left-[8%] w-[45%] -translate-y-1/2 -translate-x-[10%] object-contain aspect-square scale-90"
|
||||
tw="absolute top-[48%] -left-[8%] w-[45%] -translate-y-1/2 -translate-x-[10%] object-contain aspect-square scale-90"
|
||||
/>
|
||||
{/* main */}
|
||||
<div tw="h-full w-[30%] text-right shrink-0 flex flex-col justify-center mr-[.25em]">
|
||||
<div tw="h-full w-[30%] text-[105%] text-right shrink-0 flex flex-col justify-center mr-[.25em]">
|
||||
<EnkaIcon
|
||||
icon={a.main.mainPropId}
|
||||
tw="h-[1em] w-[1em] ml-auto max-h-[22%]"
|
||||
tw="h-[1em] w-[1em] ml-auto max-h-[22%] mt-[.5em]"
|
||||
/>
|
||||
<div
|
||||
tw="text-[1.1em] py-[2%] ml-auto"
|
||||
tw="text-[1.1em] pt-[2%] ml-auto"
|
||||
style={{ textShadow: "0 .07em .1em black" }}
|
||||
>
|
||||
{PercentageFightProp.includes(
|
||||
@@ -797,7 +805,7 @@ export function card(app: App) {
|
||||
? `${a.main.statValue.toFixed(1)}%`
|
||||
: a.main.statValue.toLocaleString()}
|
||||
</div>
|
||||
<div tw="flex grow max-h-[20%] items-end justify-end">
|
||||
<div tw="flex grow max-h-[20%] items-center justify-end">
|
||||
<div>
|
||||
{Array.from({ length: a.stars ?? 0 }, () =>
|
||||
star(".6em"),
|
||||
@@ -816,7 +824,7 @@ export function card(app: App) {
|
||||
<div tw="flex flex-col flex-wrap mt-[.35em] mb-[.35em] mr-[.35em] w-full">
|
||||
{a.sub.map((s, i) => (
|
||||
<div
|
||||
tw={`flex items-center pl-[.25em] relative gap-[.25em] w-1/2 h-1/2 ${i === 3 ? "opacity-80" : ""}`}
|
||||
tw={`flex items-center pl-[.7em] relative gap-[.5em] w-1/2 h-1/2 ${i === 3 ? "opacity-80" : ""} ${i > 1 ? "pl-[.3em]" : ""}`}
|
||||
>
|
||||
{enableRollQuality && (
|
||||
<div tw="absolute top-0 m-[.1em] left-[-.25em] bottom-0 right-[.25em] bg-gradient-to-t from-transparent to-black/25 rounded-[.2em] flex gap-0.5">
|
||||
@@ -840,9 +848,9 @@ export function card(app: App) {
|
||||
)}
|
||||
<EnkaIcon
|
||||
icon={s.appendPropId}
|
||||
tw="h-[1em] w-[1em]"
|
||||
tw="h-[1.1em] w-[1.1em]"
|
||||
/>
|
||||
<div>
|
||||
<div tw="text-[1.05em]">
|
||||
+
|
||||
{PercentageFightProp.includes(
|
||||
s.appendPropId as (typeof PercentageFightProp)[number],
|
||||
|
||||
Reference in New Issue
Block a user