From 1fe36fad4397c6eb574f46e76ef9ec00c01a03ec Mon Sep 17 00:00:00 2001 From: CAtlas0784 Date: Sat, 17 Oct 2026 01:40:58 +0700 Subject: [PATCH] feat(settings): replace health check interval with practical presets, ping latency test, battle defaults and cache diagnostics --- index.html | 163 +++++++++++++++++++++++++------ js/app.js | 278 +++++++++++++++++++++++++++++++++++++++++++++++------ js/i18n.js | 58 +++++++++-- 3 files changed, 433 insertions(+), 66 deletions(-) diff --git a/index.html b/index.html index e86863b..c52edd2 100644 --- a/index.html +++ b/index.html @@ -419,38 +419,143 @@
-
- -
-
⚙️ ตั้งค่าการเชื่อมต่อ API Gateway
-

ปรับเปลี่ยนพอร์ตหรือ IP ได้ตามเซิร์ฟเวอร์ที่คุณใช้

+
+ +
+ +
+
⚙️ ตั้งค่าการเชื่อมต่อ API Gateway
+

กำหนด Endpoint และโหมดของเซิร์ฟเวอร์ที่ต้องการสั่งการ

-
-
- - + +
+ + +
+ +
+ + +
+ + + +
+
+ +
+ + +
+ +
+ + +
+
🎮 ค่าเริ่มต้นการต่อสู้ & FreeSR
+

ปรับแต่งพฤติกรรมเริ่มต้นของระบบในเกมและการจัดทีม

+ +
+ +
+ +
+ + + +
+
+ + +
+
+
ซิงค์ไปยังเซิร์ฟเวอร์อัตโนมัติ (Auto-Sync)
+
ส่งการเปลี่ยนแปลงไปยังเซิร์ฟเวอร์ทันทีเมื่อแก้ไขตัวละครหรือไอเทม
+
+ +
+ + +
+
+
เปิดโหมดสร้างสรรค์เมื่อเริ่มต้น (Creative Mode)
+
อนุญาตให้ใส่ตัวละครซ้ำหรือจัดทีมได้มากกว่า 4 ช่องตลอดเวลา
+
+ +
-
- - - - * พอร์ตเริ่มต้น 21000 สำหรับ SDK server - -
-
- - -
-
- - -
- +
+ +
+ +
+
🖼️ การแสดงผลการ์ด Showcase
+

ปรับแต่งความสวยงามและรายละเอียดในหน้าการ์ดแสดงผล Build

+ +
+ +
+
+
แสดงจำนวน Roll ของซับสเตตัสรีลิกส์
+
แสดงแท็กจำนวนครั้งที่สุ่มลงสเตตัสย่อย (Sub-stat Roll Counts)
+
+ +
+ + +
+
+
เอฟเฟกต์แสงเรืองตามธาตุ (Element Aura Glow)
+
เปลี่ยนสีพื้นหลังและการเรืองแสงของการ์ดตามธาตุของตัวละครนั้นๆ
+
+ +
+
+
+ + +
+
🧹 จัดการข้อมูลแคช & รีเซ็ตระบบ
+

ล้างข้อมูลชั่วคราวในเบราว์เซอร์หรือคืนค่าเครื่องมือเป็นค่าเริ่มต้น

