UPDATE: New cdn, assets
All checks were successful
Gitea Auto Deploy / Deploy-Container (push) Successful in 1m21s

This commit is contained in:
2026-02-17 22:26:15 +07:00
parent cf5eaaa3d4
commit 8fbb27b5c1
69 changed files with 487057 additions and 1131 deletions

View File

@@ -93,7 +93,7 @@ export default function ActionBar() {
};
const actionMove = (path: string) => {
const actionMove = (path: string) => {
router.push(`/${path}`)
}
@@ -134,7 +134,7 @@ export default function ActionBar() {
}
}
}
const modalConfigs: ModalConfig[] = [
{
id: "update_profile_modal",
@@ -193,16 +193,16 @@ export default function ActionBar() {
// Handle ESC key to close modal
useEffect(() => {
for (const item of modalConfigs) {
if (!item?.isOpen) {
handleCloseModal(item?.id || "")
}
if (!item?.isOpen) {
handleCloseModal(item?.id || "")
}
}
const handleEscKey = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
for (const item of modalConfigs) {
handleCloseModal(item?.id || "")
}
if (event.key === 'Escape') {
for (const item of modalConfigs) {
handleCloseModal(item?.id || "")
}
}
};
window.addEventListener('keydown', handleEscKey);
@@ -223,6 +223,8 @@ export default function ActionBar() {
<Image
src={`/icon/${avatarSelected.damageType.toLowerCase()}.webp`}
alt={'fire'}
unoptimized
crossOrigin="anonymous"
className="h-10 w-10 object-contain"
width={100}
height={100}

View File

@@ -8,13 +8,13 @@ import { useTranslations } from "next-intl"
export default function AvatarBar({ onClose }: { onClose?: () => void }) {
const {
listAvatar,
setAvatarSelected,
setSkillSelected,
setFilter,
filter,
listElement,
const {
listAvatar,
setAvatarSelected,
setSkillSelected,
setFilter,
filter,
listElement,
listPath,
setListElement,
setListPath
@@ -22,10 +22,10 @@ export default function AvatarBar({ onClose }: { onClose?: () => void }) {
const transI18n = useTranslations("DataPage")
const { locale } = useLocaleStore()
useEffect(() => {
setFilter({ ...filter, locale: locale, element: Object.keys(listElement).filter((key) => listElement[key]), path: Object.keys(listPath).filter((key) => listPath[key]) })
// eslint-disable-next-line react-hooks/exhaustive-deps
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [locale, listElement, listPath])
@@ -54,8 +54,11 @@ export default function AvatarBar({ onClose }: { onClose?: () => void }) {
style={{
backgroundColor: listElement[key] ? "#374151" : "#6B7280"
}}>
<Image src={ `/icon/${key}.webp`}
<Image
src={`/icon/${key}.webp`}
alt={key}
unoptimized
crossOrigin="anonymous"
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md"
width={200}
height={200} />
@@ -76,7 +79,10 @@ export default function AvatarBar({ onClose }: { onClose?: () => void }) {
}}
>
<Image src={`/icon/${key}.webp`}
<Image
src={`/icon/${key}.webp`}
unoptimized
crossOrigin="anonymous"
alt={key}
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md"
width={200}
@@ -91,7 +97,7 @@ export default function AvatarBar({ onClose }: { onClose?: () => void }) {
<ul className="grid grid-cols-3 sm:grid-cols-2 lg:grid-cols-3 gap-2 w-full h-[65vh] overflow-y-scroll overflow-x-hidden">
{listAvatar.map((item, index) => (
<div key={index} onClick={() => {
setAvatarSelected(item);
setAvatarSelected(item);
setSkillSelected(null)
if (onClose) onClose()
}}>

View File

@@ -423,10 +423,12 @@ export default function AvatarInfo() {
<div className="lg:col-span-1">
<div className="">
<Image
unoptimized
crossOrigin="anonymous"
width={904}
height={1260}
priority
src={`https://api.hakush.in/hsr/UI/lightconemaxfigures/${lightconeDetail.id}.webp`}
src={`${process.env.CDN_URL}/${lightconeDetail.image}`}
className="w-full h-full rounded-lg object-cover shadow-lg"
alt="Lightcone"
/>

View File

@@ -29,19 +29,22 @@ export default function CharacterCard({ data }: CharacterCardProps) {
}`}
>
<div className="relative w-full h-full">
<div className="relative w-full h-32 lg:h-26 xl:h-36">
<Image
width={376}
height={512}
src={`https://api.hakush.in/hsr/UI/avatarshopicon/${data.id}.webp`}
unoptimized
crossOrigin="anonymous"
src={`${process.env.CDN_URL}/${data.icon}`}
priority={true}
className="w-full h-full rounded-md object-cover"
className="rounded-md w-full h-full object-contain"
alt="ALT"
/>
<Image
width={32}
height={32}
unoptimized
crossOrigin="anonymous"
src={`/icon/${data.damageType.toLowerCase()}.webp`}
className="absolute top-0 left-0 w-6 h-6 rounded-full"
alt={data.damageType.toLowerCase()}
@@ -49,6 +52,8 @@ export default function CharacterCard({ data }: CharacterCardProps) {
<Image
width={32}
height={32}
unoptimized
crossOrigin="anonymous"
src={`/icon/${data.baseType.toLowerCase()}.webp`}
className="absolute top-0 right-0 w-6 h-6 rounded-full"
alt={data.baseType.toLowerCase()}

View File

@@ -29,15 +29,19 @@ export default function CharacterInfoCard({ character, selectedCharacters, onCha
<div className="relative mb-4">
<div className="w-full h-48 rounded-lg overflow-hidden relative">
<Image
src={`https://api.hakush.in/hsr/UI/avatarshopicon/${character.avatar_id}.webp`}
src={`${process.env.CDN_URL}/spriteoutput/avatarshopicon/avatar/${character.avatar_id}.png`}
alt={mapAvatarInfo[character.avatar_id.toString()]?.Name || ""}
width={376}
height={512}
unoptimized
crossOrigin="anonymous"
className="w-full h-full object-contain"
/>
<Image
width={48}
height={48}
unoptimized
crossOrigin="anonymous"
src={`/icon/${mapAvatarInfo[character.avatar_id.toString()]?.DamageType.toLowerCase()}.webp`}
className="absolute top-0 left-0 w-10 h-10 rounded-full"
alt={mapAvatarInfo[character.avatar_id.toString()]?.DamageType.toLowerCase()}
@@ -45,6 +49,8 @@ export default function CharacterInfoCard({ character, selectedCharacters, onCha
<Image
width={48}
height={48}
unoptimized
crossOrigin="anonymous"
src={`/icon/${mapAvatarInfo[character.avatar_id.toString()]?.BaseType.toLowerCase()}.webp`}
className="absolute top-0 right-0 w-10 h-10 rounded-full"
alt={mapAvatarInfo[character.avatar_id.toString()]?.BaseType.toLowerCase()}
@@ -71,8 +77,10 @@ export default function CharacterInfoCard({ character, selectedCharacters, onCha
<div key={index} className="relative">
<div className="w-9 h-9 rounded-lg flex items-center justify-center border border-amber-500/50">
<Image
src={`https://api.hakush.in/hsr/UI/relicfigures/IconRelic_${relic.relic_set_id}_${relic.relic_id.toString()[relic.relic_id.toString().length - 1]}.webp`}
src={`${process.env.CDN_URL}/spriteoutput/relicfigures/IconRelic_${relic.relic_set_id}_${relic.relic_id.toString()[relic.relic_id.toString().length - 1]}.png`}
alt="Relic"
unoptimized
crossOrigin="anonymous"
width={124}
height={124}
className="w-14 h-14 object-contain"
@@ -91,7 +99,9 @@ export default function CharacterInfoCard({ character, selectedCharacters, onCha
<div className="">
<div className="rounded-lg h-42 flex items-center justify-center">
<Image
src={`https://api.hakush.in/hsr/UI/lightconemediumicon/${character.lightcone.item_id}.webp`}
unoptimized
crossOrigin="anonymous"
src={`${process.env.CDN_URL}/spriteoutput/lightconemaxfigures/${character.lightcone.item_id}.png`}
alt={mapLightconeInfo[character.lightcone.item_id.toString()]?.Name}
width={348}
height={408}

View File

@@ -30,7 +30,8 @@ export default function LightconeCard({ data }: LightconeCardProps) {
<div className="relative w-full h-full">
<Image
loading="lazy"
src={`https://api.hakush.in/hsr/UI/lightconemediumicon/${data.id}.webp`}
src={`${process.env.CDN_URL}/${data.thumbnail}`}
unoptimized={true}
width={348}
height={408}
className="w-full h-full rounded-md object-cover"

View File

@@ -27,7 +27,7 @@ export default function ProfileCard({ profile, selectedProfile, onProfileToggle
<div className="">
<div className="rounded-lg h-42 flex items-center justify-center">
<Image
src={`https://api.hakush.in/hsr/UI/lightconemediumicon/${profile.lightcone.item_id}.webp`}
src={`${process.env.CDN_URL}/spriteoutput/lightconemaxfigures/${profile.lightcone.item_id}.png`}
alt={mapLightconeInfo[profile.lightcone.item_id.toString()]?.Name}
width={348}
height={408}
@@ -55,7 +55,7 @@ export default function ProfileCard({ profile, selectedProfile, onProfileToggle
<div key={index} className="relative">
<div className="w-9 h-9 rounded-lg flex items-center justify-center border border-amber-500/50">
<Image
src={`https://api.hakush.in/hsr/UI/relicfigures/IconRelic_${relic.relic_set_id}_${relic.relic_id.toString()[relic.relic_id.toString().length - 1]}.webp`}
src={`${process.env.CDN_URL}/spriteoutput/relicfigures/IconRelic_${relic.relic_set_id}_${relic.relic_id.toString()[relic.relic_id.toString().length - 1]}.webp`}
alt="Relic"
width={124}
height={124}

View File

@@ -128,8 +128,9 @@ export default function RelicCard({ slot, avatarId }: RelicCardProps) {
>
<span>
<Image
src={`https://api.hakush.in/hsr/UI/relicfigures/IconRelic_${relicDetail.relic_set_id}_${slot}.webp`}
src={`${process.env.CDN_URL}/spriteoutput/relicfigures/IconRelic_${relicDetail.relic_set_id}_${slot}.png`}
alt="Relic"
unoptimized={true}
width={124}
height={124}
className="w-14 h-14 object-contain"

View File

@@ -47,7 +47,7 @@ export default function EidolonsInfo() {
>
<Image
className={`w-60 object-contain mb-2 ${Number(key) <= avatars[avatarSelected?.id || ""]?.data?.rank ? "" : "grayscale"}`}
src={`https://api.hakush.in/hsr/UI/rank/_dependencies/textures/${avatarSelected?.id}/${avatarSelected?.id}_Rank_${key}.webp`}
src={`${process.env.CDN_URL}/ui/ui3d/rank/_dependencies/textures/${avatarSelected?.id}/${avatarSelected?.id}_Rank_${key}.png`}
alt={`Rank ${key}`}
priority
width={240}

View File

@@ -344,7 +344,15 @@ export default function Header() {
<a className="hidden sm:grid sm:grid-cols-1 items-start justify-items-center text-left gap-0 hover:scale-105 px-2">
<div className="flex items-center justify-center gap-2">
<Image src="/ff-srtool.png" alt="Logo" width={250} height={250} className="w-10 h-10 xl:w-12 xl:h-12 object-contain" />
<Image
unoptimized
crossOrigin="anonymous"
src="/ff-srtool.png"
alt="Logo"
width={250}
height={250}
className="w-10 h-10 xl:w-12 xl:h-12 object-contain"
/>
<div className="flex flex-col justify-center items-start">
<h1 className="text-lg xl:text-xl font-bold leading-tight">
<span className="text-emerald-500">Firefly Sr</span>

View File

@@ -154,6 +154,8 @@ export default function CopyImport() {
backgroundColor: listPath[key] ? "#374151" : "#6B7280"
}}>
<Image
unoptimized
crossOrigin="anonymous"
src={`/icon/${key}.webp`}
alt={key}
className="h-8 w-8 object-contain rounded-md"
@@ -179,6 +181,8 @@ export default function CopyImport() {
backgroundColor: listElement[key] ? "#374151" : "#6B7280"
}}>
<Image
unoptimized
crossOrigin="anonymous"
src={`/icon/${key}.webp`}
alt={key}
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md"
@@ -220,7 +224,7 @@ export default function CopyImport() {
customSet={listCopyAvatar.map((avatar) => ({
value: avatar.id.toString(),
label: getNameChar(locale, transI18n, avatar),
imageUrl: `https://api.hakush.in/hsr/UI/avatarshopicon/${avatar.id}.webp`
imageUrl: `${process.env.CDN_URL}/spriteoutput/avatarshopicon/avatar/${avatar.id}.png`
}))}
excludeSet={[]}
selectedCustomSet={avatarCopySelected?.id.toString() || ""}

View File

@@ -12,15 +12,15 @@ import { useTranslations } from "next-intl";
export default function LightconeBar() {
const { locale } = useLocaleStore()
const {
listLightcone,
filter,
setFilter,
defaultFilter,
listPath,
listRank,
setListPath,
setListRank
const {
listLightcone,
filter,
setFilter,
defaultFilter,
listPath,
listRank,
setListPath,
setListRank
} = useLightconeStore()
const { setAvatar, avatars } = useUserDataStore()
const { avatarSelected } = useAvatarStore()
@@ -86,6 +86,8 @@ export default function LightconeBar() {
}}
>
<Image
unoptimized
crossOrigin="anonymous"
src={`/icon/${key}.webp`}
alt={key}
className="h-7 w-7 md:h-8 md:w-8 object-contain rounded-md"

View File

@@ -10,11 +10,10 @@ import Image from "next/image";
import { MonsterStore } from "@/types";
import useMazeStore from "@/stores/mazeStore";
import { useTranslations } from "next-intl";
import { listCurrentLanguageApi } from "@/constant/constant";
export default function AsBar() {
const { ASEvent, mapASInfo } = useEventStore()
const { listMonster } = useMonsterStore()
const { mapMonster } = useMonsterStore()
const { locale } = useLocaleStore()
const {
as_config,
@@ -151,7 +150,7 @@ export default function AsBar() {
<div className="rounded-xl p-4 mb-2 border border-warning">
<div className="mb-4 w-full">
<SelectCustomText
customSet={ASEvent.filter(as => as.lang.get(listCurrentLanguageApi[locale])).map((as) => ({
customSet={ASEvent.map((as) => ({
id: as.id,
name: getLocaleName(locale, as),
time: `${as.begin} - ${as.end}`,
@@ -259,8 +258,10 @@ export default function AsBar() {
>
<div className="flex items-center space-x-3">
<div className="relative w-20 h-20 rounded-full overflow-hidden shrink-0 border border-white/10 shadow-sm">
{listMonster.find((monster) => monster.child.includes(waveValue))?.icon && <Image
src={`https://api.hakush.in/hsr/UI/monstermiddleicon/${listMonster.find((monster) => monster.child.includes(waveValue))?.icon?.split("/")?.pop()?.replace(".png", "")}.webp`}
{mapMonster?.[waveValue.toString()]?.icon && <Image
unoptimized
crossOrigin="anonymous"
src={`${process.env.CDN_URL}/${mapMonster?.[waveValue.toString()]?.icon}`}
alt="Enemy Icon"
width={376}
height={512}
@@ -271,18 +272,18 @@ export default function AsBar() {
<div className="flex flex-col">
<div className="text-sm font-semibold">Lv. {challengeSelected?.EventIDList1[0].Level}</div>
<div className="flex items-center space-x-1 mt-1">
{listMonster
.find((monster) => monster.child.includes(waveValue))
?.weak?.map((icon, iconIndex) => (
<Image
src={`/icon/${icon.toLowerCase()}.webp`}
alt={icon}
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md border border-white/20 shadow-sm"
width={200}
height={200}
key={iconIndex}
/>
))}
{mapMonster?.[waveValue.toString()]?.weak?.map((icon, iconIndex) => (
<Image
unoptimized
crossOrigin="anonymous"
src={`/icon/${icon.toLowerCase()}.webp`}
alt={icon}
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md border border-white/20 shadow-sm"
width={200}
height={200}
key={iconIndex}
/>
))}
</div>
</div>
</div>
@@ -309,8 +310,10 @@ export default function AsBar() {
<div className="flex items-center space-x-3">
<div className="relative w-20 h-20 rounded-full overflow-hidden shrink-0 border border-white/10 shadow-sm">
{listMonster.find((monster) => monster.child.includes(waveValue))?.icon && <Image
src={`https://api.hakush.in/hsr/UI/monstermiddleicon/${listMonster.find((monster) => monster.child.includes(waveValue))?.icon?.split("/")?.pop()?.replace(".png", "")}.webp`}
{mapMonster?.[waveValue.toString()]?.icon && <Image
unoptimized
crossOrigin="anonymous"
src={`${process.env.CDN_URL}/${mapMonster?.[waveValue.toString()]?.icon}`}
alt="Enemy Icon"
width={376}
height={512}
@@ -321,18 +324,18 @@ export default function AsBar() {
<div className="flex flex-col">
<div className="text-sm font-semibold">Lv. {challengeSelected?.EventIDList1[0].Level}</div>
<div className="flex items-center space-x-1 mt-1">
{listMonster
.find((monster) => monster.child.includes(waveValue))
?.weak?.map((icon, iconIndex) => (
<Image
src={`/icon/${icon.toLowerCase()}.webp`}
alt={icon}
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md border border-white/20 shadow-sm"
width={200}
height={200}
key={iconIndex}
/>
))}
{mapMonster?.[waveValue.toString()].weak?.map((icon, iconIndex) => (
<Image
unoptimized
crossOrigin="anonymous"
src={`/icon/${icon.toLowerCase()}.webp`}
alt={icon}
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md border border-white/20 shadow-sm"
width={200}
height={200}
key={iconIndex}
/>
))}
</div>
</div>
</div>

View File

@@ -17,7 +17,6 @@ import { getLocaleName } from "@/helper";
import Image from "next/image";
import { MonsterBasic } from "@/types";
import { useTranslations } from "next-intl";
import { listCurrentLanguageApi } from "@/constant/constant";
import useGlobalStore from "@/stores/globalStore";
@@ -26,7 +25,7 @@ export default function CeBar() {
const [showSearchWaveId, setShowSearchWaveId] = useState<number | null>(null);
const { Stage } = useMazeStore()
const { ce_config, setCeConfig } = useUserDataStore()
const { mapMonsterInfo } = useMonsterStore()
const { listMonster, mapMonster } = useMonsterStore()
const { locale } = useLocaleStore()
const transI18n = useTranslations("DataPage")
const [showSearchStage, setShowSearchStage] = useState(false)
@@ -39,33 +38,9 @@ export default function CeBar() {
const pageSizeMonsters = 30
const [monsterPage, setMonsterPage] = useState(1)
const listMonsterDetail = useMemo(() => {
const result: MonsterBasic[] = []
for (const monster of Object.values(mapMonsterInfo)) {
for (const monsterChild of monster.Child) {
result.push({
id: monsterChild.Id.toString(),
rank: monster.Rank,
camp: null,
icon: monster.ImagePath,
weak: monsterChild.StanceWeakList,
desc: monster.Desc,
child: [],
lang: new Map<string, string>([
[listCurrentLanguageApi[locale], monster.Name],
]),
})
}
}
return result
}, [mapMonsterInfo, locale])
const filteredMonsters = useMemo(() => {
const newlistMonster = new Set<MonsterBasic>()
for (const monster of listMonsterDetail) {
for (const monster of listMonster) {
if (getLocaleName(locale, monster).toLowerCase().includes(searchTerm.toLowerCase())) {
newlistMonster.add(monster)
}
@@ -74,7 +49,7 @@ export default function CeBar() {
}
}
return Array.from(newlistMonster)
}, [listMonsterDetail, locale, searchTerm]);
}, [listMonster, locale, searchTerm]);
const paginatedMonsters = useMemo(() =>
filteredMonsters.slice((monsterPage - 1) * pageSizeMonsters, monsterPage * pageSizeMonsters),
@@ -114,12 +89,12 @@ export default function CeBar() {
useEffect(() => {
if (!ce_config) return
if (!extraData || !extraData.theory_craft?.mode) return
const newExtraData = structuredClone(extraData)
if (!newExtraData?.theory_craft?.hp) {
newExtraData.theory_craft!.hp = {}
}
for (let i = 0; i < ce_config.monsters.length; i++) {
const waveKey = (i + 1).toString()
if (!newExtraData.theory_craft!.hp[waveKey]) {
@@ -132,9 +107,9 @@ export default function CeBar() {
}
}
setExtraData(newExtraData)
// eslint-disable-next-line react-hooks/exhaustive-deps
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ce_config])
return (
<div className="z-4 py-8 h-full w-full" onClick={() => {
@@ -337,8 +312,10 @@ export default function CeBar() {
</button>
<div className="flex justify-center">
{listMonsterDetail.find((monster2) => monster2.id === member.monster_id.toString())?.icon && <Image
src={`https://api.hakush.in/hsr/UI/monstermiddleicon/${listMonsterDetail.find((monster2) => monster2.id === member.monster_id.toString())?.icon?.split("/")?.pop()?.replace(".png", "")}.webp`}
{mapMonster?.[member.monster_id.toString()]?.icon && <Image
unoptimized
crossOrigin="anonymous"
src={`${process.env.CDN_URL}/${mapMonster?.[member.monster_id.toString()]?.icon}`}
alt="Enemy Icon"
width={376}
height={512}
@@ -347,22 +324,22 @@ export default function CeBar() {
</div>
<div className="flex flex-wrap justify-center gap-1 mb-2">
{listMonsterDetail
.find((monster) => monster.id === member.monster_id.toString())
?.weak?.map((icon, iconIndex) => (
<Image
src={`/icon/${icon.toLowerCase()}.webp`}
alt={icon}
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md border border-white/20 shadow-sm"
width={200}
height={200}
key={iconIndex}
/>
))}
{mapMonster?.[member.monster_id.toString()]?.weak?.map((icon, iconIndex) => (
<Image
unoptimized
crossOrigin="anonymous"
src={`/icon/${icon.toLowerCase()}.webp`}
alt={icon}
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md border border-white/20 shadow-sm"
width={200}
height={200}
key={iconIndex}
/>
))}
</div>
<div className="text-center flex flex-col items-center justify-center">
<div className="text-sm font-medium">
{getLocaleName(locale, listMonsterDetail.find((monster) => monster.id === member.monster_id.toString()))} {`(${member.monster_id})`}
{getLocaleName(locale, mapMonster?.[member.monster_id.toString()])} {`(${member.monster_id})`}
</div>
<div className="flex items-center gap-1 mt-1 mx-2">
<span className="text-sm">LV.</span>
@@ -393,15 +370,15 @@ export default function CeBar() {
onChange={(e) => {
const val = Number(e.target.value)
if (isNaN(val) || val < 0) return
const newData = structuredClone(extraData)
if (!newData?.theory_craft?.hp?.[(waveIndex + 1).toString()]) {
newData.theory_craft!.hp![(waveIndex + 1).toString()] = []
newData.theory_craft!.hp![(waveIndex + 1).toString()] = []
}
newData.theory_craft!.hp![(waveIndex + 1).toString()][memberIndex] = val
setExtraData(newData)
}}
/>
@@ -415,8 +392,8 @@ export default function CeBar() {
))}
{/* Add Member Button + Search */}
<div
className="relative flex items-start justify-center w-full h-full"
<div
className="relative flex items-start justify-center w-full h-full"
onClick={(e) => e.stopPropagation()}
>
<button
@@ -481,9 +458,11 @@ export default function CeBar() {
}}
>
<div className="relative w-8 h-8 rounded-full overflow-hidden shrink-0 border border-white/10 shadow-sm">
{listMonsterDetail.find((monster2) => monster2.id === monster.id)?.icon?.split("/")?.pop()?.replace(".png", "") && (
{mapMonster?.[monster.id.toString()]?.icon && (
<Image
src={`https://api.hakush.in/hsr/UI/monstermiddleicon/${listMonsterDetail.find((monster2) => monster2.id === monster.id)?.icon?.split("/")?.pop()?.replace(".png", "")}.webp`}
unoptimized
crossOrigin="anonymous"
src={`${process.env.CDN_URL}/${mapMonster?.[monster.id.toString()]?.icon}`}
alt="Enemy Icon"
width={376}
height={512}

View File

@@ -64,6 +64,8 @@ export default function MonsterBar() {
}
>
<Image
unoptimized
crossOrigin="anonymous"
src={`/icon/${item.icon}.webp`}
alt={item.name}
width={24}

View File

@@ -14,7 +14,7 @@ import { MonsterStore } from "@/types";
export default function MocBar() {
const { MOCEvent, mapMOCInfo } = useEventStore()
const { listMonster } = useMonsterStore()
const { mapMonster } = useMonsterStore()
const { locale } = useLocaleStore()
const {
moc_config,
@@ -239,8 +239,10 @@ export default function MocBar() {
>
<div className="flex items-center space-x-3">
<div className="relative w-20 h-20 rounded-full overflow-hidden shrink-0 border border-white/10 shadow-sm">
{listMonster.find((monster) => monster.child.includes(waveValue))?.icon && <Image
src={`https://api.hakush.in/hsr/UI/monstermiddleicon/${listMonster.find((monster) => monster.child.includes(waveValue))?.icon?.split("/")?.pop()?.replace(".png", "")}.webp`}
{mapMonster?.[waveValue.toString()]?.icon && <Image
unoptimized
crossOrigin="anonymous"
src={`${process.env.CDN_URL}/${mapMonster?.[waveValue.toString()]?.icon}`}
alt="Enemy Icon"
width={376}
height={512}
@@ -251,18 +253,18 @@ export default function MocBar() {
<div className="flex flex-col">
<div className="text-sm font-semibold">Lv. {challengeSelected?.EventIDList1[0].Level}</div>
<div className="flex items-center space-x-1 mt-1">
{listMonster
.find((monster) => monster.child.includes(waveValue))
?.weak?.map((icon, iconIndex) => (
<Image
src={`/icon/${icon.toLowerCase()}.webp`}
alt={icon}
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md border border-white/20 shadow-sm"
width={200}
height={200}
key={iconIndex}
/>
))}
{mapMonster?.[waveValue.toString()]?.weak?.map((icon, iconIndex) => (
<Image
unoptimized
crossOrigin="anonymous"
src={`/icon/${icon.toLowerCase()}.webp`}
alt={icon}
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md border border-white/20 shadow-sm"
width={200}
height={200}
key={iconIndex}
/>
))}
</div>
</div>
</div>
@@ -289,8 +291,10 @@ export default function MocBar() {
<div className="flex items-center space-x-3">
<div className="relative w-20 h-20 rounded-full overflow-hidden shrink-0 border border-white/10 shadow-sm">
{listMonster.find((monster) => monster.child.includes(waveValue))?.icon && <Image
src={`https://api.hakush.in/hsr/UI/monstermiddleicon/${listMonster.find((monster) => monster.child.includes(waveValue))?.icon?.split("/")?.pop()?.replace(".png", "")}.webp`}
{mapMonster?.[waveValue.toString()]?.icon && <Image
unoptimized
crossOrigin="anonymous"
src={`${process.env.CDN_URL}/${mapMonster?.[waveValue.toString()]?.icon}`}
alt="Enemy Icon"
width={376}
height={512}
@@ -301,18 +305,18 @@ export default function MocBar() {
<div className="flex flex-col">
<div className="text-sm font-semibold">Lv. {challengeSelected?.EventIDList1[0].Level}</div>
<div className="flex items-center space-x-1 mt-1">
{listMonster
.find((monster) => monster.child.includes(waveValue))
?.weak?.map((icon, iconIndex) => (
<Image
src={`/icon/${icon.toLowerCase()}.webp`}
alt={icon}
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md border border-white/20 shadow-sm"
width={200}
height={200}
key={iconIndex}
/>
))}
{mapMonster?.[waveValue.toString()]?.weak?.map((icon, iconIndex) => (
<Image
unoptimized
crossOrigin="anonymous"
src={`/icon/${icon.toLowerCase()}.webp`}
alt={icon}
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md border border-white/20 shadow-sm"
width={200}
height={200}
key={iconIndex}
/>
))}
</div>
</div>
</div>

View File

@@ -12,7 +12,7 @@ import { MonsterStore } from "@/types";
export default function PeakBar() {
const { PEAKEvent, mapPEAKInfo } = useEventStore()
const { listMonster } = useMonsterStore()
const { mapMonster } = useMonsterStore()
const { locale } = useLocaleStore()
const {
peak_config,
@@ -216,7 +216,7 @@ export default function PeakBar() {
{/* Enemy Waves */}
{(peak_config?.challenge_id ?? 0) !== 0 && (
{(peak_config?.challenge_id ?? 0) !== 0 && (
<div className="grid grid-cols-1 gap-4">
<div className="rounded-xl p-4 mt-2 border border-warning">
@@ -234,8 +234,10 @@ export default function PeakBar() {
<div className="flex items-center space-x-3">
<div className="relative w-20 h-20 rounded-full overflow-hidden shrink-0 border border-white/10 shadow-sm">
{listMonster.find((monster) => monster.child.includes(monsterId))?.icon && <Image
src={`https://api.hakush.in/hsr/UI/monstermiddleicon/${listMonster.find((monster) => monster.child.includes(monsterId))?.icon?.split("/")?.pop()?.replace(".png", "")}.webp`}
{mapMonster?.[monsterId.toString()]?.icon && <Image
unoptimized
crossOrigin="anonymous"
src={`${process.env.CDN_URL}/${mapMonster?.[monsterId.toString()]?.icon}`}
alt="Enemy Icon"
width={376}
height={512}
@@ -246,18 +248,18 @@ export default function PeakBar() {
<div className="flex flex-col">
<div className="text-sm font-semibold">Lv. {challengeSelected?.EventIDList[0].Level}</div>
<div className="flex items-center space-x-1 mt-1">
{listMonster
.find((monster) => monster.child.includes(monsterId))
?.weak?.map((icon, iconIndex) => (
<Image
src={`/icon/${icon.toLowerCase()}.webp`}
alt={icon}
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md border border-white/20 shadow-sm"
width={200}
height={200}
key={iconIndex}
/>
))}
{mapMonster?.[monsterId.toString()]?.weak?.map((icon, iconIndex) => (
<Image
unoptimized
crossOrigin="anonymous"
src={`/icon/${icon.toLowerCase()}.webp`}
alt={icon}
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md border border-white/20 shadow-sm"
width={200}
height={200}
key={iconIndex}
/>
))}
</div>
</div>
</div>

View File

@@ -13,7 +13,7 @@ import { useTranslations } from "next-intl";
export default function PfBar() {
const { PFEvent, mapPFInfo } = useEventStore()
const { listMonster } = useMonsterStore()
const { mapMonster } = useMonsterStore()
const { locale } = useLocaleStore()
const {
pf_config,
@@ -248,8 +248,10 @@ export default function PfBar() {
>
<div className="flex items-center space-x-3">
<div className="relative w-20 h-20 rounded-full overflow-hidden shrink-0 border border-white/10 shadow-sm">
{listMonster.find((monster) => monster.child.includes(monsterId))?.icon && <Image
src={`https://api.hakush.in/hsr/UI/monstermiddleicon/${listMonster.find((monster) => monster.child.includes(monsterId))?.icon?.split("/")?.pop()?.replace(".png", "")}.webp`}
{mapMonster?.[monsterId.toString()]?.icon && <Image
unoptimized
crossOrigin="anonymous"
src={`${process.env.CDN_URL}/${mapMonster?.[monsterId.toString()]?.icon}`}
alt="Enemy Icon"
width={376}
height={512}
@@ -258,20 +260,20 @@ export default function PfBar() {
</div>
<div className="flex flex-col">
<div className="text-sm font-semibold">Lv. {challengeSelected?.EventIDList1[0].Level}</div>
<div className="text-sm font-semibold">{mapMonster?.[monsterId.toString()]?.id} | Lv. {challengeSelected?.EventIDList1[0].Level}</div>
<div className="flex items-center space-x-1 mt-1">
{listMonster
.find((monster) => monster.child.includes(monsterId))
?.weak?.map((icon, iconIndex) => (
<Image
src={`/icon/${icon.toLowerCase()}.webp`}
alt={icon}
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md border border-white/20 shadow-sm"
width={200}
height={200}
key={iconIndex}
/>
))}
{mapMonster?.[monsterId.toString()]?.weak?.map((icon, iconIndex) => (
<Image
unoptimized
crossOrigin="anonymous"
src={`/icon/${icon.toLowerCase()}.webp`}
alt={icon}
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md border border-white/20 shadow-sm"
width={200}
height={200}
key={iconIndex}
/>
))}
</div>
</div>
</div>
@@ -298,11 +300,13 @@ export default function PfBar() {
<div className="flex items-center space-x-3">
<div className="relative w-20 h-20 rounded-full overflow-hidden shrink-0 border border-white/10 shadow-sm">
{listMonster.find((monster) => monster.child.includes(monsterId))?.icon && <Image
src={`https://api.hakush.in/hsr/UI/monstermiddleicon/${listMonster.find((monster) => monster.child.includes(monsterId))?.icon?.split("/")?.pop()?.replace(".png", "")}.webp`}
{mapMonster?.[monsterId.toString()]?.icon && <Image
unoptimized
crossOrigin="anonymous"
src={`${process.env.CDN_URL}/${mapMonster?.[monsterId.toString()]?.icon}`}
alt="Enemy Icon"
width={376}
height={512}
width={400}
height={300}
className="w-full h-full object-cover"
/>}
</div>
@@ -310,18 +314,18 @@ export default function PfBar() {
<div className="flex flex-col">
<div className="text-sm font-semibold">Lv. {challengeSelected?.EventIDList1[0].Level}</div>
<div className="flex items-center space-x-1 mt-1">
{listMonster
.find((monster) => monster.child.includes(monsterId))
?.weak?.map((icon, iconIndex) => (
<Image
src={`/icon/${icon.toLowerCase()}.webp`}
alt={icon}
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md border border-white/20 shadow-sm"
width={200}
height={200}
key={iconIndex}
/>
))}
{mapMonster?.[monsterId.toString()]?.weak?.map((icon, iconIndex) => (
<Image
unoptimized
crossOrigin="anonymous"
src={`/icon/${icon.toLowerCase()}.webp`}
alt={icon}
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md border border-white/20 shadow-sm"
width={200}
height={200}
key={iconIndex}
/>
))}
</div>
</div>
</div>

View File

@@ -70,7 +70,7 @@ export default function QuickView() {
const subAffixMap = mapSubAffix["5"]
if (!mainAffixMap || !subAffixMap) return
return {
img: `https://api.hakush.in/hsr/UI/relicfigures/IconRelic_${value.relic_set_id}_${key}.webp`,
img: `${process.env.CDN_URL}/spriteoutput/relicfigures/IconRelic_${value.relic_set_id}_${key}.png`,
mainAffix: {
property: mainAffixMap?.[value?.main_affix_id]?.property,
level: value?.level,
@@ -408,7 +408,15 @@ export default function QuickView() {
return (
<div key={index} className="flex flex-row items-center justify-between">
<div className="flex flex-row items-center">
<NextImage src={stat?.icon || ""} alt="Stat Icon" width={40} height={40} className="h-auto w-10 p-1 mx-1 bg-black/20 rounded-full" />
<NextImage
src={stat?.icon || ""}
unoptimized
crossOrigin="anonymous"
alt="Stat Icon"
width={40}
height={40}
className="h-auto w-10 p-1 mx-1 bg-black/20 rounded-full"
/>
<div className="font-bold">{stat.name}</div>
</div>
<div className="ml-3 mr-3 grow border rounded opacity-50" />

View File

@@ -276,7 +276,7 @@ export default function RelicMaker() {
customSet={Object.entries(relicSets).map(([key, value]) => ({
value: key,
label: value.Name,
imageUrl: `https://api.hakush.in/hsr/UI/itemfigures/${value.Icon.match(/\d+/)?.[0]}.webp`
imageUrl: `${process.env.CDN_URL}/spriteoutput/itemfigures/${value.Icon.match(/\d+/)?.[0]}.png`
}))}
excludeSet={[]}
selectedCustomSet={selectedRelicSet}

View File

@@ -78,7 +78,15 @@ export default function SelectCustomImage({ customSet, excludeSet, selectedCusto
const formatOptionLabel = (option: SelectOption) => (
<div className="flex items-center gap-1 w-full h-full">
<Image src={option.imageUrl} alt="" width={125} height={125} className="w-8 h-8 object-contain bg-warning-content rounded-full" />
<Image
unoptimized
crossOrigin="anonymous"
src={option.imageUrl}
alt=""
width={125}
height={125}
className="w-8 h-8 object-contain bg-warning-content rounded-full"
/>
<ParseText className='font-bold' text={option.label} locale={locale} />
</div>
)

View File

@@ -1,7 +1,7 @@
"use client";
import { useEffect, useState, useRef, useMemo, useCallback } from 'react';
import useAvatarStore from "@/stores/avatarStore";
import { FastAverageColor, FastAverageColorResult } from 'fast-average-color';
import { FastAverageColor } from 'fast-average-color';
import NextImage from 'next/image';
import ParseText from '../parseText';
import useLocaleStore from '@/stores/localeStore';
@@ -41,10 +41,8 @@ export default function ShowCaseInfo() {
html2canvas(cardRef.current!, {
scale: 2,
backgroundColor: "#000000",
logging: true,
useCORS: true,
allowTaint: false,
imageTimeout: 30000,
useCORS: true
})
)
.then((canvas: HTMLCanvasElement) => {
@@ -63,13 +61,20 @@ export default function ShowCaseInfo() {
if (!avatarSelected?.id) return;
const fac = new FastAverageColor();
const img = new Image();
img.crossOrigin = 'anonymous';
img.src = `https://api.hakush.in/hsr/UI/avatardrawcard/${avatarSelected.id}.webp`;
img.src = `${process.env.CDN_URL}/spriteoutput/avatardrawcard/${avatarSelected?.id}.png`;
img.onload = () => {
fac.getColorAsync(img).then((color: FastAverageColorResult) => {
setAvgColor(color.hex); // #RRGGBB
});
fac.getColorAsync(img)
.then((color) => {
setAvgColor(color.hex);
})
.catch(e => console.error("Vẫn lỗi CORS:", e));
};
return () => {
fac.destroy();
img.onload = null;
};
}, [avatarSelected]);
@@ -153,7 +158,7 @@ export default function ShowCaseInfo() {
const subAffixMap = mapSubAffix["5"]
if (!mainAffixMap || !subAffixMap) return
return {
img: `https://api.hakush.in/hsr/UI/relicfigures/IconRelic_${value.relic_set_id}_${key}.webp`,
img: `${process.env.CDN_URL}/spriteoutput/relicfigures/IconRelic_${value.relic_set_id}_${key}.png`,
mainAffix: {
property: mainAffixMap?.[value?.main_affix_id]?.property,
level: value?.level,
@@ -185,7 +190,6 @@ export default function ShowCaseInfo() {
}, 0)
}, 0)
}, [relicStats, avatarInfo])
const characterStats = useMemo(() => {
if (!avatarSelected || !avatarData) return
@@ -507,15 +511,15 @@ export default function ShowCaseInfo() {
const getImageSkill = useCallback((icon: string | undefined, status: StatusAddType | undefined) => {
if (!icon) return
if (icon.startsWith("SkillIcon")) {
return `https://api.hakush.in/hsr/UI/skillicons/${icon.replace(".png", ".webp")}`
return `${process.env.CDN_URL}/spriteoutput/skillicons/avatar/${avatarSelected?.id}/${icon}`
} else if (status && mappingStats[status.PropertyType]) {
return mappingStats[status.PropertyType].icon
}
else if (icon.startsWith("Icon")) {
return `https://api.hakush.in/hsr/UI/trace/${icon.replace(".png", ".webp")}`
return `${process.env.CDN_URL}/spriteoutput/trace/${icon}`
}
return ""
}, [])
}, [avatarSelected?.id])
return (
<div className="flex flex-col justify-start m-1 text-white">
@@ -544,8 +548,10 @@ export default function ShowCaseInfo() {
{avatarSelected && (
<NextImage
ref={imgRef}
src={`https://api.hakush.in/hsr/UI/avatardrawcard/${avatarSelected?.id}.webp`}
className="object-cover scale-[2] overflow-hidden"
unoptimized
crossOrigin="anonymous"
src={`${process.env.CDN_URL}/spriteoutput/avatardrawcard/${avatarSelected?.id}.png`}
className="object-contain scale-[2] overflow-hidden"
alt="Character Preview"
width={1024}
height={1024}
@@ -582,7 +588,7 @@ export default function ShowCaseInfo() {
transition: "transform 0.3s ease, filter 0.3s ease",
}}
>
{isActive && (
<div
className="absolute inset-0 rounded-full pointer-events-none"
@@ -617,9 +623,11 @@ export default function ShowCaseInfo() {
<NextImage
src={src ?? null}
alt="Rank Icon"
width={48}
height={48}
className="block rounded-full object-cover"
width={125}
height={125}
unoptimized
crossOrigin="anonymous"
className="block rounded-full object-contain"
style={{
width: "44px",
height: "44px",
@@ -653,8 +661,23 @@ export default function ShowCaseInfo() {
{avatarSelected && (
<div className="flex gap-1">
<NextImage src={`/icon/${avatarSelected?.baseType.toLowerCase()}.webp`} alt="Path Icon" width={32} height={32} className="h-auto w-8" />
<NextImage src={`/icon/${avatarSelected?.damageType.toLowerCase()}.webp`} alt="Element Icon" width={32} height={32} className="h-auto w-8" />
<NextImage
unoptimized
crossOrigin="anonymous"
src={`/icon/${avatarSelected?.baseType.toLowerCase()}.webp`}
alt="Path Icon"
width={32}
height={32}
className="h-auto w-8"
/>
<NextImage
unoptimized
crossOrigin="anonymous"
src={`/icon/${avatarSelected?.damageType.toLowerCase()}.webp`}
alt="Element Icon"
width={32}
height={32}
className="h-auto w-8" />
</div>
)}
@@ -665,7 +688,15 @@ export default function ShowCaseInfo() {
<div className="relative flex h-56.25 w-auto flex-row items-center">
{avatarSelected && (
<div className="absolute inset-0 flex items-center justify-center">
<NextImage src={`/icon/${avatarSelected?.baseType.toLowerCase()}.webp`} alt="Path Icon" width={160} height={160} className="h-40 w-40 opacity-20" />
<NextImage
unoptimized
crossOrigin="anonymous"
src={`/icon/${avatarSelected?.baseType.toLowerCase()}.webp`}
alt="Path Icon"
width={160}
height={160}
className="h-40 w-40 opacity-20"
/>
</div>
)}
@@ -721,10 +752,12 @@ export default function ShowCaseInfo() {
return skillImg ? (
<NextImage
src={skillImg}
unoptimized
crossOrigin="anonymous"
alt={btn.id}
width={32}
height={32}
className={`h-auto ${imageSize} ${filterClass}`}
width={125}
height={125}
className={`h-full ${imageSize} ${filterClass}`}
/>
) : null;
})()
@@ -777,8 +810,10 @@ export default function ShowCaseInfo() {
{/* Card Image */}
<NextImage
className="absolute object-cover rounded-xl z-9 w-[95%]"
src={`https://api.hakush.in/hsr/UI/lightconemaxfigures/${avatarProfile?.lightcone.item_id}.webp`}
className="absolute object-contain rounded-xl z-9 w-[95%]"
unoptimized
crossOrigin="anonymous"
src={`${process.env.CDN_URL}/spriteoutput/lightconemaxfigures/${avatarProfile?.lightcone.item_id}.png`}
alt="Lightcone Image"
width={904}
height={1206}
@@ -872,19 +907,43 @@ export default function ShowCaseInfo() {
<div className="flex justify-center items-center flex-col gap-1 mt-1 ">
<div className="flex gap-1 text-sm ">
<div className="flex items-center gap-1 rounded bg-black/30 px-1 w-fit py-1">
<NextImage src="/icon/hp.webp" alt="HP" width={16} height={16} className="w-4 h-4" />
<NextImage
unoptimized
crossOrigin="anonymous"
src="/icon/hp.webp"
alt="HP"
width={16}
height={16}
className="w-4 h-4"
/>
<span>{
lightconeStats?.hp
}</span>
</div>
<div className="flex items-center gap-1 rounded bg-black/30 px-1 w-fit py-1">
<NextImage src="/icon/attack.webp" alt="ATK" width={16} height={16} className="w-4 h-4" />
<NextImage
src="/icon/attack.webp"
unoptimized
crossOrigin="anonymous"
alt="ATK"
width={16}
height={16}
className="w-4 h-4"
/>
<span>{lightconeStats?.attack}</span>
</div>
</div>
<div className="flex items-center gap-1 rounded bg-black/30 px-1 w-fit py-1">
<NextImage src="/icon/defence.webp" alt="DEF" width={16} height={16} className="w-4 h-4" />
<NextImage
unoptimized
crossOrigin="anonymous"
src="/icon/defence.webp"
alt="DEF"
width={16}
height={16}
className="w-4 h-4"
/>
<span>{lightconeStats?.def}</span>
</div>
</div>
@@ -905,7 +964,14 @@ export default function ShowCaseInfo() {
return (
<div key={index} className="flex flex-row items-center justify-between">
<div className="flex flex-row items-center">
<NextImage src={stat?.icon || ""} alt="Stat Icon" width={40} height={40} className="h-auto w-10 p-2" />
<NextImage src={stat?.icon || ""}
unoptimized
crossOrigin="anonymous"
alt="Stat Icon"
width={40}
height={40}
className="h-auto w-10 p-2"
/>
<span className="font-bold">{stat.name}</span>
</div>
<div className="ml-3 mr-3 grow border rounded opacity-50" />

View File

@@ -23,6 +23,8 @@ export default function RelicShowcase({
<div className="absolute inset-0 rounded-lg blur-lg -z-10"></div>
<NextImage
src={relic?.img || ""}
unoptimized
crossOrigin="anonymous"
width={78}
height={78}
alt="Relic Icon"
@@ -47,6 +49,8 @@ export default function RelicShowcase({
<div className="absolute inset-0 bg-yellow-500/15 rounded-full blur-md -z-10"></div>
<NextImage
src={relic?.mainAffix?.detail?.icon || ""}
unoptimized
crossOrigin="anonymous"
width={35}
height={35}
alt="Main Affix Icon"
@@ -72,6 +76,8 @@ export default function RelicShowcase({
{subAffix?.detail?.icon ? (
<NextImage
src={subAffix?.detail?.icon || ""}
unoptimized
crossOrigin="anonymous"
width={32}
height={32}
alt="Sub Affix Icon"
@@ -86,11 +92,11 @@ export default function RelicShowcase({
+{subAffix?.valueAffix + subAffix?.detail?.unit}
</span>
{
(avatarInfo?.Relics?.SubAffixPropertyList.findIndex((item) => item === subAffix?.property) !== -1) && (
<span className="ml-1 bg-yellow-600/20 text-yellow-400 rounded-full px-1 py-0.5 text-[10px] font-semibold border border-yellow-600/30 shrink-0 leading-none">
{subAffix?.count}
</span>
)}
(avatarInfo?.Relics?.SubAffixPropertyList.findIndex((item) => item === subAffix?.property) !== -1) && (
<span className="ml-1 bg-yellow-600/20 text-yellow-400 rounded-full px-1 py-0.5 text-[10px] font-semibold border border-yellow-600/30 shrink-0 leading-none">
{subAffix?.count}
</span>
)}
</div>
</div>
)

View File

@@ -48,14 +48,13 @@ export default function SkillsInfo() {
const getImageSkill = (icon: string | undefined, status: StatusAddType | undefined) => {
if (!icon) return
const urlPrefix = "https://api.hakush.in/hsr/UI/skillicons/";
const urlPrefix = `${process.env.CDN_URL}/spriteoutput/skillicons/avatar/${avatarSelected?.id}/`;
if (icon.startsWith("SkillIcon")) {
return `${urlPrefix}${icon.replace(".png", ".webp")}`
return `${urlPrefix}${icon}`
} else if (status && mappingStats[status.PropertyType]) {
return mappingStats[status.PropertyType].icon
}
else if (icon.startsWith("Icon")) {
return `https://api.hakush.in/hsr/UI/trace/${icon.replace(".png", ".webp")}`
} else if (icon.startsWith("Icon")) {
return `${process.env.CDN_URL}/spriteoutput/trace/${icon}`
}
}
@@ -147,6 +146,8 @@ export default function SkillsInfo() {
{traceButtons && avatarInfo && (
<div className="grid col-span-4 relative w-full aspect-square">
<Image
unoptimized
crossOrigin="anonymous"
src={`/skilltree/${avatarSelected?.baseType?.toUpperCase()}.webp`}
alt=""
width={312}
@@ -179,8 +180,8 @@ export default function SkillsInfo() {
${btn.size === "elation" ? "w-[9vw] h-[9vw] md:w-[3.5vw] md:h-[3.5vw] bg-black" : ""}
${skillSelected === btn.id ? "border-4 border-primary" : ""}
${!avatarData?.data.skills?.[avatarSkillTree?.[btn.id]?.["1"]?.PointID]
? "opacity-50 cursor-not-allowed"
: ""}
? "opacity-50 cursor-not-allowed"
: ""}
`}
onClick={() => {
setSkillSelected(btn.id === skillSelected ? null : btn.id)
@@ -195,13 +196,14 @@ export default function SkillsInfo() {
src={getImageSkill(avatarInfo?.SkillTrees?.[btn.id]?.["1"]?.Icon, avatarSkillTree?.[btn.id]?.["1"]?.StatusAddList[0]) || ""}
alt={btn.id.replaceAll("Point", "")}
priority={true}
unoptimized={true}
width={124}
height={124}
style={{
filter: (btn.size !== "big" && btn.size !== "memory" && btn.size !== "elation") ? "brightness(0%)" : ""
}}
/>
{(btn.size === "big" || btn.size === "memory" || btn.size === "elation") && (
{(btn.size === "big" || btn.size === "memory" || btn.size === "elation") && (
<p className="
z-12 text-sm sm:text-xs lg:text-sm xl:text-base 2xl:text-2xl
font-bold text-center rounded-full absolute
@@ -252,12 +254,12 @@ export default function SkillsInfo() {
{btn.size === "big" && (
<div
style={{
position: "absolute",
inset: 0,
backgroundColor: "#f5e4b0",
mixBlendMode: "screen",
opacity: 0.3,
borderRadius: "50%"
position: "absolute",
inset: 0,
backgroundColor: "#f5e4b0",
mixBlendMode: "screen",
opacity: 0.3,
borderRadius: "50%"
}}
/>
)}