UPDATE: Draft
Some checks failed
Gitea Auto Deploy / Deploy-Container (push) Failing after 1m0s

This commit is contained in:
2026-01-10 20:23:16 +07:00
parent cb2f040010
commit f837e880d0
16 changed files with 473 additions and 167 deletions

View File

@@ -254,9 +254,25 @@
"extraSetting": "额外设置", "extraSetting": "额外设置",
"disableCensorship": "禁用审查", "disableCensorship": "禁用审查",
"hideUI": "隐藏界面", "hideUI": "隐藏界面",
"theoryCraftMode": "理论研究模式", "theoryCraftMode": "Theory Craft 模式",
"cycleCount": "循环次数", "cycleCount": "循环次数",
"pleaseSelectAllSubStats": "请选取所有副属性", "pleaseSelectAllSubStats": "请选取所有副属性",
"subStatRollCountCannotBeZero": "副属性的行数不能为0" "subStatRollCountCannotBeZero": "副属性的行数不能为0",
"theoryCraft": "Theory Craft",
"multipathCharacter": "多命途角色",
"mainPath": "主角命途",
"march7Path": "三月七命途",
"challenge": "挑战",
"skipNode": "跳过节点",
"disableSkip": "禁用跳过",
"skipNode1": "跳过节点1",
"skipNode2": "跳过节点2",
"extraFeatures": "附加功能",
"detailTheoryCraft": "开启后可自定义循环数,并在敌人设置中调整生命值。",
"detailSkipNode": "开启后可跳过混沌回忆或虚构叙事的节点1/节点2。",
"detailChallengePeak": "允许更改当前异相中的「巅峰」赛季。",
"detailHiddenUi": "开启后将隐藏游戏界面。",
"detailDisableCensorship": "开启后将关闭游戏内的审查。",
"detailMultipathCharacter": "允许更改部分角色的命途。"
} }
} }

View File

@@ -19,22 +19,22 @@
"servant": "Servant", "servant": "Servant",
"damage": "Damage", "damage": "Damage",
"type": "Type", "type": "Type",
"warrior": "Destruction", "warrior": "Destruction",
"knight": "Preservation", "knight": "Preservation",
"mage": "Erudition", "mage": "Erudition",
"priest": "Abundance", "priest": "Abundance",
"rogue": "The Hunt", "rogue": "The Hunt",
"shaman": "Harmony", "shaman": "Harmony",
"warlock": "Nihility", "warlock": "Nihility",
"memory": "Remembrance", "memory": "Remembrance",
"elation": "The Elation", "elation": "The Elation",
"fire": "Fire", "fire": "Fire",
"ice": "Ice", "ice": "Ice",
"imaginary": "Imaginary", "imaginary": "Imaginary",
"physical": "Physical", "physical": "Physical",
"quantum": "Quantum", "quantum": "Quantum",
"thunder": "Thunder", "thunder": "Thunder",
"wind": "Wind", "wind": "Wind",
"hp": "Hp", "hp": "Hp",
"atk": "Atk", "atk": "Atk",
"speed": "Speed", "speed": "Speed",
@@ -258,6 +258,22 @@
"theoryCraftMode": "Theorycraft Mode", "theoryCraftMode": "Theorycraft Mode",
"cycleCount": "Cycle Count", "cycleCount": "Cycle Count",
"pleaseSelectAllSubStats": "Please select all sub stats", "pleaseSelectAllSubStats": "Please select all sub stats",
"subStatRollCountCannotBeZero": "Sub stat roll count cannot be zero" "subStatRollCountCannotBeZero": "Sub stat roll count cannot be zero",
"theorycraft": "Theorycraft",
"multipathCharacter": "Multipath Character",
"mainPath": "Main Path",
"march7Path": "March 7 Path",
"challenge": "Challenge",
"skipNode": "Skip Node",
"disableSkip": "Disable skip",
"skipNode1": "Skip node 1",
"skipNode2": "Skip node 2",
"extraFeatures": "Extra Features",
"detailTheoryCraft": "Enabling this feature allows you to customize the cycle count and adjust enemy HP in the enemy settings.",
"detailSkipNode": "Enabling this feature allows you to skip (Node 1/Node 2) in Memory of Chaos or Pure Fiction.",
"detailChallengePeak": "Allows changing the Peak season in the current Anomaly.",
"detailHiddenUi": "Enabling this feature will hide the game UI.",
"detailDisableCensorship": "Enabling this feature will disable in-game censorship.",
"detailMultipathCharacter": "Allows changing the Path of certain characters."
} }
} }

View File