+ +
+
+ สถานะหน่วยความจำ LocalStorage: + ตรวจวัด... +
+ +
+ + +
+
+
+
+
diff --git a/js/app.js b/js/app.js index 9b51daf..dbfd649 100644 --- a/js/app.js +++ b/js/app.js @@ -6,7 +6,7 @@ import { SUPPORTED_LANGUAGES, TRANSLATIONS, getCurrentLanguage, setLanguage, t, const state = { serverMode: localStorage.getItem('gac_srtool_mode') || 'gac', // 'gac' | 'firefly' | 'robin' apiUrl: localStorage.getItem('gac_srtool_api_url') || 'http://localhost:21000', - pingIntervalSec: parseInt(localStorage.getItem('gac_srtool_ping_interval') || '10', 10), + pingIntervalSec: 10, isConnected: false, statusData: null, avatarsList: [], @@ -16,7 +16,12 @@ const state = { activeTab: 'tab-dashboard', selectedLineupSlot: null, selectedAvatarDetail: null, - creativeMode: localStorage.getItem('gac_srtool_creative_mode') === 'true' + creativeMode: localStorage.getItem('gac_srtool_creative_mode') === 'true' || localStorage.getItem('gac_srtool_default_creative') === 'true', + defaultEnergy: parseInt(localStorage.getItem('gac_srtool_default_energy') || '50', 10), + autoSync: localStorage.getItem('gac_srtool_auto_sync') !== 'false', + defaultCreative: localStorage.getItem('gac_srtool_default_creative') === 'true', + showcaseRolls: localStorage.getItem('gac_srtool_showcase_rolls') !== 'false', + showcaseAura: localStorage.getItem('gac_srtool_showcase_aura') !== 'false' }; // UI Elements Map @@ -117,7 +122,14 @@ const el = { // Settings formSettings: document.getElementById('form-settings'), settingApiUrl: document.getElementById('setting-api-url'), - settingPingInterval: document.getElementById('setting-ping-interval'), + btnTestConnection: document.getElementById('btn-test-connection'), + pingLatencyBadge: document.getElementById('ping-latency-badge'), + settingAutoSync: document.getElementById('setting-auto-sync'), + settingDefaultCreative: document.getElementById('setting-default-creative'), + settingShowcaseRolls: document.getElementById('setting-showcase-rolls'), + settingShowcaseAura: document.getElementById('setting-showcase-aura'), + lblCacheUsage: document.getElementById('lbl-cache-usage'), + btnClearBrowserCache: document.getElementById('btn-clear-browser-cache'), btnResetSettings: document.getElementById('btn-reset-settings'), // Character Detail Modal @@ -1830,6 +1842,10 @@ function renderShowcaseCard(avatarId) { 6: { name: "สร้อย", stat: meta.path === 'Preservation' || meta.path === 'Harmony' ? "ERR 19.4%" : "ATK 43.2%" } }; + const showRolls = localStorage.getItem('gac_srtool_showcase_rolls') !== 'false'; + const showAura = localStorage.getItem('gac_srtool_showcase_aura') !== 'false'; + const rollBadge = (rolls) => showRolls ? `+${rolls}` : ''; + let relicsHtml = ''; for (let slot = 1; slot <= 6; slot++) { const equipped = avatarRelics.find(r => (r.relic_id % 10 === slot)); @@ -1850,10 +1866,10 @@ function renderShowcaseCard(avatarId) { ${slotInfo.stat}
- CRIT Rate +${(3.2 + slot * 1.5).toFixed(1)}% - CRIT DMG +${(5.8 + slot * 2.1).toFixed(1)}% - ATK +${(3.8 + slot * 0.8).toFixed(1)}% - DEF +${(4.8 + slot * 1.2).toFixed(1)}% + CRIT Rate +${(3.2 + slot * 1.5).toFixed(1)}%${rollBadge(slot % 2 === 0 ? 3 : 2)} + CRIT DMG +${(5.8 + slot * 2.1).toFixed(1)}%${rollBadge(slot % 3 === 0 ? 4 : 2)} + ATK +${(3.8 + slot * 0.8).toFixed(1)}%${rollBadge(1)} + DEF +${(4.8 + slot * 1.2).toFixed(1)}%${rollBadge(1)}
@@ -1929,6 +1945,36 @@ function renderShowcaseCard(avatarId) { ${relicsHtml} `; + + // Apply Element Aura Glow + const auraGlows = { + Physical: 'linear-gradient(135deg, rgba(30, 41, 59, 0.95) 0%, rgba(15, 23, 42, 0.98) 100%)', + Fire: 'linear-gradient(135deg, rgba(79, 15, 15, 0.95) 0%, rgba(15, 23, 42, 0.98) 100%)', + Ice: 'linear-gradient(135deg, rgba(12, 74, 96, 0.95) 0%, rgba(15, 23, 42, 0.98) 100%)', + Lightning: 'linear-gradient(135deg, rgba(68, 15, 110, 0.95) 0%, rgba(15, 23, 42, 0.98) 100%)', + Wind: 'linear-gradient(135deg, rgba(6, 78, 59, 0.95) 0%, rgba(15, 23, 42, 0.98) 100%)', + Quantum: 'linear-gradient(135deg, rgba(49, 46, 129, 0.95) 0%, rgba(15, 23, 42, 0.98) 100%)', + Imaginary: 'linear-gradient(135deg, rgba(88, 40, 5, 0.95) 0%, rgba(15, 23, 42, 0.98) 100%)' + }; + const auraBorders = { + Physical: 'rgba(203, 213, 225, 0.35)', + Fire: 'rgba(239, 68, 68, 0.45)', + Ice: 'rgba(56, 189, 248, 0.45)', + Lightning: 'rgba(168, 85, 247, 0.45)', + Wind: 'rgba(52, 211, 153, 0.45)', + Quantum: 'rgba(129, 140, 248, 0.45)', + Imaginary: 'rgba(251, 191, 36, 0.45)' + }; + + if (showAura && auraGlows[meta.element]) { + el.showcaseCardContainer.style.background = auraGlows[meta.element]; + el.showcaseCardContainer.style.borderColor = auraBorders[meta.element] || 'var(--border-color)'; + el.showcaseCardContainer.style.boxShadow = `0 10px 40px -10px ${auraBorders[meta.element] || 'rgba(0,0,0,0.5)'}`; + } else { + el.showcaseCardContainer.style.background = 'linear-gradient(135deg, #0d121f 0%, #171f30 100%)'; + el.showcaseCardContainer.style.borderColor = 'var(--border-color)'; + el.showcaseCardContainer.style.boxShadow = '0 10px 30px rgba(0,0,0,0.5)'; + } } // Auto-equip recommended 6-piece relics for avatar @@ -2843,6 +2889,24 @@ async function saveJsonData() { } } +// Cache Usage Status for Diagnostics +function updateCacheUsageDisplay() { + if (!el.lblCacheUsage) return; + try { + let totalBytes = 0; + for (let key in localStorage) { + if (Object.prototype.hasOwnProperty.call(localStorage, key)) { + totalBytes += (localStorage[key].length + key.length) * 2; + } + } + const kb = (totalBytes / 1024).toFixed(1); + const count = Object.keys(localStorage).length; + el.lblCacheUsage.textContent = `${kb} KB (${count} keys)`; + } catch (err) { + el.lblCacheUsage.textContent = 'ไม่สามารถตรวจสอบได้'; + } +} + // Tab Switching function setupTabs() { const tabs = document.querySelectorAll('.tab-btn'); @@ -2867,6 +2931,8 @@ function setupTabs() { loadPersistentSettings(); } else if (targetId === 'tab-json') { loadJsonData(state.currentJsonMode); + } else if (targetId === 'tab-settings') { + updateCacheUsageDisplay(); } } }); @@ -3222,33 +3288,183 @@ function setupEvents() { reader.readAsText(file); }); - // Settings Form - el.settingApiUrl.value = state.apiUrl; - el.settingPingInterval.value = state.pingIntervalSec; - - el.formSettings.addEventListener('submit', (e) => { - e.preventDefault(); - state.apiUrl = el.settingApiUrl.value.trim(); - state.pingIntervalSec = parseInt(el.settingPingInterval.value, 10) || 10; - - localStorage.setItem('gac_srtool_api_url', state.apiUrl); - localStorage.setItem('gac_srtool_ping_interval', state.pingIntervalSec); - - showToast(t('settingsSaved'), 'success'); - refreshStatus(); - }); - - el.btnResetSettings.addEventListener('click', () => { - state.apiUrl = 'http://localhost:21000'; - state.pingIntervalSec = 10; + // ------------------------------------------------------------- + // Settings Tab Controls & Diagnostics + // ------------------------------------------------------------- + if (el.settingApiUrl) { el.settingApiUrl.value = state.apiUrl; - el.settingPingInterval.value = state.pingIntervalSec; - localStorage.removeItem('gac_srtool_api_url'); - localStorage.removeItem('gac_srtool_ping_interval'); - showToast(t('settingsReset'), 'info'); - refreshStatus(); + } + + // Quick URL Preset Chips + document.querySelectorAll('.btn-quick-url').forEach(btn => { + btn.addEventListener('click', () => { + if (el.settingApiUrl) { + el.settingApiUrl.value = btn.dataset.url; + showToast(`เลือก Endpoint: ${btn.dataset.url}`, 'info'); + } + }); }); + // Ping Latency Test + if (el.btnTestConnection) { + el.btnTestConnection.addEventListener('click', async () => { + const targetUrl = (el.settingApiUrl?.value || state.apiUrl).trim().replace(/\/+$/, ''); + if (el.pingLatencyBadge) { + el.pingLatencyBadge.style.display = 'inline-block'; + el.pingLatencyBadge.textContent = '⏳ ...'; + el.pingLatencyBadge.style.backgroundColor = 'var(--bg-card)'; + el.pingLatencyBadge.style.color = 'var(--text-muted)'; + } + const t0 = performance.now(); + try { + const res = await fetch(`${targetUrl}/api/status`, { signal: AbortSignal.timeout(3500) }); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + const latency = Math.round(performance.now() - t0); + if (el.pingLatencyBadge) { + el.pingLatencyBadge.textContent = `${latency} ms`; + el.pingLatencyBadge.style.backgroundColor = latency < 80 ? 'rgba(34, 197, 94, 0.2)' : 'rgba(234, 179, 8, 0.2)'; + el.pingLatencyBadge.style.color = latency < 80 ? 'var(--accent-green)' : 'var(--accent-gold)'; + } + showToast(t('toastPingSuccess', { ms: latency }), 'success'); + setConnectionStatus(true); + } catch (err) { + if (el.pingLatencyBadge) { + el.pingLatencyBadge.textContent = 'ERR'; + el.pingLatencyBadge.style.backgroundColor = 'rgba(239, 68, 68, 0.2)'; + el.pingLatencyBadge.style.color = '#ef4444'; + } + showToast(t('toastPingFailed'), 'error'); + setConnectionStatus(false); + } + }); + } + + // Save Connection Settings + if (el.formSettings) { + el.formSettings.addEventListener('submit', (e) => { + e.preventDefault(); + state.apiUrl = el.settingApiUrl.value.trim().replace(/\/+$/, '') || 'http://localhost:21000'; + localStorage.setItem('gac_srtool_api_url', state.apiUrl); + showToast(t('settingsSaved'), 'success'); + refreshStatus(); + }); + } + + // Battle Starting Energy Presets + const curEnergy = localStorage.getItem('gac_srtool_default_energy') || '50'; + document.querySelectorAll('.btn-setting-energy').forEach(btn => { + if (btn.dataset.energy === curEnergy) { + btn.classList.add('active', 'btn-primary'); + btn.classList.remove('btn-secondary'); + } else { + btn.classList.remove('active', 'btn-primary'); + btn.classList.add('btn-secondary'); + } + btn.addEventListener('click', () => { + document.querySelectorAll('.btn-setting-energy').forEach(b => { + b.classList.remove('active', 'btn-primary'); + b.classList.add('btn-secondary'); + }); + btn.classList.add('active', 'btn-primary'); + btn.classList.remove('btn-secondary'); + localStorage.setItem('gac_srtool_default_energy', btn.dataset.energy); + state.defaultEnergy = parseInt(btn.dataset.energy, 10); + showToast(`ตั้งค่าพลังงานเริ่มต้น: ${btn.dataset.energy}%`, 'info'); + }); + }); + + // Auto-Sync Toggle + if (el.settingAutoSync) { + el.settingAutoSync.checked = localStorage.getItem('gac_srtool_auto_sync') !== 'false'; + el.settingAutoSync.addEventListener('change', (e) => { + localStorage.setItem('gac_srtool_auto_sync', e.target.checked); + state.autoSync = e.target.checked; + }); + } + + // Creative Mode by Default Toggle + if (el.settingDefaultCreative) { + el.settingDefaultCreative.checked = localStorage.getItem('gac_srtool_default_creative') === 'true'; + el.settingDefaultCreative.addEventListener('change', (e) => { + const val = e.target.checked; + localStorage.setItem('gac_srtool_default_creative', val); + state.defaultCreative = val; + if (val && !state.creativeMode) { + state.creativeMode = true; + localStorage.setItem('gac_srtool_creative_mode', 'true'); + if (el.toggleCreativeMode) el.toggleCreativeMode.checked = true; + if (el.badgeCreativeMode) el.badgeCreativeMode.style.display = 'inline-flex'; + renderLineup(state.statusData?.lineups); + } + }); + } + + // Showcase Sub-stat Rolls Toggle + if (el.settingShowcaseRolls) { + el.settingShowcaseRolls.checked = localStorage.getItem('gac_srtool_showcase_rolls') !== 'false'; + el.settingShowcaseRolls.addEventListener('change', (e) => { + localStorage.setItem('gac_srtool_showcase_rolls', e.target.checked); + state.showcaseRolls = e.target.checked; + if (state.selectedAvatarDetail?.avatar_id) { + renderShowcaseCard(state.selectedAvatarDetail.avatar_id); + } + }); + } + + // Showcase Element Aura Glow Toggle + if (el.settingShowcaseAura) { + el.settingShowcaseAura.checked = localStorage.getItem('gac_srtool_showcase_aura') !== 'false'; + el.settingShowcaseAura.addEventListener('change', (e) => { + localStorage.setItem('gac_srtool_showcase_aura', e.target.checked); + state.showcaseAura = e.target.checked; + if (state.selectedAvatarDetail?.avatar_id) { + renderShowcaseCard(state.selectedAvatarDetail.avatar_id); + } + }); + } + + // Clear Browser Cache Button + if (el.btnClearBrowserCache) { + el.btnClearBrowserCache.addEventListener('click', () => { + const keepUrl = localStorage.getItem('gac_srtool_api_url'); + const keepMode = localStorage.getItem('gac_srtool_mode'); + const keepLang = localStorage.getItem('gac_srtool_lang'); + localStorage.clear(); + if (keepUrl) localStorage.setItem('gac_srtool_api_url', keepUrl); + if (keepMode) localStorage.setItem('gac_srtool_mode', keepMode); + if (keepLang) localStorage.setItem('gac_srtool_lang', keepLang); + updateCacheUsageDisplay(); + showToast(t('toastCacheCleared'), 'success'); + }); + } + + // Factory Reset Button + if (el.btnResetSettings) { + el.btnResetSettings.addEventListener('click', () => { + localStorage.clear(); + state.apiUrl = 'http://localhost:21000'; + state.serverMode = 'gac'; + state.creativeMode = false; + state.defaultEnergy = 50; + state.autoSync = true; + state.showcaseRolls = true; + state.showcaseAura = true; + if (el.settingApiUrl) el.settingApiUrl.value = state.apiUrl; + if (el.settingServerMode) el.settingServerMode.value = 'gac'; + if (el.settingAutoSync) el.settingAutoSync.checked = true; + if (el.settingDefaultCreative) el.settingDefaultCreative.checked = false; + if (el.settingShowcaseRolls) el.settingShowcaseRolls.checked = true; + if (el.settingShowcaseAura) el.settingShowcaseAura.checked = true; + if (el.toggleCreativeMode) el.toggleCreativeMode.checked = false; + if (el.badgeCreativeMode) el.badgeCreativeMode.style.display = 'none'; + updateCacheUsageDisplay(); + showToast(t('settingsReset'), 'info'); + refreshStatus(); + }); + } + + updateCacheUsageDisplay(); + // Modal Backdrop Click to close [el.modalAvatarDetail, el.modalLineupPicker, el.modalSettings].forEach(modal => { if (modal) { diff --git a/js/i18n.js b/js/i18n.js index f11d601..c699d8c 100644 --- a/js/i18n.js +++ b/js/i18n.js @@ -158,13 +158,36 @@ export const TRANSLATIONS = { "jsonStatusOpening": "กำลังเปิด: {name}", "jsonSaveSuccess": "บันทึกไฟล์ {name} เรียบร้อยแล้ว", "jsonInvalid": "รูปแบบ JSON ไม่ถูกต้อง", - "apiSettingsTitle": "⚙️ ตั้งค่าการเชื่อมต่อ API Gateway", - "apiSettingsSubtitle": "ปรับเปลี่ยนพอร์ตหรือ IP ได้ตามไฟล์ server.json ของคุณ", + "settingsConnTitle": "⚙️ ตั้งค่าการเชื่อมต่อ API Gateway", + "settingsConnSubtitle": "กำหนด Endpoint และโหมดของเซิร์ฟเวอร์ที่ต้องการสั่งการ", "lblServerMode": "โหมดเซิร์ฟเวอร์", "lblApiGatewayUrl": "REST API Gateway URL", - "lblPingInterval": "ความถี่ในการตรวจสอบสถานะ (วินาที)", + "btnTestPing": "⚡ ทดสอบการเชื่อมต่อ", "btnSaveSettings": "💾 บันทึกการตั้งค่า", "btnResetDefault": "คืนค่าเริ่มต้น", + "settingsBattleTitle": "🎮 ค่าเริ่มต้นการต่อสู้ & FreeSR", + "settingsBattleSubtitle": "ปรับแต่งพฤติกรรมเริ่มต้นของระบบในเกมและการจัดทีม", + "lblDefaultEnergy": "พลังงานเริ่มต้นของทีม (Battle Starting Energy)", + "lblAutoSync": "ซิงค์ไปยังเซิร์ฟเวอร์อัตโนมัติ (Auto-Sync)", + "descAutoSync": "ส่งการเปลี่ยนแปลงไปยังเซิร์ฟเวอร์ทันทีเมื่อแก้ไขตัวละครหรือไอเทม", + "lblDefaultCreative": "เปิดโหมดสร้างสรรค์เมื่อเริ่มต้น (Creative Mode)", + "descDefaultCreative": "อนุญาตให้ใส่ตัวละครซ้ำหรือจัดทีมได้มากกว่า 4 ช่องตลอดเวลา", + "settingsShowcaseTitle": "🖼️ การแสดงผลการ์ด Showcase", + "settingsShowcaseSubtitle": "ปรับแต่งความสวยงามและรายละเอียดในหน้าการ์ดแสดงผล Build", + "lblShowRolls": "แสดงจำนวน Roll ของซับสเตตัสรีลิกส์", + "descShowRolls": "แสดงแท็กจำนวนครั้งที่สุ่มลงสเตตัสย่อย (Sub-stat Roll Counts)", + "lblElementAura": "เอฟเฟกต์แสงเรืองตามธาตุ (Element Aura Glow)", + "descElementAura": "เปลี่ยนสีพื้นหลังและการเรืองแสงของการ์ดตามธาตุของตัวละครนั้นๆ", + "settingsCacheTitle": "🧹 จัดการข้อมูลแคช & รีเซ็ตระบบ", + "settingsCacheSubtitle": "ล้างข้อมูลชั่วคราวในเบราว์เซอร์หรือคืนค่าเครื่องมือเป็นค่าเริ่มต้น", + "lblCacheSize": "สถานะหน่วยความจำ LocalStorage:", + "btnClearCache": "🧹 ล้างแคชเบราว์เซอร์", + "btnFactoryReset": "🔄 คืนค่าเริ่มต้นทั้งหมด", + "toastPingSuccess": "⚡ เชื่อมต่อสำเร็จ! Latency: {ms} ms", + "toastPingFailed": "❌ ไม่สามารถเชื่อมต่อกับ Gateway ได้ กรุณาตรวจสอบเซิร์ฟเวอร์", + "toastCacheCleared": "🧹 ล้างข้อมูลแคชเบราว์เซอร์เรียบร้อยแล้ว", + "apiSettingsTitle": "⚙️ ตั้งค่าการเชื่อมต่อ API Gateway", + "apiSettingsSubtitle": "ปรับเปลี่ยนพอร์ตหรือ IP ได้ตามไฟล์ server.json ของคุณ", "modalTitleCharEdit": "ปรับแต่งตัวละคร", "modalBtnMaxAllChar": "⭐ ปรับเต็มทั้งหมด", "modalTabSkills": "⚡ สกิลและร่องรอย", @@ -387,13 +410,36 @@ export const TRANSLATIONS = { "jsonStatusOpening": "Opening: {name}", "jsonSaveSuccess": "File {name} saved successfully", "jsonInvalid": "Invalid JSON format", - "apiSettingsTitle": "⚙️ API Gateway Connection Settings", - "apiSettingsSubtitle": "Adjust port or IP according to your server.json", + "settingsConnTitle": "⚙️ API Gateway Connection", + "settingsConnSubtitle": "Configure server mode and REST endpoint", "lblServerMode": "Server Mode", "lblApiGatewayUrl": "REST API Gateway URL", - "lblPingInterval": "Health Check Interval (seconds)", + "btnTestPing": "⚡ Test Connection", "btnSaveSettings": "💾 Save Settings", "btnResetDefault": "Reset Defaults", + "settingsBattleTitle": "🎮 Battle & FreeSR Defaults", + "settingsBattleSubtitle": "Customize gameplay behavior and lineup defaults", + "lblDefaultEnergy": "Battle Starting Energy", + "lblAutoSync": "Auto-Sync to Server", + "descAutoSync": "Instantly send character and equipment updates to the server", + "lblDefaultCreative": "Creative Mode by Default", + "descDefaultCreative": "Allow duplicate characters and more than 4 lineup slots", + "settingsShowcaseTitle": "🖼️ Showcase Card Display", + "settingsShowcaseSubtitle": "Customize character build card styling and details", + "lblShowRolls": "Show Sub-stat Roll Counts", + "descShowRolls": "Display roll tags for relic sub-affixes", + "lblElementAura": "Element Aura Glow Effect", + "descElementAura": "Dynamic card background and ambient glow matching character element", + "settingsCacheTitle": "🧹 Cache Management & Reset", + "settingsCacheSubtitle": "Clear temporary browser data or restore default settings", + "lblCacheSize": "LocalStorage Memory Usage:", + "btnClearCache": "🧹 Clear Browser Cache", + "btnFactoryReset": "🔄 Factory Reset", + "toastPingSuccess": "⚡ Connected successfully! Latency: {ms} ms", + "toastPingFailed": "❌ Failed to connect to Gateway. Please check server status", + "toastCacheCleared": "🧹 Browser cache cleared successfully", + "apiSettingsTitle": "⚙️ API Gateway Connection Settings", + "apiSettingsSubtitle": "Adjust port or IP according to your server.json", "modalTitleCharEdit": "Character Customization", "modalBtnMaxAllChar": "⭐ Max All", "modalTabSkills": "⚡ Skills & Traces",