feat(settings): replace health check interval with practical presets, ping latency test, battle defaults and cache diagnostics
This commit is contained in:
1 parent
1b8d1744b2
commit
1fe36fad43
3 files changed
+433
-66
No files matched your search
+134
-29
@@ -419,38 +419,143 @@
|
|||||||
|
|
||||||
<!-- 8. Settings -->
|
<!-- 8. Settings -->
|
||||||
<section id="tab-settings" class="tab-content">
|
<section id="tab-settings" class="tab-content">
|
||||||
<div style="max-width: 620px; margin: 0 auto;">
|
<div style="max-width: 900px; margin: 0 auto; display: flex; flex-direction: column; gap: 1.25rem;">
|
||||||
<!-- Endpoint config -->
|
|
||||||
<div class="card">
|
<div class="grid-2">
|
||||||
<div class="card-title" data-i18n="apiSettingsTitle">⚙️ ตั้งค่าการเชื่อมต่อ API Gateway</div>
|
<!-- Card 1: Server & Gateway Connection -->
|
||||||
<p class="card-subtitle" data-i18n="apiSettingsSubtitle">ปรับเปลี่ยนพอร์ตหรือ IP ได้ตามเซิร์ฟเวอร์ที่คุณใช้</p>
|
<div class="card">
|
||||||
|
<div class="card-title" data-i18n="settingsConnTitle">⚙️ ตั้งค่าการเชื่อมต่อ API Gateway</div>
|
||||||
|
<p class="card-subtitle" data-i18n="settingsConnSubtitle">กำหนด Endpoint และโหมดของเซิร์ฟเวอร์ที่ต้องการสั่งการ</p>
|
||||||
|
|
||||||
<form id="form-settings">
|
<form id="form-settings">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label" data-i18n="lblServerMode">โหมดเซิร์ฟเวอร์</label>
|
<label class="form-label" data-i18n="lblServerMode">โหมดเซิร์ฟเวอร์</label>
|
||||||
<select id="setting-server-mode" class="form-select">
|
<select id="setting-server-mode" class="form-select">
|
||||||
<option value="gac">GaC-hkrpg-go</option>
|
<option value="gac">GaC-hkrpg-go (Official GaC Server)</option>
|
||||||
<option value="firefly">FireflyGo</option>
|
<option value="firefly">FireflyGo (CBT / FreeSR)</option>
|
||||||
<option value="robin">RobinSR</option>
|
<option value="robin">RobinSR (Proxy Gateway)</option>
|
||||||
</select>
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" data-i18n="lblApiGatewayUrl">REST API Gateway URL</label>
|
||||||
|
<input type="text" id="setting-api-url" class="form-input" placeholder="http://localhost:21000">
|
||||||
|
<div style="display: flex; gap: 0.35rem; margin-top: 0.5rem; flex-wrap: wrap;">
|
||||||
|
<button type="button" class="btn btn-sm btn-secondary btn-quick-url" data-url="http://localhost:21000">localhost:21000</button>
|
||||||
|
<button type="button" class="btn btn-sm btn-secondary btn-quick-url" data-url="http://127.0.0.1:21000">127.0.0.1:21000</button>
|
||||||
|
<button type="button" class="btn btn-sm btn-secondary btn-quick-url" data-url="http://localhost:8080">localhost:8080</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="display: flex; gap: 0.5rem; margin-top: 1.25rem;">
|
||||||
|
<button type="button" class="btn btn-secondary" id="btn-test-connection" style="display: flex; align-items: center; gap: 0.4rem;">
|
||||||
|
<span data-i18n="btnTestPing">⚡ ทดสอบการเชื่อมต่อ</span>
|
||||||
|
<span id="ping-latency-badge" class="badge-super-tag" style="display: none; padding: 0.1rem 0.45rem; font-size: 0.75rem;">-</span>
|
||||||
|
</button>
|
||||||
|
<button type="submit" class="btn btn-success" style="flex: 1;" data-i18n="btnSaveSettings">💾 บันทึกการตั้งค่า</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Card 2: Battle & Gameplay Preferences -->
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-title" data-i18n="settingsBattleTitle">🎮 ค่าเริ่มต้นการต่อสู้ & FreeSR</div>
|
||||||
|
<p class="card-subtitle" data-i18n="settingsBattleSubtitle">ปรับแต่งพฤติกรรมเริ่มต้นของระบบในเกมและการจัดทีม</p>
|
||||||
|
|
||||||
|
<div style="display: flex; flex-direction: column; gap: 1rem;">
|
||||||
|
<!-- Default Energy -->
|
||||||
|
<div class="form-group" style="margin-bottom: 0;">
|
||||||
|
<label class="form-label" data-i18n="lblDefaultEnergy">พลังงานเริ่มต้นของทีม (Battle Starting Energy)</label>
|
||||||
|
<div style="display: flex; gap: 0.4rem; flex-wrap: wrap; margin-top: 0.35rem;">
|
||||||
|
<button type="button" class="btn btn-sm btn-primary btn-setting-energy active" data-energy="50">⚡ 50% (มาตรฐานเกม)</button>
|
||||||
|
<button type="button" class="btn btn-sm btn-secondary btn-setting-energy" data-energy="100">🔥 100% (อัลติพร้อมใช้)</button>
|
||||||
|
<button type="button" class="btn btn-sm btn-secondary btn-setting-energy" data-energy="0">❄️ 0% (ท้าทาย)</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Auto-Sync Toggle -->
|
||||||
|
<div style="display: flex; justify-content: space-between; align-items: center; padding-top: 0.75rem; border-top: 1px solid var(--border-color);">
|
||||||
|
<div>
|
||||||
|
<div style="font-weight: 700; font-size: 0.9rem;" data-i18n="lblAutoSync">ซิงค์ไปยังเซิร์ฟเวอร์อัตโนมัติ (Auto-Sync)</div>
|
||||||
|
<div style="font-size: 0.75rem; color: var(--text-muted);" data-i18n="descAutoSync">ส่งการเปลี่ยนแปลงไปยังเซิร์ฟเวอร์ทันทีเมื่อแก้ไขตัวละครหรือไอเทม</div>
|
||||||
|
</div>
|
||||||
|
<label class="switch">
|
||||||
|
<input type="checkbox" id="setting-auto-sync" checked>
|
||||||
|
<span class="slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Creative Mode Default -->
|
||||||
|
<div style="display: flex; justify-content: space-between; align-items: center; padding-top: 0.75rem; border-top: 1px solid var(--border-color);">
|
||||||
|
<div>
|
||||||
|
<div style="font-weight: 700; font-size: 0.9rem;" data-i18n="lblDefaultCreative">เปิดโหมดสร้างสรรค์เมื่อเริ่มต้น (Creative Mode)</div>
|
||||||
|
<div style="font-size: 0.75rem; color: var(--text-muted);" data-i18n="descDefaultCreative">อนุญาตให้ใส่ตัวละครซ้ำหรือจัดทีมได้มากกว่า 4 ช่องตลอดเวลา</div>
|
||||||
|
</div>
|
||||||
|
<label class="switch">
|
||||||
|
<input type="checkbox" id="setting-default-creative">
|
||||||
|
<span class="slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
</div>
|
||||||
<label class="form-label" data-i18n="lblApiGatewayUrl">REST API Gateway URL</label>
|
|
||||||
<input type="text" id="setting-api-url" class="form-input" placeholder="http://localhost:21000">
|
|
||||||
<small style="color: var(--text-muted); display: block; margin-top: 0.35rem;">
|
|
||||||
* พอร์ตเริ่มต้น 21000 สำหรับ SDK server
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="form-label" data-i18n="lblPingInterval">ความถี่ในการตรวจสอบสถานะ (วินาที)</label>
|
|
||||||
<input type="number" id="setting-ping-interval" class="form-input" min="3" max="60" value="10">
|
|
||||||
</div>
|
|
||||||
<div style="display: flex; gap: 0.75rem;">
|
|
||||||
<button type="submit" class="btn btn-success" style="flex: 1;" data-i18n="btnSaveSettings">💾 บันทึกการตั้งค่า</button>
|
|
||||||
<button type="button" class="btn btn-secondary" id="btn-reset-settings" data-i18n="btnResetDefault">คืนค่าเริ่มต้น</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="grid-2">
|
||||||
|
<!-- Card 3: Showcase Card Display Settings -->
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-title" data-i18n="settingsShowcaseTitle">🖼️ การแสดงผลการ์ด Showcase</div>
|
||||||
|
<p class="card-subtitle" data-i18n="settingsShowcaseSubtitle">ปรับแต่งความสวยงามและรายละเอียดในหน้าการ์ดแสดงผล Build</p>
|
||||||
|
|
||||||
|
<div style="display: flex; flex-direction: column; gap: 0.85rem;">
|
||||||
|
<!-- Show Roll Counts on Relics -->
|
||||||
|
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||||
|
<div>
|
||||||
|
<div style="font-weight: 700; font-size: 0.9rem;" data-i18n="lblShowRolls">แสดงจำนวน Roll ของซับสเตตัสรีลิกส์</div>
|
||||||
|
<div style="font-size: 0.75rem; color: var(--text-muted);" data-i18n="descShowRolls">แสดงแท็กจำนวนครั้งที่สุ่มลงสเตตัสย่อย (Sub-stat Roll Counts)</div>
|
||||||
|
</div>
|
||||||
|
<label class="switch">
|
||||||
|
<input type="checkbox" id="setting-showcase-rolls" checked>
|
||||||
|
<span class="slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Dynamic Element Aura Glow -->
|
||||||
|
<div style="display: flex; justify-content: space-between; align-items: center; padding-top: 0.75rem; border-top: 1px solid var(--border-color);">
|
||||||
|
<div>
|
||||||
|
<div style="font-weight: 700; font-size: 0.9rem;" data-i18n="lblElementAura">เอฟเฟกต์แสงเรืองตามธาตุ (Element Aura Glow)</div>
|
||||||
|
<div style="font-size: 0.75rem; color: var(--text-muted);" data-i18n="descElementAura">เปลี่ยนสีพื้นหลังและการเรืองแสงของการ์ดตามธาตุของตัวละครนั้นๆ</div>
|
||||||
|
</div>
|
||||||
|
<label class="switch">
|
||||||
|
<input type="checkbox" id="setting-showcase-aura" checked>
|
||||||
|
<span class="slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Card 4: Diagnostics & Cache Reset -->
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-title" data-i18n="settingsCacheTitle">🧹 จัดการข้อมูลแคช & รีเซ็ตระบบ</div>
|
||||||
|
<p class="card-subtitle" data-i18n="settingsCacheSubtitle">ล้างข้อมูลชั่วคราวในเบราว์เซอร์หรือคืนค่าเครื่องมือเป็นค่าเริ่มต้น</p>
|
||||||
|
|
||||||
|
<div style="display: flex; flex-direction: column; gap: 0.75rem;">
|
||||||
|
<div style="display: flex; justify-content: space-between; align-items: center; font-size: 0.85rem;">
|
||||||
|
<span style="color: var(--text-muted);" data-i18n="lblCacheSize">สถานะหน่วยความจำ LocalStorage:</span>
|
||||||
|
<strong id="lbl-cache-usage" style="color: var(--accent-cyan); font-family: monospace;">ตรวจวัด...</strong>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="display: flex; gap: 0.75rem; margin-top: 0.5rem; flex-wrap: wrap;">
|
||||||
|
<button type="button" class="btn btn-secondary" id="btn-clear-browser-cache" style="flex: 1; min-width: 140px;" data-i18n="btnClearCache">
|
||||||
|
🧹 ล้างแคชเบราว์เซอร์
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-danger" id="btn-reset-settings" style="flex: 1; min-width: 140px;" data-i18n="btnFactoryReset">
|
||||||
|
🔄 คืนค่าเริ่มต้นทั้งหมด
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { SUPPORTED_LANGUAGES, TRANSLATIONS, getCurrentLanguage, setLanguage, t,
|
|||||||
const state = {
|
const state = {
|
||||||
serverMode: localStorage.getItem('gac_srtool_mode') || 'gac', // 'gac' | 'firefly' | 'robin'
|
serverMode: localStorage.getItem('gac_srtool_mode') || 'gac', // 'gac' | 'firefly' | 'robin'
|
||||||
apiUrl: localStorage.getItem('gac_srtool_api_url') || 'http://localhost:21000',
|
apiUrl: localStorage.getItem('gac_srtool_api_url') || 'http://localhost:21000',
|
||||||
pingIntervalSec: parseInt(localStorage.getItem('gac_srtool_ping_interval') || '10', 10),
|
pingIntervalSec: 10,
|
||||||
isConnected: false,
|
isConnected: false,
|
||||||
statusData: null,
|
statusData: null,
|
||||||
avatarsList: [],
|
avatarsList: [],
|
||||||
@@ -16,7 +16,12 @@ const state = {
|
|||||||
activeTab: 'tab-dashboard',
|
activeTab: 'tab-dashboard',
|
||||||
selectedLineupSlot: null,
|
selectedLineupSlot: null,
|
||||||
selectedAvatarDetail: 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
|
// UI Elements Map
|
||||||
@@ -117,7 +122,14 @@ const el = {
|
|||||||
// Settings
|
// Settings
|
||||||
formSettings: document.getElementById('form-settings'),
|
formSettings: document.getElementById('form-settings'),
|
||||||
settingApiUrl: document.getElementById('setting-api-url'),
|
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'),
|
btnResetSettings: document.getElementById('btn-reset-settings'),
|
||||||
|
|
||||||
// Character Detail Modal
|
// 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%" }
|
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 ? `<span style="display:inline-block; font-size:0.65rem; color:var(--accent-gold); font-weight:800; background:rgba(234,179,8,0.18); border-radius:3px; padding:0 3px; margin-left:3px;">+${rolls}</span>` : '';
|
||||||
|
|
||||||
let relicsHtml = '';
|
let relicsHtml = '';
|
||||||
for (let slot = 1; slot <= 6; slot++) {
|
for (let slot = 1; slot <= 6; slot++) {
|
||||||
const equipped = avatarRelics.find(r => (r.relic_id % 10 === slot));
|
const equipped = avatarRelics.find(r => (r.relic_id % 10 === slot));
|
||||||
@@ -1850,10 +1866,10 @@ function renderShowcaseCard(avatarId) {
|
|||||||
<span style="color: var(--accent-cyan); font-weight: 800;">${slotInfo.stat}</span>
|
<span style="color: var(--accent-cyan); font-weight: 800;">${slotInfo.stat}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="showcase-relic-subs">
|
<div class="showcase-relic-subs">
|
||||||
<span>CRIT Rate +${(3.2 + slot * 1.5).toFixed(1)}%</span>
|
<span>CRIT Rate +${(3.2 + slot * 1.5).toFixed(1)}%${rollBadge(slot % 2 === 0 ? 3 : 2)}</span>
|
||||||
<span>CRIT DMG +${(5.8 + slot * 2.1).toFixed(1)}%</span>
|
<span>CRIT DMG +${(5.8 + slot * 2.1).toFixed(1)}%${rollBadge(slot % 3 === 0 ? 4 : 2)}</span>
|
||||||
<span>ATK +${(3.8 + slot * 0.8).toFixed(1)}%</span>
|
<span>ATK +${(3.8 + slot * 0.8).toFixed(1)}%${rollBadge(1)}</span>
|
||||||
<span>DEF +${(4.8 + slot * 1.2).toFixed(1)}%</span>
|
<span>DEF +${(4.8 + slot * 1.2).toFixed(1)}%${rollBadge(1)}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1929,6 +1945,36 @@ function renderShowcaseCard(avatarId) {
|
|||||||
${relicsHtml}
|
${relicsHtml}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
// 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
|
// 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
|
// Tab Switching
|
||||||
function setupTabs() {
|
function setupTabs() {
|
||||||
const tabs = document.querySelectorAll('.tab-btn');
|
const tabs = document.querySelectorAll('.tab-btn');
|
||||||
@@ -2867,6 +2931,8 @@ function setupTabs() {
|
|||||||
loadPersistentSettings();
|
loadPersistentSettings();
|
||||||
} else if (targetId === 'tab-json') {
|
} else if (targetId === 'tab-json') {
|
||||||
loadJsonData(state.currentJsonMode);
|
loadJsonData(state.currentJsonMode);
|
||||||
|
} else if (targetId === 'tab-settings') {
|
||||||
|
updateCacheUsageDisplay();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -3222,33 +3288,183 @@ function setupEvents() {
|
|||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Settings Form
|
// -------------------------------------------------------------
|
||||||
el.settingApiUrl.value = state.apiUrl;
|
// Settings Tab Controls & Diagnostics
|
||||||
el.settingPingInterval.value = state.pingIntervalSec;
|
// -------------------------------------------------------------
|
||||||
|
if (el.settingApiUrl) {
|
||||||
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;
|
|
||||||
el.settingApiUrl.value = state.apiUrl;
|
el.settingApiUrl.value = state.apiUrl;
|
||||||
el.settingPingInterval.value = state.pingIntervalSec;
|
}
|
||||||
localStorage.removeItem('gac_srtool_api_url');
|
|
||||||
localStorage.removeItem('gac_srtool_ping_interval');
|
// Quick URL Preset Chips
|
||||||
showToast(t('settingsReset'), 'info');
|
document.querySelectorAll('.btn-quick-url').forEach(btn => {
|
||||||
refreshStatus();
|
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
|
// Modal Backdrop Click to close
|
||||||
[el.modalAvatarDetail, el.modalLineupPicker, el.modalSettings].forEach(modal => {
|
[el.modalAvatarDetail, el.modalLineupPicker, el.modalSettings].forEach(modal => {
|
||||||
if (modal) {
|
if (modal) {
|
||||||
|
|||||||
+52
-6
@@ -158,13 +158,36 @@ export const TRANSLATIONS = {
|
|||||||
"jsonStatusOpening": "กำลังเปิด: {name}",
|
"jsonStatusOpening": "กำลังเปิด: {name}",
|
||||||
"jsonSaveSuccess": "บันทึกไฟล์ {name} เรียบร้อยแล้ว",
|
"jsonSaveSuccess": "บันทึกไฟล์ {name} เรียบร้อยแล้ว",
|
||||||
"jsonInvalid": "รูปแบบ JSON ไม่ถูกต้อง",
|
"jsonInvalid": "รูปแบบ JSON ไม่ถูกต้อง",
|
||||||
"apiSettingsTitle": "⚙️ ตั้งค่าการเชื่อมต่อ API Gateway",
|
"settingsConnTitle": "⚙️ ตั้งค่าการเชื่อมต่อ API Gateway",
|
||||||
"apiSettingsSubtitle": "ปรับเปลี่ยนพอร์ตหรือ IP ได้ตามไฟล์ server.json ของคุณ",
|
"settingsConnSubtitle": "กำหนด Endpoint และโหมดของเซิร์ฟเวอร์ที่ต้องการสั่งการ",
|
||||||
"lblServerMode": "โหมดเซิร์ฟเวอร์",
|
"lblServerMode": "โหมดเซิร์ฟเวอร์",
|
||||||
"lblApiGatewayUrl": "REST API Gateway URL",
|
"lblApiGatewayUrl": "REST API Gateway URL",
|
||||||
"lblPingInterval": "ความถี่ในการตรวจสอบสถานะ (วินาที)",
|
"btnTestPing": "⚡ ทดสอบการเชื่อมต่อ",
|
||||||
"btnSaveSettings": "💾 บันทึกการตั้งค่า",
|
"btnSaveSettings": "💾 บันทึกการตั้งค่า",
|
||||||
"btnResetDefault": "คืนค่าเริ่มต้น",
|
"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": "ปรับแต่งตัวละคร",
|
"modalTitleCharEdit": "ปรับแต่งตัวละคร",
|
||||||
"modalBtnMaxAllChar": "⭐ ปรับเต็มทั้งหมด",
|
"modalBtnMaxAllChar": "⭐ ปรับเต็มทั้งหมด",
|
||||||
"modalTabSkills": "⚡ สกิลและร่องรอย",
|
"modalTabSkills": "⚡ สกิลและร่องรอย",
|
||||||
@@ -387,13 +410,36 @@ export const TRANSLATIONS = {
|
|||||||
"jsonStatusOpening": "Opening: {name}",
|
"jsonStatusOpening": "Opening: {name}",
|
||||||
"jsonSaveSuccess": "File {name} saved successfully",
|
"jsonSaveSuccess": "File {name} saved successfully",
|
||||||
"jsonInvalid": "Invalid JSON format",
|
"jsonInvalid": "Invalid JSON format",
|
||||||
"apiSettingsTitle": "⚙️ API Gateway Connection Settings",
|
"settingsConnTitle": "⚙️ API Gateway Connection",
|
||||||
"apiSettingsSubtitle": "Adjust port or IP according to your server.json",
|
"settingsConnSubtitle": "Configure server mode and REST endpoint",
|
||||||
"lblServerMode": "Server Mode",
|
"lblServerMode": "Server Mode",
|
||||||
"lblApiGatewayUrl": "REST API Gateway URL",
|
"lblApiGatewayUrl": "REST API Gateway URL",
|
||||||
"lblPingInterval": "Health Check Interval (seconds)",
|
"btnTestPing": "⚡ Test Connection",
|
||||||
"btnSaveSettings": "💾 Save Settings",
|
"btnSaveSettings": "💾 Save Settings",
|
||||||
"btnResetDefault": "Reset Defaults",
|
"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",
|
"modalTitleCharEdit": "Character Customization",
|
||||||
"modalBtnMaxAllChar": "⭐ Max All",
|
"modalBtnMaxAllChar": "⭐ Max All",
|
||||||
"modalTabSkills": "⚡ Skills & Traces",
|
"modalTabSkills": "⚡ Skills & Traces",
|
||||||
|
|||||||
Reference in new issue
Block a user