@@ -257,6 +257,22 @@
"theoryCraftMode": "シアリークラフトモード", "theoryCraftMode": "シアリークラフトモード",
"cycleCount": "サイクル数", "cycleCount": "サイクル数",
"pleaseSelectAllSubStats": "すべてのサブステータスを選択してください", "pleaseSelectAllSubStats": "すべてのサブステータスを選択してください",
"subStatRollCountCannotBeZero": "サブステータスの行数は0にできません" "subStatRollCountCannotBeZero": "サブステータスの行数は0にできません",
"theoryCraft": "シアリークラフト",
"multipathCharacter": "複数運命キャラ",
"mainPath": "主人公の運命",
"march7Path": "三月なのかの運命",
"challenge": "挑戦",
"skipNode": "ノードをスキップ",
"disableSkip": "スキップ無効",
"skipNode1": "ード1をスキップ",
"skipNode2": "ード2をスキップ",
"extraFeatures": "追加機能",
"detailTheoryCraft": "この機能を有効にすると、サイクル数の調整や敵設定でHPの調整が可能になります。",
"detailSkipNode": "この機能を有効にすると、混沌の記憶または虚構叙事のード1ード2をスキップできます。",
"detailChallengePeak": "現在の異相における「頂」のシーズンを変更できます。",
"detailHiddenUi": "この機能を有効にすると、ゲームのUIを非表示にします。",
"detailDisableCensorship": "この機能を有効にすると、ゲーム内の検閲を無効にします。",
"detailMultipathCharacter": "一部キャラクターの運命を変更できます。"
} }
} }

View File

@@ -254,9 +254,25 @@
"extraSetting": "추가 설정", "extraSetting": "추가 설정",
"disableCensorship": "검열 비활성화", "disableCensorship": "검열 비활성화",
"hideUI": "UI 숨기기", "hideUI": "UI 숨기기",
"theoryCraftMode": "이론 제작 모드", "theoryCraftMode": "Theory Craft 모드",
"cycleCount": "사이클 수", "cycleCount": "사이클 수",
"pleaseSelectAllSubStats": "모든 부옵션을 선택하세요", "pleaseSelectAllSubStats": "모든 부옵션을 선택하세요",
"subStatRollCountCannotBeZero": "부옵션의 줄 수는 0일 수 없습니다" "subStatRollCountCannotBeZero": "부옵션의 줄 수는 0일 수 없습니다",
"theoryCraft": "Theory Craft",
"multipathCharacter": "다중 운명 캐릭터",
"mainPath": "개척자 운명",
"march7Path": "삼월칠일 운명",
"challenge": "도전",
"skipNode": "노드 건너뛰기",
"disableSkip": "건너뛰기 비활성화",
"skipNode1": "노드 1 건너뛰기",
"skipNode2": "노드 2 건너뛰기",
"extraFeatures": "추가 기능",
"detailTheoryCraft": "이 기능을 활성화하면 사이클 수를 조정하고 적 설정에서 HP를 변경할 수 있습니다.",
"detailSkipNode": "이 기능을 활성화하면 혼돈의 기억 또는 허구 서사의 (노드 1/노드 2)를 건너뛸 수 있습니다.",
"detailChallengePeak": "현재 이형에서의 피크 시즌을 변경할 수 있습니다.",
"detailHiddenUi": "이 기능을 활성화하면 게임 UI가 숨겨집니다.",
"detailDisableCensorship": "이 기능을 활성화하면 게임 내 검열이 비활성화됩니다.",
"detailMultipathCharacter": "일부 캐릭터의 운명을 변경할 수 있습니다."
} }
} }

View File

@@ -254,9 +254,25 @@
"extraSetting": "Cài đặt bổ sung", "extraSetting": "Cài đặt bổ sung",
"disableCensorship": "Tắt kiểm duyệt", "disableCensorship": "Tắt kiểm duyệt",
"hideUI": "Ẩn giao diện", "hideUI": "Ẩn giao diện",
"theoryCraftMode": "Chế độ Theorycraft", "theoryCraftMode": "Chế độ Theory Craft",
"cycleCount": "Số vòng", "cycleCount": "Số vòng",
"pleaseSelectAllSubStats": "Vui lòng chọn tất cả chỉ số phụ", "pleaseSelectAllSubStats": "Vui lòng chọn tất cả chỉ số phụ",
"subStatRollCountCannotBeZero": "Số dòng của chỉ số phụ không thể bằng 0" "subStatRollCountCannotBeZero": "Số dòng của chỉ số phụ không thể bằng 0",
"theoryCraft": "Theory Craft",
"multipathCharacter": "Nhân vật đa Vận Mệnh",
"mainPath": "Vận Mệnh Nhân Vật Chính",
"march7Path": "Vận Mệnh March 7",
"challenge": "Thử thách",
"skipNode": "Bỏ qua node",
"disableSkip": "Tắt bỏ qua",
"skipNode1": "Bỏ qua node 1",
"skipNode2": "Bỏ qua node 2",
"extraFeatures": "Tính năng bổ sung",
"detailTheoryCraft": "Khi bật tính năng này sẽ cho phép tùy chỉnh số cycle và trong mục kẻ địch tủy chỉnh sẽ cho phép điều chỉnh số hp.",
"detailSkipNode": "Khi bật tính năng này sẽ cho phép bỏ qua (node 1/node 2) của Hồi ức hỗn độn hoặc Kể chuyện hư cấu.",
"detailChallengePeak": "Cho phép thay đổi mùa Trọng tại dị tướng hiện tại.",
"detailHiddenUi": "Khi bật tính năng này sẽ ẩn giao diện của game.",
"detailDisableCensorship": "Khi bật tính năng này sẽ tắt kiểm duyệt của game.",
"detailMultipathCharacter": "Cho phép thay đổi Vận Mệnh của một vài nhân vật."
} }
} }

View File

