@@ -199,9 +202,12 @@
-
-
🛡️ รีลิกส์ในคลัง
-
0 ชิ้น
+
+
+ 🛡️ รีลิกส์ในคลัง
+ 0 ชิ้น
+
+
รายการรีลิกส์ทั้งหมดจาก freesr-data.json
@@ -282,6 +288,17 @@
+
+
+
Aeon ★ Aha Global Feature
+
เปิดใช้งานฟังก์ชันพิเศษ Aeon ★ Aha ทั่วทั้งเซิร์ฟเวอร์
+
+
+
+
@@ -653,6 +670,101 @@
+
+
+
+
+
+
diff --git a/js/app.js b/js/app.js
index 3a4e9e6..23281ac 100644
--- a/js/app.js
+++ b/js/app.js
@@ -1,5 +1,5 @@
import { AVATAR_METADATA, getAvatarInfo } from './avatars-data.js';
-import { getLightconeInfo, getRelicSetInfo } from './equipment-data.js';
+import { LIGHTCONE_METADATA, RELIC_SET_METADATA, getLightconeInfo, getRelicSetInfo } from './equipment-data.js';
import { SUPPORTED_LANGUAGES, TRANSLATIONS, getCurrentLanguage, setLanguage, t, translateElement, translatePath } from './i18n.js';
// Application State
@@ -60,6 +60,26 @@ const el = {
relicsList: document.getElementById('relics-list'),
badgeTotalLc: document.getElementById('badge-total-lc'),
badgeTotalRelics: document.getElementById('badge-total-relics'),
+ btnOpenAddLc: document.getElementById('btn-open-add-lc'),
+ modalAddLc: document.getElementById('modal-add-lc'),
+ btnCloseAddLcModal: document.getElementById('btn-close-add-lc-modal'),
+ btnCancelAddLc: document.getElementById('btn-cancel-add-lc'),
+ formAddLc: document.getElementById('form-add-lc'),
+ addLcSelect: document.getElementById('add-lc-select'),
+ addLcLevel: document.getElementById('add-lc-level'),
+ addLcPromotion: document.getElementById('add-lc-promotion'),
+ addLcRank: document.getElementById('add-lc-rank'),
+ addLcAvatar: document.getElementById('add-lc-avatar'),
+ btnOpenAddRelic: document.getElementById('btn-open-add-relic'),
+ modalAddRelic: document.getElementById('modal-add-relic'),
+ btnCloseAddRelicModal: document.getElementById('btn-close-add-relic-modal'),
+ btnCancelAddRelic: document.getElementById('btn-cancel-add-relic'),
+ formAddRelic: document.getElementById('form-add-relic'),
+ addRelicSet: document.getElementById('add-relic-set'),
+ addRelicSlot: document.getElementById('add-relic-slot'),
+ addRelicLevel: document.getElementById('add-relic-level'),
+ addRelicMainAffix: document.getElementById('add-relic-main-affix'),
+ addRelicAvatar: document.getElementById('add-relic-avatar'),
// Persistent Tab
tabBtnPersistent: document.getElementById('tab-btn-persistent'),
@@ -68,6 +88,7 @@ const el = {
persistentMarchSelect: document.getElementById('persistent-march-select'),
toggleSwGlobal: document.getElementById('toggle-sw-global'),
toggleCastoriceGlobal: document.getElementById('toggle-castorice-global'),
+ toggleAhaGlobal: document.getElementById('toggle-aha-global'),
btnSavePersistentToggles: document.getElementById('btn-save-persistent-toggles'),
// Ex-Rank
@@ -236,7 +257,45 @@ function setConnectionStatus(connected, extraText = '') {
}
// Helper to get element CSS class
+// Canonical element & path maps (handles English and all 13 supported languages)
+const CANONICAL_ELEMENTS = {
+ physical: 'Physical', fire: 'Fire', ice: 'Ice', lightning: 'Lightning',
+ wind: 'Wind', quantum: 'Quantum', imaginary: 'Imaginary', thunder: 'Lightning'
+};
+
+const CANONICAL_PATHS = {
+ destruction: 'Destruction', hunt: 'Hunt', 'the hunt': 'Hunt', erudition: 'Erudition',
+ harmony: 'Harmony', nihility: 'Nihility', preservation: 'Preservation',
+ abundance: 'Abundance', remembrance: 'Remembrance', elation: 'Elation'
+};
+
+// Dynamically index all translated element and path strings across languages
+for (const langKey in TRANSLATIONS) {
+ const tr = TRANSLATIONS[langKey];
+ if (tr.elements) {
+ for (const [enKey, locVal] of Object.entries(tr.elements)) {
+ if (locVal) CANONICAL_ELEMENTS[String(locVal).trim().toLowerCase()] = enKey;
+ }
+ }
+ if (tr.paths) {
+ for (const [enKey, locVal] of Object.entries(tr.paths)) {
+ if (locVal) CANONICAL_PATHS[String(locVal).trim().toLowerCase()] = (enKey === 'The Hunt' ? 'Hunt' : enKey);
+ }
+ }
+}
+
+function normalizeElement(elem) {
+ if (!elem) return 'Physical';
+ return CANONICAL_ELEMENTS[String(elem).trim().toLowerCase()] || elem;
+}
+
+function normalizePath(p) {
+ if (!p) return 'Destruction';
+ return CANONICAL_PATHS[String(p).trim().toLowerCase()] || (p === 'The Hunt' ? 'Hunt' : p);
+}
+
function getElementClass(elem) {
+ const canonical = normalizeElement(elem);
const map = {
Fire: 'elem-fire',
Ice: 'elem-ice',
@@ -246,30 +305,31 @@ function getElementClass(elem) {
Quantum: 'elem-quantum',
Imaginary: 'elem-imaginary'
};
- return map[elem] || 'elem-physical';
+ return map[canonical] || 'elem-physical';
}
// Render Element Icon HTML (Official Star Rail Game Icon)
function getElementIconHtml(element, size = 20) {
if (!element) return '';
- const elemClass = getElementClass(element);
+ const canonical = normalizeElement(element);
+ const elemClass = getElementClass(canonical);
return `
-
+ onerror="if(this.src.indexOf('StarRailRes')===-1){this.src='https://raw.githubusercontent.com/Mar-7th/StarRailRes/master/icon/element/${canonical === 'Lightning' ? 'Thunder' : canonical}.png';}">
`;
}
// Render Path Icon HTML (Official Star Rail Game Icon)
function getPathIconHtml(path, size = 20) {
if (!path) return '';
- const safePath = path === 'The Hunt' ? 'Hunt' : path;
+ const canonical = normalizePath(path);
return `
-
+ onerror="if(this.src.indexOf('StarRailRes')===-1){this.src='https://raw.githubusercontent.com/Mar-7th/StarRailRes/master/icon/path/${canonical}.png';}">
`;
}
@@ -442,6 +502,7 @@ function initAvatarsList() {
rank: 6
});
}
+ list.sort((a, b) => b.avatar_id - a.avatar_id);
state.avatarsList = list;
}
@@ -580,8 +641,9 @@ function renderPickerGrid() {
});
el.pickerAvatarsGrid.appendChild(removeCard);
- // Available avatars
- state.avatarsList.forEach(item => {
+ // Available avatars (sorted descending by avatar_id)
+ const sortedAvatars = [...state.avatarsList].sort((a, b) => b.avatar_id - a.avatar_id);
+ sortedAvatars.forEach(item => {
const meta = getAvatarInfo(item.avatar_id);
if (search && !meta.name.toLowerCase().includes(search) && !String(item.avatar_id).includes(search)) {
return;
@@ -665,6 +727,7 @@ async function loadAvatars() {
console.warn('Live avatar list fetch failed, using local list:', err);
}
}
+ state.avatarsList.sort((a, b) => b.avatar_id - a.avatar_id);
renderAvatarsGrid();
}
@@ -678,11 +741,15 @@ function renderAvatarsGrid() {
const meta = getAvatarInfo(item.avatar_id);
const matchesSearch = meta.name.toLowerCase().includes(searchTerm) || String(item.avatar_id).includes(searchTerm);
const matchesRarity = rarityFilter === 'all' || String(meta.rarity) === rarityFilter;
- const matchesElement = elementFilter === 'all' || meta.element === elementFilter;
- const matchesPath = pathFilter === 'all' || meta.path === pathFilter || (pathFilter === 'Hunt' && (meta.path === 'Hunt' || meta.path === 'The Hunt'));
+ const elemCanonical = normalizeElement(meta.rawElement || meta.element);
+ const matchesElement = elementFilter === 'all' || elemCanonical === normalizeElement(elementFilter);
+ const pathCanonical = normalizePath(meta.rawPath || meta.path);
+ const matchesPath = pathFilter === 'all' || pathCanonical === normalizePath(pathFilter);
return matchesSearch && matchesRarity && matchesElement && matchesPath;
});
+ filtered.sort((a, b) => b.avatar_id - a.avatar_id);
+
if (filtered.length === 0) {
el.avatarsGrid.innerHTML = `
${t('noAvatarsFound')}
`;
return;
@@ -1294,9 +1361,25 @@ async function loadEquipment() {
Lv. ${lc.level} • S${lc.rank} • ${t('equippedBy')}:
${equipped}