From 7fad0ac37222d740fb7e55c19f380981b80186bb Mon Sep 17 00:00:00 2001
From: Mistivia <i@mistivia.com>
Date: Sat, 4 Oct 2025 04:11:24 +0800
Subject: add english japanese cardtext

---
 src/components/left_panel.svelte |  26 ++++--
 src/left_panel.js                | 191 ++++++++++++++++++++++++++++++++++-----
 2 files changed, 189 insertions(+), 28 deletions(-)

diff --git a/src/components/left_panel.svelte b/src/components/left_panel.svelte
index c3171a0..d9d0db0 100644
--- a/src/components/left_panel.svelte
+++ b/src/components/left_panel.svelte
@@ -4,6 +4,7 @@
         leftPanelCardId,
         leftPanelCardImgUrl,
         leftPanelCardDesc,
+        leftPanelCardRuby,
         isMobileInfoVisible,
         closeMobileInfo,
     } from '../left_panel';
@@ -21,6 +22,9 @@
             {/if}
         </div>
         <div class="card-description">
+            {#if $leftPanelCardRuby !== ''}
+                <p class="card-desc-ruby">{@html $leftPanelCardRuby}</p>
+            {/if}
             <pre class="card-desc-text">{$leftPanelCardDesc}</pre>
             <div style="height:3em;line-height:1.0;overflow:hidden;">
                 <div style="break-inside:avoid;"><p style="text-align:center;"><small>
@@ -43,6 +47,9 @@
                 {/if}
             </div>
             <div class="card-description">
+                {#if $leftPanelCardRuby !== ''}
+                    <p class="card-desc-ruby">{@html $leftPanelCardRuby}</p>
+                {/if}
                 <pre class="card-desc-text">{$leftPanelCardDesc}</pre>
             </div>
         </div>
@@ -82,24 +89,29 @@
           word-wrap: break-word;
           overflow: auto;
     }
+    .card-desc-ruby {
+          white-space: pre-wrap;
+          word-wrap: break-word;
+          overflow: auto;
+    }
     
     a.link {
-        color: #000000; /* 文本颜色设置为黑色 */
-        text-decoration: underline; /* 添加下划线 */
-        cursor: pointer; /* 鼠标悬停时显示手形指针 */
+        color: #000000;
+        text-decoration: underline;
+        cursor: pointer;
     }
     
     a.link:visited {
-        color: #000000; /* 已访问链接颜色保持黑色 */
+        color: #000000;
     }
     
     a.link:hover {
-        color: #000000; /* 悬停时颜色不变 */
-        text-decoration: underline; /* 悬停时保持下划线 */
+        color: #000000;
+        text-decoration: underline;
     }
     
     a.link:active {
-        color: #000000; /* 点击时颜色不变 */
+        color: #000000;
     }
 
     @media screen and (max-width: 768px) {
diff --git a/src/left_panel.js b/src/left_panel.js
index 1ec3e92..c2691ee 100644
--- a/src/left_panel.js
+++ b/src/left_panel.js
@@ -1,13 +1,20 @@
 import { writable } from 'svelte/store';
 import { cardImageUrl } from './utils';
+import { currentTranslations } from './language';
+import { get } from 'svelte/store';
 
 let leftPanelCardId = writable('');
 let leftPanelCardDesc = writable('');
+let leftPanelCardRuby = writable('');
 let isMobileInfoVisible = writable(false);
 let leftPanelCardImgUrl = writable('');
 
 let curVersion = 0;
-let descCache = new Map();
+let rubyCache = new Map();
+let descCache = {};
+descCache.cn = new Map();
+descCache.en = new Map();
+descCache.jp = new Map();
 
 function showMobileInfo() {
     isMobileInfoVisible.set(true);
@@ -32,7 +39,7 @@ function setLeftPanelCard(id, lang) {
     leftPanelCardId.set(id);
     leftPanelCardImgUrl.set('');
     curVersion += 1;
-    leftPanelCardDesc.set('加载中...');
+    leftPanelCardDesc.set('Loading...');
     let ver = curVersion;
     setDesc(ver, id);
     let url = '';
@@ -48,28 +55,169 @@ function setLeftPanelCard(id, lang) {
     });
 }
 
+function toEnglish(meta) {
+    let ret = "";
+    ret += meta.name.en + '\n';
+    if (meta.card_type === 'Monster') {
+        if (meta.hasOwnProperty('monster_type_line')) {
+            ret += '[' + meta.monster_type_line + '] '
+        }
+        if (meta.hasOwnProperty('attribute')) {
+            ret += meta.attribute + ' ';
+        }
+        ret += '\n'
+        if (meta.hasOwnProperty('level')) {
+            ret += 'Level ' + meta.level.toString();
+        }
+        if (meta.hasOwnProperty('rank')) {
+            ret += 'Rank ' + meta.rank.toString();
+        }
+        if (meta.hasOwnProperty('link_arrows')) {
+            ret += 'Link ' + meta.link_arrows.length.toString();
+        }
+        if (meta.hasOwnProperty('atk')) {
+            ret += ' | ATK ' + meta.atk.toString();
+        }
+        if (meta.hasOwnProperty('def')) {
+            ret += ' | DEF ' + meta.atk.toString();
+        }
+        if (meta.hasOwnProperty('link_arrows')) {
+            ret += ' | [' + meta.link_arrows.join(' ') + ']'
+        }
+        if (meta.hasOwnProperty('pendulum_scale')) {
+            ret += ' | P' + meta.pendulum_scale.toString();
+        }
+        if (meta.hasOwnProperty('pendulum_effect')) {
+            ret += '\n---\n' + meta.pendulum_effect.en + '\n---';
+        }
+        ret += '\n'
+    } else {
+        ret += '[' + meta.card_type;
+        if (meta.hasOwnProperty('property')) {
+            ret += ' / ' + meta.property;
+        }
+        ret += ']\n';
+    }
+    ret += meta.text.en;
+
+    return [ret, ''];
+}
+
+function toJapanese(meta) {
+    let ruby = '';
+    let ret = '';
+    ruby = meta.name.ja;
+    if (meta.card_type === 'Monster') {
+        if (meta.hasOwnProperty('monster_type_line')) {
+            ret += '[' + meta.monster_type_line + '] '
+        }
+        if (meta.hasOwnProperty('attribute')) {
+            ret += meta.attribute + ' ';
+        }
+        ret += '\n'
+        if (meta.hasOwnProperty('level')) {
+            ret += 'Level ' + meta.level.toString();
+        }
+        if (meta.hasOwnProperty('rank')) {
+            ret += 'Rank ' + meta.rank.toString();
+        }
+        if (meta.hasOwnProperty('link_arrows')) {
+            ret += 'Link ' + meta.link_arrows.length.toString();
+        }
+        if (meta.hasOwnProperty('atk')) {
+            ret += ' | ATK ' + meta.atk.toString();
+        }
+        if (meta.hasOwnProperty('def')) {
+            ret += ' | DEF ' + meta.atk.toString();
+        }
+        if (meta.hasOwnProperty('link_arrows')) {
+            ret += ' | [' + meta.link_arrows.join(' ') + ']'
+        }
+        if (meta.hasOwnProperty('pendulum_scale')) {
+            ret += ' | P' + meta.pendulum_scale.toString();
+        }
+        if (meta.hasOwnProperty('pendulum_effect')) {
+            ret += '\n---\n' + meta.pendulum_effect.ja + '\n---';
+        }
+        ret += '\n'
+    } else {
+        ret += '[' + meta.card_type;
+        if (meta.hasOwnProperty('property')) {
+            ret += ' / ' + meta.property;
+        }
+        ret += ']\n';
+    }
+    ret += meta.text.ja;
+    
+    return [ret, ruby];
+}
+
+function cardJsonToText(meta, lang) {
+    try {
+        if (lang === 'en')
+            return toEnglish(meta);
+        else
+            return toJapanese(meta);
+    } catch(e) {
+        console.log(e);
+        return "Err!";
+    }
+}
+
 function setDesc(version, id) {
-    if (descCache.has(id)) {
-        leftPanelCardDesc.set(descCache.get(id));
+    let lang = get(currentTranslations).key;
+    leftPanelCardRuby.set('');
+    if (rubyCache.has(id) && lang === 'jp') {
+        leftPanelCardRuby.set(rubyCache.get(id));
+    }
+    if (descCache[lang].has(id)) {
+        leftPanelCardDesc.set(descCache[lang].get(id));
         return;
     }
-    let descUrl = 'https://raye.mistivia.com/cardtext/' + id + '.txt';
-    fetch(descUrl)
-        .then((response) => {
-            if (!response.ok) {
-                throw new Error(`HTTP error! status: ${response.status}`);
-            }
-            return response.text(); 
-        })
-        .then((desc) => {
-            descCache.set(id, desc);
-            if (version === curVersion) {
-                leftPanelCardDesc.set(desc);
-            }
-        })
-        .catch((error) => {
-            console.error('Error fetching the file:', error);
-        });
+    if (lang === 'cn') {
+        let descUrl = 'https://raye.mistivia.com/cardtext/' + id + '.txt';
+        fetch(descUrl)
+            .then((response) => {
+                if (!response.ok) {
+                    throw new Error(`HTTP error! status: ${response.status}`);
+                }
+                return response.text(); 
+            })
+            .then((desc) => {
+                descCache[lang].set(id, desc);
+                if (version === curVersion) {
+                    leftPanelCardDesc.set(desc);
+                }
+            })
+            .catch((error) => {
+                console.error('Error fetching the file:', error);
+            });
+    } else {
+        let descUrl = 'https://raye.mistivia.com/cardtext/' + id.padStart(8, '0') + '.json';
+        fetch(descUrl)
+            .then((response) => {
+                if (!response.ok) {
+                    throw new Error(`HTTP error! status: ${response.status}`);
+                }
+                return response.json();
+            })
+            .then((descJson) => {
+                let ret = cardJsonToText(descJson, lang);
+                let ruby = ret[1];
+                let desc = ret[0];
+                descCache[lang].set(id, desc);
+                if (ruby !== '') {
+                    rubyCache.set(id, ruby);
+                }
+                if (version === curVersion) {
+                    leftPanelCardRuby.set(ruby);
+                    leftPanelCardDesc.set(desc);
+                }
+            })
+            .catch((error) => {
+                console.error('Error fetching the file:', error);
+            });
+    }
 }
 
 export {
@@ -80,4 +228,5 @@ export {
     closeMobileInfo,
     isMobileInfoVisible,
     leftPanelCardImgUrl,
+    leftPanelCardRuby
 };
-- 
cgit v1.0

