From 030d02f7a13d4ff3700f4029fcda4fa723e7edfa Mon Sep 17 00:00:00 2001 From: CAtlas0784 Date: Sat, 17 Oct 2026 03:20:18 +0700 Subject: [PATCH] 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 --- css/styles.css | 118 ++++++++++--- index.html | 103 ++++++++++- js/app.js | 456 +++++++++++++++++++++++++++++++++++++++++++------ js/i18n.js | 52 +++++- 4 files changed, 639 insertions(+), 90 deletions(-) diff --git a/css/styles.css b/css/styles.css index 7ffa293..0fb9a57 100644 --- a/css/styles.css +++ b/css/styles.css @@ -1471,20 +1471,16 @@ input:checked + .slider:before { background: linear-gradient(135deg, #0d111a 0%, #151a27 50%, #0f131f 100%); border: 1px solid rgba(255, 255, 255, 0.12); border-radius: 16px; - padding: 1.25rem; + padding: 1.5rem; box-shadow: 0 16px 40px rgba(0, 0, 0, 0.7), inset 0 0 40px rgba(56, 189, 248, 0.03); display: grid; - grid-template-columns: 240px 1.15fr 1.35fr; + grid-template-columns: 360px 320px 370px; gap: 1.25rem; position: relative; overflow: hidden; -} - -@media (max-width: 960px) { - .showcase-card-layout { - grid-template-columns: 1fr; - gap: 1.25rem; - } + min-width: 1050px; + width: 1050px; + box-sizing: border-box; } .showcase-splash-col { @@ -1495,7 +1491,8 @@ input:checked + .slider:before { display: flex; flex-direction: column; justify-content: flex-end; - min-height: 380px; + min-height: 520px; + width: 360px; border: 1px solid rgba(255, 255, 255, 0.08); } @@ -2058,26 +2055,58 @@ input:checked + .slider:before { /* Relics 6 Slots Layout */ .relics-slots-grid { display: grid; - grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); gap: 0.85rem; } .relic-slot-box { - background: rgba(18, 24, 38, 0.65); + background: rgba(18, 24, 38, 0.7); border: 1px solid var(--border-color); border-radius: var(--radius-md); - padding: 0.85rem; + padding: 0.85rem 1rem; display: flex; gap: 0.85rem; align-items: center; position: relative; + cursor: pointer; transition: all 0.2s ease; } +.relic-slot-box.empty { + border: 1px dashed rgba(255, 255, 255, 0.18); + background: rgba(14, 19, 31, 0.5); +} + .relic-slot-box:hover { - border-color: rgba(56, 189, 248, 0.5); - background: rgba(18, 24, 38, 0.85); - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4); + border-color: rgba(56, 189, 248, 0.6); + background: rgba(22, 30, 48, 0.9); + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4); +} + +.relic-slot-box .btn-slot-unequip-corner { + position: absolute; + top: 6px; + right: 6px; + width: 22px; + height: 22px; + border-radius: 50%; + background: rgba(239, 68, 68, 0.15); + border: 1px solid rgba(239, 68, 68, 0.35); + color: #f87171; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.75rem; + cursor: pointer; + transition: all 0.15s; + z-index: 5; +} + +.relic-slot-box .btn-slot-unequip-corner:hover { + background: #ef4444; + color: #fff; + transform: scale(1.1); } .relic-slot-icon-wrap { @@ -2100,7 +2129,7 @@ input:checked + .slider:before { bottom: 0; left: 0; right: 0; - background: rgba(0, 0, 0, 0.8); + background: rgba(0, 0, 0, 0.85); font-size: 0.6rem; text-align: center; font-weight: 800; @@ -2114,33 +2143,70 @@ input:checked + .slider:before { } .relic-slot-title { - font-size: 0.85rem; - font-weight: 700; + font-size: 0.875rem; + font-weight: 800; color: var(--text-highlight); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +.relic-slot-lvl { + font-size: 0.7rem; + font-weight: 800; + color: #000; + background: var(--accent-gold); + padding: 0.05rem 0.4rem; + border-radius: 4px; +} + .relic-slot-main { - font-size: 0.75rem; - font-weight: 700; - color: var(--accent-gold); + font-size: 0.8rem; + font-weight: 800; + color: var(--accent-cyan); margin-top: 0.2rem; } .relic-slot-subs { - font-size: 0.7rem; + font-size: 0.725rem; color: var(--text-muted); margin-top: 0.2rem; line-height: 1.35; } -.relic-slot-actions { +/* Lightcone Picker Card */ +.lc-picker-card { + background: rgba(18, 24, 38, 0.7); + border: 1px solid var(--border-color); + border-radius: var(--radius-sm); + padding: 0.65rem; display: flex; - flex-direction: column; - gap: 0.35rem; + gap: 0.65rem; + align-items: center; + cursor: pointer; + transition: all 0.2s; + position: relative; +} + +.lc-picker-card:hover { + background: rgba(28, 38, 60, 0.95); + border-color: rgba(56, 189, 248, 0.6); + transform: translateY(-2px); + box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4); +} + +.lc-picker-card.active-equipped { + border-color: var(--accent-gold); + background: rgba(251, 191, 36, 0.1); +} + +.lc-picker-thumb { + width: 44px; + height: 60px; + border-radius: 4px; + object-fit: cover; flex-shrink: 0; + border: 1px solid var(--border-color); } /* Rich Skills & Traces */ diff --git a/index.html b/index.html index bc3af57..26780fc 100644 --- a/index.html +++ b/index.html @@ -232,7 +232,8 @@ -
+
+
@@ -633,10 +634,7 @@ 📋 ข้อมูลภาพรวม -
@@ -731,6 +729,31 @@ + +
+
+ 🗡️ กรวยแสงที่สวมใส่ (คลิกเพื่อเลือกเปลี่ยน) + เปลี่ยนกรวยแสง ➔ +
+
+ +
+
+ - + ★★★★★ +
+
+ S5 + • + Lv. 80 / 80 + • + HP 952 / ATK 582 / DEF 529 +
+
+ +
+
+ - -
- + +
+
+ +
@@ -1235,6 +1260,66 @@ + + +
diff --git a/js/app.js b/js/app.js index eda0411..3532eb5 100644 --- a/js/app.js +++ b/js/app.js @@ -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() { Lv. ${item.level} E${item.rank} -
- - - -
`; - // 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 = `
Relic - Slot ${slot} + ${slotInfo.icon} ${slotInfo.name.split(' ')[0]}
-
${slotInfo.icon} ${rInfo.name}
-
✦ ${mainAffixName} (+${equipped.level})
-
${equipped.sub_affixes?.length || 4} ออปชั่นรอง (Max Level 15)
-
-
- - +
+ ${rInfo.name} + +15 +
+
✦ ${mainAffixName}
+
${equipped.sub_affixes?.length || 4} ออปชั่นรอง (คลิกเพื่อเลือกเปลี่ยน)
+ `; - 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 = ` -
+
${slotInfo.icon} - Slot ${slot} + ${slotInfo.name.split(' ')[0]}
${slotInfo.name}
-
ยังไม่ได้ติดตั้ง (แนะนำ: ${slotInfo.defaultStat})
-
-
- +
ยังไม่ได้สวมใส่
+
+ คลิกเพื่อเลือกหรือสร้าง (แนะนำ: ${slotInfo.defaultStat})
`; - 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 = `
ไม่พบกรวยแสงตามเงื่อนไขที่ค้นหา
`; + 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 = `สวมใส่อยู่`; + } else if (isSig) { + badgeHtml = `ประจำตัว`; + } + + card.innerHTML = ` + ${info.name} +
+
+ ${info.name} + ${badgeHtml} +
+
${'★'.repeat(info.rarity || 5)}
+
+ ${getPathIconHtml(meta.path, 14)} ${info.path} +
+
+ `; + + 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 += `
- ${sb.name} + ${sb.name} ${sb.count}-PC
`; @@ -2769,8 +3081,8 @@ function renderShowcaseCard(avatarId) {
- ${rInfo.name} - ${slotInfo.stat} + ${rInfo.name} + ${slotInfo.stat}
CRIT Rate +${(3.2 + slot * 1.5).toFixed(1)}%${rollBadge(slot % 2 === 0 ? 3 : 2)} @@ -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; diff --git a/js/i18n.js b/js/i18n.js index c699d8c..7a61d4a 100644 --- a/js/i18n.js +++ b/js/i18n.js @@ -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": "全服启用星神·阿哈特殊被动",