@@ -254,9 +254,25 @@
"extraSetting": "额外设置", "extraSetting": "额外设置",
"disableCensorship": "禁用审查", "disableCensorship": "禁用审查",
"hideUI": "隐藏界面", "hideUI": "隐藏界面",
"theoryCraftMode": "理论研究模式", "theoryCraftMode": "Theory Craft 模式",
"cycleCount": "循环次数", "cycleCount": "循环次数",
"pleaseSelectAllSubStats": "请选取所有副属性", "pleaseSelectAllSubStats": "请选取所有副属性",
"subStatRollCountCannotBeZero": "副属性的行数不能为0" "subStatRollCountCannotBeZero": "副属性的行数不能为0",
"theoryCraft": "Theory Craft",
"multipathCharacter": "多命途角色",
"mainPath": "主角命途",
"march7Path": "三月七命途",
"challenge": "挑战",
"skipNode": "跳过节点",
"disableSkip": "禁用跳过",
"skipNode1": "跳过节点1",
"skipNode2": "跳过节点2",
"extraFeatures": "附加功能",
"detailTheoryCraft": "开启后可自定义循环数,并在敌人设置中调整生命值。",
"detailSkipNode": "开启后可跳过混沌回忆或虚构叙事的节点1/节点2。",
"detailChallengePeak": "允许更改当前异相中的「巅峰」赛季。",
"detailHiddenUi": "开启后将隐藏游戏界面。",
"detailDisableCensorship": "开启后将关闭游戏内的审查。",
"detailMultipathCharacter": "允许更改部分角色的命途。"
} }
} }

View File

