fix: redesign relic gear UI, click-to-pick LC modal, fix showcase export aspect ratio, clean card buttons and add top-bar All E0S1 button

This commit is contained in:
CAtlas0784 committed 2026-10-17 03:20:18 +07:00
1 parent 42efabfe9a
commit 030d02f7a1
4 files changed
+639 -90

No files matched your search

+403 -53
View File
@@ -546,7 +546,7 @@ function applyTranslations() {
document.querySelectorAll('[data-i18n]').forEach(elem => {
const key = elem.getAttribute('data-i18n');
const val = t(key);
if (val) elem.textContent = val;
if (val && val !== key) elem.textContent = val;
});
document.querySelectorAll('[data-i18n-placeholder]').forEach(elem => {
@@ -1129,37 +1129,9 @@ function renderAvatarsGrid() {
<span class="avatar-lvl">Lv. ${item.level}</span>
<span class="avatar-eidolon ${item.rank === 6 ? 'e6' : ''}">E${item.rank}</span>
</div>
<div style="display: flex; gap: 0.3rem; align-items: center;">
<button type="button" class="btn btn-sm btn-card-e0s1" title="ตั้งค่า E0S1 ทันที">⚡ E0S1</button>
<button type="button" class="btn btn-sm btn-card-e6s5" title="ตั้งค่า E6S5 เต็มพลัง">⭐ E6S5</button>
<button type="button" class="btn btn-sm btn-secondary btn-card-showcase" title="ดูการ์ดแสดงผล Build">🖼️</button>
</div>
</div>
`;
// Quick Showcase Card Button
const btnShowcase = card.querySelector('.btn-card-showcase');
btnShowcase?.addEventListener('click', (e) => {
e.stopPropagation();
openAvatarDetailModal(item);
const showcaseTabBtn = document.querySelector('.modal-subtab-btn[data-modaltab="modaltab-showcase"]');
if (showcaseTabBtn) showcaseTabBtn.click();
});
// Quick E0S1 Button on Card Outside
const btnE0S1 = card.querySelector('.btn-card-e0s1');
btnE0S1?.addEventListener('click', async (e) => {
e.stopPropagation();
await setAvatarE0S1(item.avatar_id);
});
// Quick E6S5 Button on Card Outside
const btnE6S5 = card.querySelector('.btn-card-e6s5');
btnE6S5?.addEventListener('click', async (e) => {
e.stopPropagation();
await setAvatarE6S5(item.avatar_id);
});
// Clicking card opens full character build modal
card.addEventListener('click', () => {
openAvatarDetailModal(item);
@@ -1899,13 +1871,52 @@ function renderModalOverviewTab(avatarId) {
}
}
// Render Relics 6 Slots Tab
// Render Relics & Gear Tab (6 Slots + Equipped Lightcone)
function renderModalRelicsTab(avatarId) {
const aid = parseInt(avatarId, 10);
const meta = getAvatarInfo(aid);
if (!el.modalRelicsSlotsGrid) return;
el.modalRelicsSlotsGrid.innerHTML = '';
// 1. Equipped Lightcone in Relics Tab
const lc = (state.fullFreesrData?.lightcones || []).find(l => l.equip_avatar === aid);
const lcId = lc?.item_id || SIGNATURE_LIGHTCONES[aid] || 21001;
const lcInfo = getLightconeInfo(lcId);
const lcRank = lc?.rank || 5;
const lcLevel = lc?.level || 80;
const lcMeta = LIGHTCONE_METADATA[lcId] || {};
const lcThumbEl = document.getElementById('relics-tab-lc-thumb');
const lcNameEl = document.getElementById('relics-tab-lc-name');
const lcStarsEl = document.getElementById('relics-tab-lc-stars');
const lcRankEl = document.getElementById('relics-tab-lc-rank');
const lcLvlEl = document.getElementById('relics-tab-lc-level');
const lcStatsEl = document.getElementById('relics-tab-lc-stats');
const lcBox = document.getElementById('relics-equipped-lc-box');
if (lcThumbEl) {
lcThumbEl.src = getLightconeArtSrc(lcId);
lcThumbEl.onerror = function() {
const fb = getLightconeArtFallback(lcId);
if (this.src !== fb) this.src = fb;
else this.src = './assets/logo.png';
};
}
if (lcNameEl) lcNameEl.textContent = lcInfo.name || `Light Cone ${lcId}`;
if (lcStarsEl) lcStarsEl.textContent = '★'.repeat(lcInfo.rarity || 5);
if (lcRankEl) lcRankEl.textContent = `S${lcRank}`;
if (lcLvlEl) lcLvlEl.textContent = `Lv. ${lcLevel} / 80`;
if (lcStatsEl) {
const hp = lcMeta.hp || (lcMeta.rarity === 5 ? 1164 : (lcMeta.rarity === 4 ? 953 : 846));
const atk = lcMeta.atk || (lcMeta.rarity === 5 ? 582 : (lcMeta.rarity === 4 ? 423 : 317));
const def = lcMeta.def || (lcMeta.rarity === 5 ? 529 : (lcMeta.rarity === 4 ? 396 : 264));
lcStatsEl.textContent = `HP ${hp} / ATK ${atk} / DEF ${def}`;
}
if (lcBox) {
lcBox.onclick = () => openLightconePicker(aid);
}
// 2. Relic Slots
const allRelics = state.fullFreesrData?.relics || [];
const equippedRelics = allRelics.filter(r => r.equip_avatar === aid);
@@ -1920,34 +1931,36 @@ function renderModalRelicsTab(avatarId) {
const equipped = equippedRelics.find(r => (r.relic_id % 10) === slot);
const box = document.createElement('div');
box.className = 'relic-slot-box';
if (equipped) {
const setId = Math.floor((equipped.relic_id % 10000) / 10);
const rInfo = getRelicSetInfo(setId);
const mainAffixName = getRelicMainAffixName(slot, equipped.main_affix_id);
box.className = 'relic-slot-box equipped';
box.innerHTML = `
<div class="relic-slot-icon-wrap">
<img src="https://raw.githubusercontent.com/Mar-7th/StarRailRes/master/icon/relic/${setId}.png"
onerror="this.src='./assets/logo.png'" alt="Relic" style="width: 100%; height: 100%; object-fit: contain;">
<span class="relic-slot-badge">Slot ${slot}</span>
<span class="relic-slot-badge">${slotInfo.icon} ${slotInfo.name.split(' ')[0]}</span>
</div>
<div class="relic-slot-content">
<div class="relic-slot-title" title="${rInfo.name}">${slotInfo.icon} ${rInfo.name}</div>
<div class="relic-slot-main">✦ ${mainAffixName} (+${equipped.level})</div>
<div class="relic-slot-subs">${equipped.sub_affixes?.length || 4} ออปชั่นรอง (Max Level 15)</div>
</div>
<div class="relic-slot-actions">
<button type="button" class="btn btn-sm btn-primary btn-slot-change" style="font-size: 0.725rem; padding: 0.25rem 0.5rem;">🔄 เปลี่ยน</button>
<button type="button" class="btn btn-sm btn-secondary btn-slot-unequip" style="font-size: 0.725rem; padding: 0.25rem 0.5rem; color: var(--accent-red);">✕ ถอด</button>
<div style="display: flex; justify-content: space-between; align-items: center; padding-right: 1.5rem;">
<span class="relic-slot-title" title="${rInfo.name}">${rInfo.name}</span>
<span class="relic-slot-lvl">+15</span>
</div>
<div class="relic-slot-main">✦ ${mainAffixName}</div>
<div class="relic-slot-subs">${equipped.sub_affixes?.length || 4} ออปชั่นรอง (คลิกเพื่อเลือกเปลี่ยน)</div>
</div>
<button type="button" class="btn-slot-unequip-corner" title="ถอดชิ้นนี้">✕</button>
`;
box.querySelector('.btn-slot-change').addEventListener('click', () => {
box.addEventListener('click', () => {
openSlotPicker(aid, slot);
});
box.querySelector('.btn-slot-unequip').addEventListener('click', async () => {
box.querySelector('.btn-slot-unequip-corner')?.addEventListener('click', async (e) => {
e.stopPropagation();
equipped.equip_avatar = 0;
if (state.isConnected) await saveFreesrChangesToServer();
renderModalRelicsTab(aid);
@@ -1956,20 +1969,20 @@ function renderModalRelicsTab(avatarId) {
showToast(`ถอดรีลิกส์ ${slotInfo.name} เรียบร้อย`, 'info');
});
} else {
box.className = 'relic-slot-box empty';
box.innerHTML = `
<div class="relic-slot-icon-wrap" style="opacity: 0.5;">
<div class="relic-slot-icon-wrap" style="opacity: 0.4;">
<span style="font-size: 1.5rem;">${slotInfo.icon}</span>
<span class="relic-slot-badge">Slot ${slot}</span>
<span class="relic-slot-badge">${slotInfo.name.split(' ')[0]}</span>
</div>
<div class="relic-slot-content">
<div class="relic-slot-title" style="color: var(--text-muted);">${slotInfo.name}</div>
<div class="relic-slot-subs" style="color: var(--text-muted);">ยังไม่ได้ติดตั้ง (แนะนำ: ${slotInfo.defaultStat})</div>
</div>
<div class="relic-slot-actions">
<button type="button" class="btn btn-sm btn-primary btn-slot-equip" style="font-size: 0.725rem; padding: 0.25rem 0.5rem;">➕ สวมใส่</button>
<div class="relic-slot-main" style="color: var(--text-muted); font-size: 0.775rem;">ยังไม่ได้สวมใส่</div>
<div class="relic-slot-subs" style="color: var(--accent-cyan); font-size: 0.725rem;">+ คลิกเพื่อเลือกหรือสร้าง (แนะนำ: ${slotInfo.defaultStat})</div>
</div>
`;
box.querySelector('.btn-slot-equip').addEventListener('click', () => {
box.addEventListener('click', () => {
openSlotPicker(aid, slot);
});
}
@@ -2137,6 +2150,305 @@ async function quickGenSlotRelic(avatarId, slotNum) {
showToast(`สร้างและติดตั้งรีลิกส์แนะนำช่อง ${slotNum} สำเร็จ!`, 'success');
}
// Open Lightcone Picker Modal
function openLightconePicker(avatarId) {
state.lcPickerAvatarId = parseInt(avatarId, 10);
const aid = state.lcPickerAvatarId;
const meta = getAvatarInfo(aid);
const modal = document.getElementById('modal-select-lightcone');
if (!modal) return;
const titleEl = document.getElementById('modal-lc-picker-title');
if (titleEl) {
titleEl.textContent = `🗡️ เลือกสวมใส่กรวยแสง: ${meta.name}`;
}
const sigLcId = SIGNATURE_LIGHTCONES[aid];
const sigBox = document.getElementById('lc-picker-signature-box');
const sigThumb = document.getElementById('lc-picker-sig-thumb');
const sigName = document.getElementById('lc-picker-sig-name');
const btnQuickSig = document.getElementById('btn-lc-picker-quick-sig');
if (sigLcId && LIGHTCONE_METADATA[sigLcId]) {
const sInfo = getLightconeInfo(sigLcId);
if (sigBox) sigBox.style.display = 'flex';
if (sigThumb) {
sigThumb.src = getLightconeArtSrc(sigLcId);
sigThumb.onerror = function() { this.src = getLightconeArtFallback(sigLcId); };
}
if (sigName) sigName.textContent = `${sInfo.name} (★${sInfo.rarity} ${sInfo.path})`;
if (btnQuickSig) {
btnQuickSig.onclick = async () => {
const rank = parseInt(document.getElementById('lc-picker-rank')?.value || '5', 10);
await equipLightconeToAvatar(aid, sigLcId, rank, 80);
modal.classList.remove('active');
};
}
} else if (sigBox) {
sigBox.style.display = 'none';
}
const pathSelect = document.getElementById('lc-picker-filter-path');
if (pathSelect) pathSelect.value = 'same';
const searchInput = document.getElementById('lc-picker-search');
if (searchInput) searchInput.value = '';
renderLightconePickerGrid();
modal.classList.add('active');
}
// Render Lightcone Picker Grid
function renderLightconePickerGrid() {
const listEl = document.getElementById('lc-picker-list');
if (!listEl) return;
listEl.innerHTML = '';
const aid = state.lcPickerAvatarId;
const avatarMeta = getAvatarInfo(aid);
const search = document.getElementById('lc-picker-search')?.value.trim().toLowerCase() || '';
const pathFilter = document.getElementById('lc-picker-filter-path')?.value || 'same';
const rank = parseInt(document.getElementById('lc-picker-rank')?.value || '5', 10);
const currentLc = (state.fullFreesrData?.lightcones || []).find(l => l.equip_avatar === aid);
const allIds = Object.keys(LIGHTCONE_METADATA).map(id => parseInt(id, 10)).sort((a, b) => {
const metaA = LIGHTCONE_METADATA[a];
const metaB = LIGHTCONE_METADATA[b];
if (metaB.rarity !== metaA.rarity) return metaB.rarity - metaA.rarity;
return b - a;
});
const filtered = allIds.filter(id => {
const meta = LIGHTCONE_METADATA[id];
const info = getLightconeInfo(id);
// Path match
if (pathFilter === 'same' && meta.path !== avatarMeta.path) return false;
if (pathFilter !== 'all' && pathFilter !== 'same' && meta.path !== pathFilter) return false;
// Search match
if (search) {
const matchName = info.name.toLowerCase().includes(search);
const matchId = String(id).includes(search);
if (!matchName && !matchId) return false;
}
return true;
});
if (filtered.length === 0) {
listEl.innerHTML = `<div style="grid-column: 1/-1; text-align: center; color: var(--text-muted); padding: 2rem;">ไม่พบกรวยแสงตามเงื่อนไขที่ค้นหา</div>`;
return;
}
filtered.forEach(id => {
const meta = LIGHTCONE_METADATA[id];
const info = getLightconeInfo(id);
const isCurrent = currentLc?.item_id === id;
const isSig = SIGNATURE_LIGHTCONES[aid] === id;
const card = document.createElement('div');
card.className = `lc-picker-card rarity-${info.rarity} ${isCurrent ? 'active-equipped' : ''}`;
let badgeHtml = '';
if (isCurrent) {
badgeHtml = `<span style="font-size: 0.65rem; background: var(--accent-gold); color: #000; font-weight: 800; padding: 0.1rem 0.4rem; border-radius: 4px;">สวมใส่อยู่</span>`;
} else if (isSig) {
badgeHtml = `<span style="font-size: 0.65rem; background: rgba(251, 191, 36, 0.2); border: 1px solid var(--accent-gold); color: var(--accent-gold); font-weight: 800; padding: 0.1rem 0.4rem; border-radius: 4px;">ประจำตัว</span>`;
}
card.innerHTML = `
<img class="lc-picker-thumb" src="${getLightconeArtSrc(id)}" onerror="this.src='${getLightconeArtFallback(id)}'" alt="${info.name}">
<div style="flex: 1; min-width: 0;">
<div style="display: flex; justify-content: space-between; align-items: center; gap: 0.35rem;">
<span style="font-weight: 800; font-size: 0.85rem; color: #fff; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;" title="${info.name}">${info.name}</span>
${badgeHtml}
</div>
<div style="font-size: 0.725rem; color: var(--accent-gold); margin-top: 0.2rem;">${'★'.repeat(info.rarity || 5)}</div>
<div style="font-size: 0.7rem; color: var(--accent-cyan); margin-top: 0.15rem;">
${getPathIconHtml(meta.path, 14)} ${info.path}
</div>
</div>
`;
card.addEventListener('click', async () => {
await equipLightconeToAvatar(aid, id, rank, 80);
document.getElementById('modal-select-lightcone')?.classList.remove('active');
});
listEl.appendChild(card);
});
}
// Equip Lightcone directly to Avatar
async function equipLightconeToAvatar(avatarId, lcId, rank = 5, level = 80) {
const aid = parseInt(avatarId, 10);
const id = parseInt(lcId, 10);
const meta = getAvatarInfo(aid);
const lcInfo = getLightconeInfo(id);
if (!state.fullFreesrData) state.fullFreesrData = { avatars: {}, lightcones: [], relics: [] };
state.fullFreesrData.lightcones = state.fullFreesrData.lightcones || [];
// 1. Unassign other lightcones from this avatar
state.fullFreesrData.lightcones.forEach(lc => {
if (lc.equip_avatar === aid) lc.equip_avatar = 0;
});
// 2. Find or create lightcone entry
let targetLc = state.fullFreesrData.lightcones.find(lc => lc.item_id === id && (!lc.equip_avatar || lc.equip_avatar === 0));
if (!targetLc) {
targetLc = state.fullFreesrData.lightcones.find(lc => lc.item_id === id);
if (targetLc) {
targetLc.equip_avatar = aid;
targetLc.rank = rank;
targetLc.level = level;
targetLc.promotion = 6;
} else {
let maxUid = 1000;
state.fullFreesrData.lightcones.forEach(l => {
if (l.unique_id >= maxUid) maxUid = l.unique_id + 1;
});
targetLc = {
unique_id: maxUid,
internal_uid: state.fullFreesrData.lightcones.length,
item_id: id,
level: level,
rank: rank,
promotion: 6,
equip_avatar: aid
};
state.fullFreesrData.lightcones.push(targetLc);
}
} else {
targetLc.equip_avatar = aid;
targetLc.rank = rank;
targetLc.level = level;
targetLc.promotion = 6;
}
if (state.isConnected) {
await saveFreesrChangesToServer().catch(() => {});
}
renderModalOverviewTab(aid);
renderModalRelicsTab(aid);
renderShowcaseCard(aid);
renderLightconesGrid();
showToast(`🗡️ สวมใส่ ${lcInfo.name} (S${rank}) ให้กับ ${meta.name} เรียบร้อย!`, 'success');
}
// Unequip Lightcone from Avatar
async function unequipLightconeFromAvatar(avatarId) {
const aid = parseInt(avatarId, 10);
const meta = getAvatarInfo(aid);
if (state.fullFreesrData?.lightcones) {
state.fullFreesrData.lightcones.forEach(lc => {
if (lc.equip_avatar === aid) lc.equip_avatar = 0;
});
if (state.isConnected) await saveFreesrChangesToServer().catch(() => {});
renderModalOverviewTab(aid);
renderModalRelicsTab(aid);
renderShowcaseCard(aid);
renderLightconesGrid();
showToast(`ถอดกรวยแสงออกจาก ${meta.name} เรียบร้อย`, 'info');
}
}
// Quick Set All Avatars to E0S1 Standard (Lv.80, Prom 6, E0, S1 Signature LC)
async function quickSetAllAvatarsE0S1() {
try {
if (!state.fullFreesrData) {
state.fullFreesrData = { avatars: {}, lightcones: [], relics: [] };
}
state.fullFreesrData.avatars = state.fullFreesrData.avatars || {};
state.fullFreesrData.lightcones = state.fullFreesrData.lightcones || [];
const allAvatarIds = Object.keys(AVATARS_DATA).map(id => parseInt(id, 10));
let nextUniqueId = 1000;
state.fullFreesrData.lightcones.forEach(lc => {
if (lc.unique_id >= nextUniqueId) nextUniqueId = lc.unique_id + 1;
});
for (const aid of allAvatarIds) {
if (!state.fullFreesrData.avatars[aid]) {
state.fullFreesrData.avatars[aid] = {
owner_uid: 0,
avatar_id: aid,
level: 80,
promotion: 6,
techniques: [],
sp_max: 140,
sp_value: 70,
data: {
rank: 0,
skills: {
[`${aid}001`]: 6,
[`${aid}002`]: 10,
[`${aid}003`]: 10,
[`${aid}004`]: 10,
[`${aid}007`]: 1,
[`${aid}101`]: 1,
[`${aid}102`]: 1,
[`${aid}103`]: 1,
[`${aid}201`]: 1
},
skills_by_anchor_type: {
"1": 6, "2": 10, "3": 10, "4": 10, "5": 1, "6": 1, "7": 1, "8": 1, "9": 1
}
}
};
} else {
const av = state.fullFreesrData.avatars[aid];
av.level = 80;
av.promotion = 6;
av.data = av.data || {};
av.data.rank = 0;
if (!av.sp_value) av.sp_value = 70;
}
// Equip S1 Signature Lightcone
const sigLcId = SIGNATURE_LIGHTCONES[aid] || 21001;
let existingLc = state.fullFreesrData.lightcones.find(l => l.equip_avatar === aid);
if (existingLc) {
existingLc.item_id = sigLcId;
existingLc.rank = 1;
existingLc.level = 80;
existingLc.promotion = 6;
} else {
state.fullFreesrData.lightcones.push({
unique_id: nextUniqueId++,
internal_uid: state.fullFreesrData.lightcones.length,
item_id: sigLcId,
level: 80,
promotion: 6,
rank: 1,
equip_avatar: aid
});
}
}
initAvatarsList();
state.avatarsList.forEach(a => {
a.level = 80;
a.promotion = 6;
a.rank = 0;
});
if (state.isConnected) {
await saveFreesrChangesToServer().catch(() => {});
}
renderAvatarsGrid();
if (el.statAvatars) el.statAvatars.textContent = Object.keys(state.fullFreesrData.avatars).length;
showToast('⚡ ปรับตัวละครทุกตัวเป็น E0S1 พร้อมกรวยแสงประจำตัวเรียบร้อยแล้ว!', 'success');
} catch (err) {
showToast(err.message, 'error');
}
}
// Render Skills Tab with real StarRailRes descriptions & icons
async function renderModalSkillsTab(avatarId) {
const aid = String(avatarId);
@@ -2733,7 +3045,7 @@ function renderShowcaseCard(avatarId) {
activeSetBadges.forEach(sb => {
setsHtml += `
<div class="showcase-set-item">
<span style="color: #e2e8f0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 170px;">${sb.name}</span>
<span style="color: #e2e8f0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1; min-width: 0; margin-right: 0.5rem;" title="${sb.name}">${sb.name}</span>
<span class="showcase-set-badge">${sb.count}-PC</span>
</div>
`;
@@ -2769,8 +3081,8 @@ function renderShowcaseCard(avatarId) {
</div>
<div class="showcase-relic-info">
<div class="showcase-relic-main-stat">
<span style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 130px;">${rInfo.name}</span>
<span style="color: var(--accent-cyan); font-weight: 800;">${slotInfo.stat}</span>
<span style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1; min-width: 0; margin-right: 0.5rem;" title="${rInfo.name}">${rInfo.name}</span>
<span style="color: var(--accent-cyan); font-weight: 800; flex-shrink: 0;">${slotInfo.stat}</span>
</div>
<div class="showcase-relic-subs">
<span>CRIT Rate +${(3.2 + slot * 1.5).toFixed(1)}%${rollBadge(slot % 2 === 0 ? 3 : 2)}</span>
@@ -2989,12 +3301,22 @@ async function exportShowcaseImage(avatarId) {
if (typeof html2canvas !== 'undefined') {
try {
const origWidth = showcaseEl.style.width;
const origMinWidth = showcaseEl.style.minWidth;
showcaseEl.style.width = '1050px';
showcaseEl.style.minWidth = '1050px';
const canvas = await html2canvas(showcaseEl, {
useCORS: true,
allowTaint: true,
scale: 2,
backgroundColor: '#0c1222'
backgroundColor: '#0c1222',
windowWidth: 1280
});
showcaseEl.style.width = origWidth;
showcaseEl.style.minWidth = origMinWidth;
const dataUrl = canvas.toDataURL('image/png');
const a = document.createElement('a');
a.href = dataUrl;
@@ -4344,9 +4666,37 @@ function setupEvents() {
setupSkillStepper(el.rangeSkillTalent, el.valSkillTalent, el.btnStepDecTalent, el.btnStepIncTalent, el.btnSkillMaxTalent, 10);
// Overview Tab Quick Navigation Cards
el.overviewLcCard?.addEventListener('click', () => switchModalSubtab('modaltab-lightcone'));
el.overviewLcCard?.addEventListener('click', () => {
if (state.selectedAvatarDetail) openLightconePicker(state.selectedAvatarDetail.avatar_id);
});
el.overviewRelicsCard?.addEventListener('click', () => switchModalSubtab('modaltab-relics'));
// Lightcone Picker Modal Controls
document.getElementById('btn-close-lc-picker')?.addEventListener('click', () => {
document.getElementById('modal-select-lightcone')?.classList.remove('active');
});
document.getElementById('btn-lc-picker-close')?.addEventListener('click', () => {
document.getElementById('modal-select-lightcone')?.classList.remove('active');
});
document.getElementById('lc-picker-search')?.addEventListener('input', () => {
renderLightconePickerGrid();
});
document.getElementById('lc-picker-filter-path')?.addEventListener('change', () => {
renderLightconePickerGrid();
});
document.getElementById('lc-picker-rank')?.addEventListener('change', () => {
renderLightconePickerGrid();
});
document.getElementById('btn-lc-picker-unequip')?.addEventListener('click', async () => {
if (state.lcPickerAvatarId) {
await unequipLightconeFromAvatar(state.lcPickerAvatarId);
}
document.getElementById('modal-select-lightcone')?.classList.remove('active');
});
// Top Bar Characters Tab Quick Actions
document.getElementById('btn-all-avatars-e0s1')?.addEventListener('click', quickSetAllAvatarsE0S1);
// Relic Tab Controls in Modal
el.btnModalRelicsAuto?.addEventListener('click', async () => {
if (!state.selectedAvatarDetail) return;
+50 -2
View File
@@ -268,7 +268,31 @@ export const TRANSLATIONS = {
"Elation": "ปีติ"
},
"settingsSaved": "บันทึกการตั้งค่าสำเร็จ!",
"settingsReset": "คืนค่าเริ่มต้นเรียบร้อย"
"settingsReset": "คืนค่าเริ่มต้นเรียบร้อย",
"qaLineupE0S1Title": "ปรับทีมเป็น E0S1",
"qaLineupE0S1Desc": "ตัวละครในทีมเป็น E0 พร้อม LC S1",
"qaLineupRelicsTitle": "ใส่อุปกรณ์ทีมครบชุด",
"qaLineupRelicsDesc": "ใส่รีลิกส์ +15 ครบ 4 คนในทีม",
"lblPersistenceInfo": "ระบบจัดการไฟล์เซฟและข้อมูลฝั่งเซิร์ฟเวอร์",
"modalTabOverview": "ภาพรวม",
"modalTabRelics": "รีลิกส์ & อุปกรณ์",
"overviewStatsTitle": "⚔️ ค่าพลังการต่อสู้ (Combat Stats)",
"overviewStatsNote": "รวมโบนัสกรวยแสงและรีลิกส์",
"overviewEquippedLc": "🗡️ กรวยแสงที่สวมใส่",
"overviewEquippedRelics": "🛡️ รีลิกส์และเซ็ต",
"modalRelicsTitle": "🛡️ สวมใส่รีลิกส์ (Relics 6 ชิ้น)",
"modalRelicsSubtitle": "คลิกแต่ละช่องเพื่อเลือกเปลี่ยน หรือสวมใส่เซ็ตแนะนำ",
"btnAutoEquipRelics": "⚡ สวมใส่เซ็ตแนะนำครบชุด",
"btnClearAllRelics": "🧹 ถอดทั้งหมด",
"activeSetEffects": "✨ เอฟเฟกต์เซ็ตที่เปิดใช้งาน",
"modalColLevel": "เลเวล",
"lblPromotion": "ขั้นเลื่อนขั้น",
"slotPickerTitle": "🛡️ เลือกสวมใส่รีลิกส์",
"quickGenRecTitle": "⚡ สร้างชิ้นแนะนำสำหรับช่องนี้",
"quickGenRecDesc": "สร้างรีลิกส์ 5★ +15 ตรงเซ็ตและสเตตัสหลักที่ดีที่สุด",
"btnGenAndEquip": "สร้าง & สวมใส่",
"selectFromInventory": "เลือกจากคลังเก็บของ (ตรงช่อง)",
"btnAllE0S1": "⚡ ปรับ E0S1 ทุกตัว"
},
"en": {
"ahaGlobalDesc": "Enable server-wide Aeon ★ Aha special trait",
@@ -520,7 +544,31 @@ export const TRANSLATIONS = {
"Elation": "Elation"
},
"settingsSaved": "Settings saved successfully!",
"settingsReset": "Reset to default settings"
"settingsReset": "Reset to default settings",
"qaLineupE0S1Title": "Set Team to E0S1",
"qaLineupE0S1Desc": "Team characters to E0 with S1 LC",
"qaLineupRelicsTitle": "Auto-Equip Team Relics",
"qaLineupRelicsDesc": "Equip +15 relics for 4 teammates",
"lblPersistenceInfo": "Server save file & data management",
"modalTabOverview": "Overview",
"modalTabRelics": "Relics & Gear",
"overviewStatsTitle": "⚔️ Combat Stats",
"overviewStatsNote": "Includes Lightcone & Relic bonuses",
"overviewEquippedLc": "🗡️ Equipped Lightcone",
"overviewEquippedRelics": "🛡️ Relics & Set Bonuses",
"modalRelicsTitle": "🛡️ Equip Relics (6 Slots)",
"modalRelicsSubtitle": "Click each slot to change or equip recommended sets",
"btnAutoEquipRelics": "⚡ Equip Recommended Sets",
"btnClearAllRelics": "🧹 Unequip All",
"activeSetEffects": "✨ Active Set Bonuses",
"modalColLevel": "Level",
"lblPromotion": "Ascension Rank",
"slotPickerTitle": "🛡️ Select Relic to Equip",
"quickGenRecTitle": "⚡ Generate Recommended Piece",
"quickGenRecDesc": "Generate 5★ +15 with best set & main stat",
"btnGenAndEquip": "Generate & Equip",
"selectFromInventory": "Select from Inventory",
"btnAllE0S1": "⚡ All E0S1"
},
"zh-CN": {
"ahaGlobalDesc": "全服启用星神·阿哈特殊被动",