@@ -1,12 +1,17 @@
@import "tailwindcss"; @import "tailwindcss";
@plugin "daisyui" { @plugin "daisyui" {
themes: winter --default, night --prefersdark, cupcake, coffee; themes: winter --default, night --prefersdark, cupcake, coffee;
} }
@tailwind utilities;
@plugin 'tailwind-scrollbar' { @plugin 'tailwind-scrollbar' {
nocompatible: true; nocompatible: true;
} }
:root { :root {
--size-big: 4vw; --size-big: 4vw;
--size-medium: 3vw; --size-medium: 3vw;
@@ -30,40 +35,3 @@
::-webkit-scrollbar-button { ::-webkit-scrollbar-button {
display: none; display: none;
} }
@tailwind utilities;
@layer components {
/* .my-react-select-container {
} */
.my-react-select-container .my-react-select__control {
@apply bg-white dark:bg-neutral-700 border-2 border-neutral-300 dark:border-neutral-700 hover:border-neutral-400 dark:hover:border-neutral-500;
}
.my-react-select-container .my-react-select__control--is-focused {
@apply border-neutral-500 hover:border-neutral-500 dark:border-neutral-400 dark:hover:border-neutral-400 shadow-none;
}
.my-react-select-container .my-react-select__menu {
@apply bg-neutral-100 dark:bg-neutral-700 border-2 border-neutral-300 dark:border-neutral-600;
}
.my-react-select-container .my-react-select__option {
@apply text-neutral-600 dark:text-neutral-200 bg-neutral-100 hover:bg-neutral-200 dark:bg-neutral-700 dark:hover:bg-neutral-800;
}
/* .my-react-select-container .my-react-select__option--is-focused {
@apply bg-neutral-200 dark:bg-neutral-800;
} */
.my-react-select-container .my-react-select__indicator-separator {
@apply bg-neutral-400;
}
.my-react-select-container .my-react-select__menu {
z-index: 999;
}
.my-react-select-container .my-react-select__input-container,
.my-react-select-container .my-react-select__placeholder,
.my-react-select-container .my-react-select__single-value {
@apply text-neutral-600 dark:text-neutral-200;
}
}

View File

@@ -18,7 +18,7 @@ export default function CharacterCard({ data }: CharacterCardProps) {
className="z-10 flex flex-col items-center rounded-xl shadow-xl className="z-10 flex flex-col items-center rounded-xl shadow-xl
bg-linear-to-br from-base-300 via-base-100 to-warning/70 bg-linear-to-br from-base-300 via-base-100 to-warning/70
transform transition-transform duration-300 ease-in-out transform transition-transform duration-300 ease-in-out
hover:scale-105 cursor-pointer min-h-[170px] sm:min-h-[180px] md:min-h-[210px] lg:min-h-[220px] xl:min-h-[240px] 2xl:min-h-[340px]" hover:scale-105 cursor-pointer min-h-42.5 sm:min-h-45 md:min-h-52.5 lg:min-h-55 xl:min-h-60 2xl:min-h-85"
> >
<div <div
className={`w-full rounded-md bg-linear-to-br ${data.rank === "CombatPowerAvatarRarityType5" className={`w-full rounded-md bg-linear-to-br ${data.rank === "CombatPowerAvatarRarityType5"

View File

@@ -1,80 +1,229 @@
'use client' 'use client'
import { motion } from "framer-motion" import { motion } from "framer-motion"
import { EyeOff, Eye, Hammer, RefreshCw, ShieldBan } from "lucide-react" import { EyeOff, Eye, Hammer, RefreshCw, ShieldBan, User, Swords, SkipForward, BowArrow, Info } from "lucide-react"
import useGlobalStore from '@/stores/globalStore'; import useGlobalStore from '@/stores/globalStore'
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl"
import useEventStore from "@/stores/eventStore"
import { getLocaleName, getNameChar } from "@/helper"
import useLocaleStore from "@/stores/localeStore"
import useAvatarStore from "@/stores/avatarStore"
import SelectCustomImage from "../select/customSelectImage"
export default function ExtraSettingBar() { export default function ExtraSettingBar() {
const { extraData, setExtraData } = useGlobalStore() const { extraData, setExtraData } = useGlobalStore()
const transI18n = useTranslations("DataPage") const transI18n = useTranslations("DataPage")
const { PEAKEvent } = useEventStore()
const { listAvatar } = useAvatarStore()
const { locale } = useLocaleStore()
return ( return (
<div className="px-4 sm:px-6 py-4"> <div className="px-4 sm:px-6 py-4 space-y-8">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6"> {extraData?.theory_craft && (
{/* Theorycraft Mode */} <div className="space-y-4">
{extraData?.theory_craft?.mode !== undefined && ( <h3 className="text-lg font-bold flex items-center gap-2">
<motion.div {transI18n("theoryCraft")}
whileHover={{ scale: 1.02 }} <div className="tooltip tooltip-info" data-tip={transI18n("detailTheoryCraft")}>
className="form-control bg-base-200 p-4 rounded-xl shadow" <Info className="text-primary" size={20} />
> </div>
<label className="flex flex-wrap items-center label cursor-pointer justify-start gap-3"> </h3>
<Hammer className="text-primary" size={20}/>
<motion.div className="form-control bg-base-200 p-4 rounded-xl shadow">
<label className="flex flex-wrap items-center cursor-pointer justify-start gap-3">
<Hammer className="text-primary" size={20} />
<input <input
type="checkbox" type="checkbox"
className="toggle toggle-primary" className="toggle toggle-primary"
checked={extraData?.theory_craft?.mode} checked={extraData?.theory_craft?.mode}
onChange={(e) => onChange={(e) => {
setExtraData({ setExtraData({
...extraData, ...extraData,
theory_craft: { ...extraData?.theory_craft, mode: e.target.checked } theory_craft: {
hp: extraData?.theory_craft?.hp || {},
cycle_count: extraData?.theory_craft?.cycle_count || 0,
mode: e.target.checked
}
}) })
} }
}
/> />
<span className="label-text font-semibold">{transI18n("theoryCraftMode")}</span> <span className="label-text font-semibold">{transI18n("theoryCraftMode")}</span>
</label> </label>
</motion.div> </motion.div>
)}
{/* Cycle Count */} {extraData?.theory_craft?.mode && (
{extraData?.theory_craft?.mode && ( <motion.div className="form-control bg-base-200 p-4 rounded-xl shadow">
<motion.div <label className="flex flex-wrap items-center justify-start gap-3">
whileHover={{ scale: 1.02 }} <RefreshCw className="text-info" size={20} />
className="form-control bg-base-200 p-4 rounded-xl shadow" <span className="label-text font-semibold">{transI18n("cycleCount")}</span>
> <input
<label className="flex flex-wrap items-center label justify-start gap-3"> type="number"
<RefreshCw className="text-info" size={20}/> className="input input-primary"
<span className="label-text font-semibold">{transI18n("cycleCount")}</span> value={extraData?.theory_craft?.cycle_count}
<input onChange={(e) =>
type="number" setExtraData({
className="input input-bordered" ...extraData,
value={extraData?.theory_craft?.cycle_count} theory_craft: {
onChange={(e) => hp: extraData?.theory_craft?.hp || {},
setExtraData({ cycle_count: parseInt(e.target.value) || 1,
...extraData, mode: extraData?.theory_craft?.mode || false
theory_craft: { }
...extraData?.theory_craft, })
cycle_count: parseInt(e.target.value) || 1 }
} min="1"
}) />
} </label>
min="1" </motion.div>
/> )}
</label> </div>
</motion.div> )}
)}
{/* Hidden UI */}
{extraData?.setting?.hide_ui !== undefined && (
<motion.div {/*MULTIPATH CHAR */}
whileHover={{ scale: 1.02 }} <div className="space-y-4">
className="form-control bg-base-200 p-4 rounded-xl shadow" <h3 className="text-lg font-bold flex items-center gap-2">
> {transI18n("multipathCharacter")}
<label className="flex flex-wrap items-center label cursor-pointer justify-start gap-3"> <div className="tooltip tooltip-info" data-tip={transI18n("detailMultipathCharacter")}>
{extraData?.setting?.hide_ui <Info className="text-primary" size={20} />
? <EyeOff className="text-warning" size={20}/> </div>
: <Eye className="text-success" size={20}/> </h3>
<motion.div className="form-control bg-base-200 p-4 rounded-xl shadow">
<label className="flex items-center gap-3">
<User className="text-warning" size={20} />
<span className="label-text font-semibold">{transI18n("mainPath")}</span>
<SelectCustomImage
customSet={listAvatar.filter((it) => extraData?.multi_path?.multi_path_main?.includes(Number(it.id))).map((it) => ({
value: it.id,
label: getNameChar(locale, it),
imageUrl: `/icon/${it.baseType.toLowerCase()}.webp`
}))}
excludeSet={[]}
selectedCustomSet={extraData?.multi_path?.main?.toString() || ""}
placeholder={transI18n("selectAMainStat")}
setSelectedCustomSet={(it) => {
if (!it) return
setExtraData({
...extraData,
multi_path: {
march_7: extraData?.multi_path?.march_7 || 1001,
main: Number(it) || 8001,
multi_path_main: extraData?.multi_path?.multi_path_main || [],
multi_path_march_7: extraData?.multi_path?.multi_path_march_7 || []
}
})
}}
/>
</label>
</motion.div>
<motion.div className="form-control bg-base-200 p-4 rounded-xl shadow">
<label className="flex items-center gap-3">
<BowArrow className="text-info" size={20} />
<span className="label-text font-semibold">{transI18n("march7Path")}</span>
<SelectCustomImage
customSet={listAvatar.filter((it) => extraData?.multi_path?.multi_path_march_7?.includes(Number(it.id))).map((it) => ({
value: it.id,
label: getNameChar(locale, it),
imageUrl: `/icon/${it.baseType.toLowerCase()}.webp`
}))}
excludeSet={[]}
selectedCustomSet={extraData?.multi_path?.march_7?.toString() || ""}
placeholder={transI18n("selectAMainStat")}
setSelectedCustomSet={(it) => {
if (!it) return
setExtraData({
...extraData,
multi_path: {
march_7: Number(it) || 1001,
main: extraData?.multi_path?.main || 8001,
multi_path_main: extraData?.multi_path?.multi_path_main || [],
multi_path_march_7: extraData?.multi_path?.multi_path_march_7 || []
}
})
}}
/>
</label>
</motion.div>
</div>
{/* CHALLENGE */}
<div className="space-y-4">
<h3 className="text-lg font-bold">{transI18n("challenge")}</h3>
<motion.div className="form-control bg-base-200 p-4 rounded-xl shadow">
<label className="flex items-center gap-3">
<Swords className="text-error" size={20} />
<span className="label-text font-semibold">{transI18n("anomalyArbitration")}</span>
<div className="tooltip tooltip-info" data-tip={transI18n("detailChallengePeak")}>
<Info className="text-primary" size={20} />
</div>
<select
value={extraData?.challenge?.challenge_peak_group_id || ""}
className="select select-error"
onChange={(e) =>
setExtraData({
...extraData,
challenge: {
skip_node: extraData?.challenge?.skip_node || 0,
challenge_peak_group_id: parseInt(e.target.value) || 0,
challenge_peak_group_id_list: extraData?.challenge?.challenge_peak_group_id_list || []
}
})
} }
>
{PEAKEvent.filter(event => extraData?.challenge?.challenge_peak_group_id_list?.includes(Number(event.id))).map(event => (
<option key={event.id} value={event.id}>
{getLocaleName(locale, event)} ({event.id})
</option>
))}
</select>
</label>
</motion.div>
<motion.div className="form-control bg-base-200 p-4 rounded-xl shadow">
<label className="flex items-center gap-3">
<SkipForward className="text-warning" size={20} />
<span className="label-text font-semibold">{transI18n("skipNode")}</span>
<div className="tooltip tooltip-info" data-tip={transI18n("detailSkipNode")}>
<Info className="text-primary" size={20} />
</div>
<select
value={extraData?.challenge?.skip_node || "0"}
className="select select-warning"
onChange={(e) =>
setExtraData({
...extraData,
challenge: {
challenge_peak_group_id: extraData?.challenge?.challenge_peak_group_id || 0,
challenge_peak_group_id_list: extraData?.challenge?.challenge_peak_group_id_list || [],
skip_node: parseInt(e.target.value) || 0
}
})
}
>
<option value="0">{transI18n("disableSkip")}</option>
<option value="1">{transI18n("skipNode1")}</option>
<option value="2">{transI18n("skipNode2")}</option>
</select>
</label>
</motion.div>
</div>
{/*EXTRA FEATURES */}
<div className="space-y-4">
<h3 className="text-lg font-bold">{transI18n("extraFeatures")}</h3>
{extraData?.setting?.hide_ui !== undefined && (
<motion.div className="form-control bg-base-200 p-4 rounded-xl shadow">
<label className="flex flex-wrap items-center cursor-pointer justify-start gap-3">
{extraData?.setting?.hide_ui
? <EyeOff className="text-warning" size={20} />
: <Eye className="text-success" size={20} />
}
<input <input
type="checkbox" type="checkbox"
className="toggle toggle-secondary" className="toggle toggle-secondary"
@@ -82,23 +231,27 @@ export default function ExtraSettingBar() {
onChange={(e) => onChange={(e) =>
setExtraData({ setExtraData({
...extraData, ...extraData,
setting: { ...extraData?.setting, hide_ui: e.target.checked } setting: {
hide_ui: e.target.checked,
censorship: extraData?.setting?.censorship || false,
cm: extraData?.setting?.cm || false,
first_person: extraData?.setting?.first_person || false
}
}) })
} }
/> />
<span className="label-text font-semibold">{transI18n("hideUI")}</span> <span className="label-text font-semibold">{transI18n("hideUI")}</span>
<div className="tooltip tooltip-info" data-tip={transI18n("detailHiddenUi")}>
<Info className="text-primary" size={20} />
</div>
</label> </label>
</motion.div> </motion.div>
)} )}
{/* Censorship */}
{extraData?.setting?.censorship !== undefined && ( {extraData?.setting?.censorship !== undefined && (
<motion.div <motion.div className="form-control bg-base-200 p-4 rounded-xl shadow">
whileHover={{ scale: 1.02 }} <label className="flex flex-wrap items-center cursor-pointer justify-start gap-3">
className="form-control bg-base-200 p-4 rounded-xl shadow" <ShieldBan className="text-error" size={20} />
>
<label className="flex flex-wrap items-center label cursor-pointer justify-start gap-3">
<ShieldBan className="text-error" size={20}/>
<input <input
type="checkbox" type="checkbox"
className="toggle toggle-accent" className="toggle toggle-accent"
@@ -106,15 +259,26 @@ export default function ExtraSettingBar() {
onChange={(e) => onChange={(e) =>
setExtraData({ setExtraData({
...extraData, ...extraData,
setting: { ...extraData?.setting, censorship: e.target.checked } setting: {
censorship: e.target.checked,
hide_ui: extraData?.setting?.hide_ui || false,
cm: extraData?.setting?.cm || false,
first_person: extraData?.setting?.first_person || false
}
}) })
} }
/> />
<span className="label-text font-semibold">{transI18n("disableCensorship")}</span> <span className="label-text font-semibold">{transI18n("disableCensorship")}</span>
<div className="tooltip tooltip-info" data-tip={transI18n("detailDisableCensorship")}>
<Info className="text-primary" size={20} />
</div>
</label> </label>
</motion.div> </motion.div>
)} )}
</div> </div>
</div> </div>
); )
} }

View File

@@ -108,7 +108,7 @@ export default function PeakBar() {
<SelectCustomText <SelectCustomText
customSet={PEAKEvent.map((peak) => ({ customSet={PEAKEvent.map((peak) => ({
id: peak.id, id: peak.id,
name: `${getLocaleName(locale, peak)} (${peak.id}) `, name: `${getLocaleName(locale, peak)} (${peak.id})`,
}))} }))}
excludeSet={[]} excludeSet={[]}
selectedCustomSet={peak_config.event_id.toString()} selectedCustomSet={peak_config.event_id.toString()}

View File

@@ -413,19 +413,19 @@ export default function RelicMaker() {
<div className="grid grid-cols-3 gap-1"> <div className="grid grid-cols-3 gap-1">
<button <button
onClick={() => handleSubStatChange(v.affixId, index, v.rollCount + 1, v.stepCount + 0)} onClick={() => handleSubStatChange(v.affixId, index, v.rollCount + 1, v.stepCount + 0)}
className="btn btn-sm bg-white text-slate-800 hover:bg-gray-200 border-0" className="btn btn-sm btn-info border-0"
> >
{calcAffixBonus(subAffixOptions[v.affixId], 0, v.rollCount + 1)}{mappingStats?.[subAffixOptions[v.affixId]?.property]?.unit || ""} {calcAffixBonus(subAffixOptions[v.affixId], 0, v.rollCount + 1)}{mappingStats?.[subAffixOptions[v.affixId]?.property]?.unit || ""}
</button> </button>
<button <button
onClick={() => handleSubStatChange(v.affixId, index, v.rollCount + 1, v.stepCount + 1)} onClick={() => handleSubStatChange(v.affixId, index, v.rollCount + 1, v.stepCount + 1)}
className="btn btn-sm bg-white text-slate-800 hover:bg-gray-200 border-0" className="btn btn-sm btn-info border-0"
> >
{calcAffixBonus(subAffixOptions[v.affixId], v.stepCount + 1, v.rollCount + 1)}{mappingStats?.[subAffixOptions[v.affixId]?.property]?.unit || ""} {calcAffixBonus(subAffixOptions[v.affixId], v.stepCount + 1, v.rollCount + 1)}{mappingStats?.[subAffixOptions[v.affixId]?.property]?.unit || ""}
</button> </button>
<button <button
onClick={() => handleSubStatChange(v.affixId, index, v.rollCount + 1, v.stepCount + 2)} onClick={() => handleSubStatChange(v.affixId, index, v.rollCount + 1, v.stepCount + 2)}
className="btn btn-sm bg-white text-slate-800 hover:bg-gray-200 border-0" className="btn btn-sm btn-info border-0"
> >
{calcAffixBonus(subAffixOptions[v.affixId], v.stepCount + 2, v.rollCount + 1)}{mappingStats?.[subAffixOptions[v.affixId]?.property]?.unit || ""} {calcAffixBonus(subAffixOptions[v.affixId], v.stepCount + 2, v.rollCount + 1)}{mappingStats?.[subAffixOptions[v.affixId]?.property]?.unit || ""}
</button> </button>
@@ -441,19 +441,19 @@ export default function RelicMaker() {
<div className="grid grid-cols-3 gap-1"> <div className="grid grid-cols-3 gap-1">
<button <button
onClick={() => handleSubStatChange(v.affixId, index, Math.max(v.rollCount - 1, 0), Math.max(v.stepCount, 0))} onClick={() => handleSubStatChange(v.affixId, index, Math.max(v.rollCount - 1, 0), Math.max(v.stepCount, 0))}
className="btn btn-sm bg-white text-slate-800 hover:bg-gray-200 border-0" className="btn btn-sm btn-info border-0"
> >
{calcAffixBonus(subAffixOptions[v.affixId], 0, Math.max(v.rollCount - 1, 0))}{mappingStats?.[subAffixOptions[v.affixId]?.property]?.unit || ""} {calcAffixBonus(subAffixOptions[v.affixId], 0, Math.max(v.rollCount - 1, 0))}{mappingStats?.[subAffixOptions[v.affixId]?.property]?.unit || ""}
</button> </button>
<button <button
onClick={() => handleSubStatChange(v.affixId, index, Math.max(v.rollCount - 1, 0), Math.max(v.stepCount - 1, 0))} onClick={() => handleSubStatChange(v.affixId, index, Math.max(v.rollCount - 1, 0), Math.max(v.stepCount - 1, 0))}
className="btn btn-sm bg-white text-slate-800 hover:bg-gray-200 border-0" className="btn btn-sm btn-info border-0"
> >
{calcAffixBonus(subAffixOptions[v.affixId], Math.max(v.stepCount - 1, 0), Math.max(v.rollCount - 1, 0))}{mappingStats?.[subAffixOptions[v.affixId]?.property]?.unit || ""} {calcAffixBonus(subAffixOptions[v.affixId], Math.max(v.stepCount - 1, 0), Math.max(v.rollCount - 1, 0))}{mappingStats?.[subAffixOptions[v.affixId]?.property]?.unit || ""}
</button> </button>
<button <button
onClick={() => handleSubStatChange(v.affixId, index, Math.max(v.rollCount - 1, 0), Math.max(v.stepCount - 2, 0))} onClick={() => handleSubStatChange(v.affixId, index, Math.max(v.rollCount - 1, 0), Math.max(v.stepCount - 2, 0))}
className="btn btn-sm bg-white text-slate-800 hover:bg-gray-200 border-0" className="btn btn-sm btn-info border-0"
> >
{calcAffixBonus(subAffixOptions[v.affixId], Math.max(v.stepCount - 2, 0), Math.max(v.rollCount - 1, 0))}{mappingStats?.[subAffixOptions[v.affixId]?.property]?.unit || ""} {calcAffixBonus(subAffixOptions[v.affixId], Math.max(v.stepCount - 2, 0), Math.max(v.rollCount - 1, 0))}{mappingStats?.[subAffixOptions[v.affixId]?.property]?.unit || ""}
</button> </button>

View File

@@ -4,6 +4,7 @@ import Select, { SingleValue } from 'react-select'
import Image from 'next/image' import Image from 'next/image'
import useLocaleStore from '@/stores/localeStore' import useLocaleStore from '@/stores/localeStore'
import ParseText from '../parseText' import ParseText from '../parseText'
import { themeColors } from '@/constant/constant'
export type SelectOption = { export type SelectOption = {
value: string value: string
@@ -12,8 +13,8 @@ export type SelectOption = {
} }
type SelectCustomProp = { type SelectCustomProp = {
customSet: SelectOption[] customSet: SelectOption[]
excludeSet: SelectOption[] excludeSet: SelectOption[]
selectedCustomSet: string selectedCustomSet: string
placeholder: string placeholder: string
setSelectedCustomSet: (value: string) => void setSelectedCustomSet: (value: string) => void
@@ -21,31 +22,54 @@ type SelectCustomProp = {
export default function SelectCustomImage({ customSet, excludeSet, selectedCustomSet, placeholder, setSelectedCustomSet }: SelectCustomProp) { export default function SelectCustomImage({ customSet, excludeSet, selectedCustomSet, placeholder, setSelectedCustomSet }: SelectCustomProp) {
const options: SelectOption[] = customSet const options: SelectOption[] = customSet
const { locale } = useLocaleStore() const { locale, theme } = useLocaleStore()
const c = themeColors[theme] || themeColors.winter
const customStyles = { const customStyles = {
option: (provided: any) => ({ option: (p: any, s: any) => ({
...provided, ...p,
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
gap: '8px', gap: '8px',
padding: '8px', padding: '8px 12px',
backgroundColor: 'transparent', backgroundColor: s.isFocused ? c.bgHover : c.bg,
color: c.text,
cursor: 'pointer'
}), }),
singleValue: (provided: any) => ({
...provided, singleValue: (p: any) => ({
...p,
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
gap: '8px', gap: '8px',
backgroundColor: 'transparent', color: c.text
}), }),
menuPortal: (provided: any) => ({ ...provided, zIndex: 9999 }),
menu: (provided: any) => ({ ...provided, zIndex: 9999 }) control: (p: any) => ({
...p,
backgroundColor: c.bg,
borderColor: c.border,
boxShadow: 'none'
}),
menu: (p: any) => ({
...p,
backgroundColor: c.bg,
color: c.text,
zIndex: 9999
}),
menuPortal: (p: any) => ({
...p,
zIndex: 9999
})
} }
const formatOptionLabel = (option: SelectOption) => ( const formatOptionLabel = (option: SelectOption) => (
<div className="flex items-center gap-1 w-full h-full z-50"> <div className="flex items-center gap-1 w-full h-full z-50">
<Image src={option.imageUrl} alt="" width={125} height={125} className="w-8 h-8 object-contain bg-warning-content rounded-full" /> <Image 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-warning-content' text={option.label} locale={locale} /> <ParseText className='font-bold' text={option.label} locale={locale} />
</div> </div>
) )
@@ -53,7 +77,8 @@ export default function SelectCustomImage({ customSet, excludeSet, selectedCusto
<Select <Select
options={options.filter(opt => !excludeSet.some(ex => ex.value === opt.value))} options={options.filter(opt => !excludeSet.some(ex => ex.value === opt.value))}
value={options.find(opt => { value={options.find(opt => {
return opt.value === selectedCustomSet}) || null} return opt.value === selectedCustomSet
}) || null}
onChange={(selected: SingleValue<SelectOption>) => { onChange={(selected: SingleValue<SelectOption>) => {
setSelectedCustomSet(selected?.value || '') setSelectedCustomSet(selected?.value || '')
}} }}

View File

@@ -2,6 +2,8 @@
'use client' 'use client'
import Select, { SingleValue } from 'react-select' import Select, { SingleValue } from 'react-select'
import { replaceByParam } from '@/helper' import { replaceByParam } from '@/helper'
import useLocaleStore from '@/stores/localeStore'
import { themeColors } from '@/constant/constant'
export type SelectOption = { export type SelectOption = {
id: string id: string
@@ -20,29 +22,52 @@ type SelectCustomProp = {
export default function SelectCustomText({ customSet, excludeSet, selectedCustomSet, placeholder, setSelectedCustomSet }: SelectCustomProp) { export default function SelectCustomText({ customSet, excludeSet, selectedCustomSet, placeholder, setSelectedCustomSet }: SelectCustomProp) {
const options: SelectOption[] = customSet const options: SelectOption[] = customSet
const { theme } = useLocaleStore()
const c = themeColors[theme] || themeColors.winter
const customStyles = { const customStyles = {
option: (provided: any) => ({ option: (p: any, s: any) => ({
...provided, ...p,
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
gap: '8px', gap: '8px',
padding: '8px', padding: '8px 12px',
backgroundColor: 'transparent', backgroundColor: s.isFocused ? c.bgHover : c.bg,
color: c.text,
cursor: 'pointer'
}), }),
singleValue: (provided: any) => ({
...provided, singleValue: (p: any) => ({
...p,
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
gap: '8px' gap: '8px',
color: c.text
}), }),
control: (p: any) => ({
...p,
backgroundColor: c.bg,
borderColor: c.border,
boxShadow: 'none'
}),
menuPortal: (provided: any) => ({ ...provided, zIndex: 9999 }), menu: (p: any) => ({
menu: (provided: any) => ({ ...provided, zIndex: 9999 }) ...p,
backgroundColor: c.bg,
color: c.text,
zIndex: 9999
}),
menuPortal: (p: any) => ({
...p,
zIndex: 9999
})
} }
const formatOptionLabel = (option: SelectOption) => ( const formatOptionLabel = (option: SelectOption) => (
<div className="flex flex-col gap-1 w-full h-full z-50 text-warning-content "> <div className="flex flex-col gap-1 w-full h-full z-50">
<div <div
className="font-bold text-lg" className="font-bold text-lg"
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{

View File

@@ -17,7 +17,7 @@ export const ThemeProvider = ({ children }: PropsWithChildren) => {
const storedTheme = localStorage.getItem("theme"); const storedTheme = localStorage.getItem("theme");
if (storedTheme) setTheme(storedTheme); if (storedTheme) setTheme(storedTheme);
} }
}, []); }, [setTheme]);
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const changeTheme = (nextTheme: string | null) => { const changeTheme = (nextTheme: string | null) => {

View File

@@ -169,3 +169,31 @@ export const ratioStats = [
"SpeedAddedRatio", "SpeedAddedRatio",
] ]
export const themeColors: Record<string, { bg: string; bgHover: string; text: string; border: string }> = {
winter: {
bg: '#ffffff',
bgHover: '#f1f5f9',
text: '#3a4f6b',
border: '#cbd5e1'
},
night: {
bg: '#1d232a',
bgHover: '#2a323c',
text: '#cbcdd1',
border: '#3f3f46'
},
cupcake: {
bg: '#faf7f5',
bgHover: '#f3eae6',
text: '#281333',
border: '#e5d3cb'
},
coffee: {
bg: '#20161f',
bgHover: '#2a1d29',
text: '#c4a051',
border: '#3a2a36'
}
}

View File

@@ -1,21 +1,21 @@
export interface ExtraData { export interface ExtraData {
theory_craft: { theory_craft?: {
hp: Record<string, number[]> hp: Record<string, number[]>
cycle_count: number cycle_count: number
mode: boolean mode: boolean
} }
setting: { setting?: {
censorship: boolean censorship: boolean
cm: boolean cm: boolean
first_person: boolean first_person: boolean
hide_ui: boolean hide_ui: boolean
} }
challenge: { challenge?: {
skip_node: number skip_node: number
challenge_peak_group_id: number challenge_peak_group_id: number
challenge_peak_group_id_list: number[] challenge_peak_group_id_list: number[]
} }
multi_path: { multi_path?: {
main: number main: number
march_7: number march_7: number
multi_path_main: number[] multi_path_main: number[]