From 2ebdfe109bbff27b35a5e9ae208dabddd266191e Mon Sep 17 00:00:00 2001
From: Mistivia <i@mistivia.com>
Date: Thu, 20 Feb 2025 13:26:43 +0800
Subject: change project layout

---
 src/App.svelte                    |  14 ----
 src/app.svelte                    |  14 ++++
 src/card_db.js                    |  25 ++++++
 src/components/CardThumb.svelte   |  35 --------
 src/components/LeftPanel.svelte   |  80 ------------------
 src/components/Loading.svelte     |  52 ------------
 src/components/MainPanel.svelte   | 171 --------------------------------------
 src/components/RightPanel.svelte  |  97 ---------------------
 src/components/card_thumb.svelte  |  33 ++++++++
 src/components/left_panel.svelte  |  80 ++++++++++++++++++
 src/components/loading.svelte     |  52 ++++++++++++
 src/components/main_panel.svelte  | 171 ++++++++++++++++++++++++++++++++++++++
 src/components/right_panel.svelte |  97 +++++++++++++++++++++
 src/control/deck.js               | 134 -----------------------------
 src/control/left_panel.js         |  45 ----------
 src/control/loading.js            |  57 -------------
 src/control/search.js             |  79 ------------------
 src/data/cardDb.js                |  25 ------
 src/deck.js                       | 134 +++++++++++++++++++++++++++++
 src/left_panel.js                 |  45 ++++++++++
 src/loading.js                    |  57 +++++++++++++
 src/main.ts                       |   2 +-
 src/search.js                     |  79 ++++++++++++++++++
 23 files changed, 788 insertions(+), 790 deletions(-)
 delete mode 100644 src/App.svelte
 create mode 100644 src/app.svelte
 create mode 100644 src/card_db.js
 delete mode 100644 src/components/CardThumb.svelte
 delete mode 100644 src/components/LeftPanel.svelte
 delete mode 100644 src/components/Loading.svelte
 delete mode 100644 src/components/MainPanel.svelte
 delete mode 100644 src/components/RightPanel.svelte
 create mode 100644 src/components/card_thumb.svelte
 create mode 100644 src/components/left_panel.svelte
 create mode 100644 src/components/loading.svelte
 create mode 100644 src/components/main_panel.svelte
 create mode 100644 src/components/right_panel.svelte
 delete mode 100644 src/control/deck.js
 delete mode 100644 src/control/left_panel.js
 delete mode 100644 src/control/loading.js
 delete mode 100644 src/control/search.js
 delete mode 100644 src/data/cardDb.js
 create mode 100644 src/deck.js
 create mode 100644 src/left_panel.js
 create mode 100644 src/loading.js
 create mode 100644 src/search.js

diff --git a/src/App.svelte b/src/App.svelte
deleted file mode 100644
index 09b69c3..0000000
--- a/src/App.svelte
+++ /dev/null
@@ -1,14 +0,0 @@
-<script lang="ts">
-
-import LeftPanel from './components/LeftPanel.svelte';
-import RightPanel from './components/RightPanel.svelte';
-import MainPanel from './components/MainPanel.svelte';
-import Loading from './components/Loading.svelte';
-
-</script>
-
-<LeftPanel />
-<MainPanel />
-<RightPanel />
-<Loading />
-
diff --git a/src/app.svelte b/src/app.svelte
new file mode 100644
index 0000000..399214b
--- /dev/null
+++ b/src/app.svelte
@@ -0,0 +1,14 @@
+<script lang="ts">
+
+import LeftPanel from './components/left_panel.svelte';
+import RightPanel from './components/right_panel.svelte';
+import MainPanel from './components/main_panel.svelte';
+import Loading from './components/loading.svelte';
+
+</script>
+
+<LeftPanel />
+<MainPanel />
+<RightPanel />
+<Loading />
+
diff --git a/src/card_db.js b/src/card_db.js
new file mode 100644
index 0000000..b9b991f
--- /dev/null
+++ b/src/card_db.js
@@ -0,0 +1,25 @@
+let cardDb = {};
+let altId = {};
+
+function setCardDb(d) {
+    cardDb = d;
+}
+
+function getCardDb() {
+    return cardDb;
+}
+
+function getAltId() {
+    return altId;
+}
+
+function setAltId(x) {
+    altId = x;
+}
+
+export {
+    getCardDb,
+    setCardDb,
+    getAltId,
+    setAltId,
+};
diff --git a/src/components/CardThumb.svelte b/src/components/CardThumb.svelte
deleted file mode 100644
index 2f722a9..0000000
--- a/src/components/CardThumb.svelte
+++ /dev/null
@@ -1,35 +0,0 @@
-<script lang="js">
-
-    import {setLeftPanelCard} from '../control/left_panel';
-    import { cardImageUrl } from '../utils';
-
-    let {id, area, idx} = $props();
-
-    function onhover() {
-        setLeftPanelCard(id); 
-    }
-
-    function onDragStart(e) {
-        e.dataTransfer.setData('text', JSON.stringify({id, area, idx}))
-    }
-
-</script>
-
-{#if id}
-    <img
-        style="margin:2px;"
-        draggable="true"
-        onmouseover={onhover}
-        onfocus={onhover}
-        ondragstart={onDragStart}
-        height="100%"
-        src={cardImageUrl(id)}
-        alt="yugioh card {id}"
-    />
-{/if}
-
-<style>
-
-
-
-</style>
diff --git a/src/components/LeftPanel.svelte b/src/components/LeftPanel.svelte
deleted file mode 100644
index 90937f2..0000000
--- a/src/components/LeftPanel.svelte
+++ /dev/null
@@ -1,80 +0,0 @@
-<script lang="js">
-
-    import {
-        leftPanelCardId,
-        leftPanelCardDesc,
-    } from '../control/left_panel';
-
-    import { cardImageUrl } from '../utils';
-
-
-</script>
-
-<div class="left-panel">
-    <div class="card-preview">
-        <div class="card-image-large">
-            {#if $leftPanelCardId}
-                <img height="100%" src={cardImageUrl($leftPanelCardId)} alt="card img">
-            {/if}
-        </div>
-        <div class="card-description">
-            <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>
-                    <a class="link" href="https://github.com/mistivia/ygo-deck-builder">GitHub</a>
-                    <br>关注 <a class="link" href="https://mistivia.com">Mistivia</a> 谢谢喵~
-                </small></p></div>
-            </div>
-        </div>
-    </div>
-</div>
-
-<style>
-
-.left-panel {
-    width: 25%;
-    padding: 2vh 20px;
-    background-color: #f5f5f5;
-}
-
-.card-image-large {
-    height: 40vh;
-    text-align: center;
-    background-color: #ddd;
-    margin-bottom: 20px;
-    border-radius: 8px;
-}
-
-.card-description {
-    line-height: 1.4;
-    font-size: 14px;
-    height: 50vh;
-}
-
-.card-desc-text {
-      height: calc(50vh - 3em); 
-      white-space: pre-wrap;
-      word-wrap: break-word;
-      overflow: auto;
-}
-
-a.link {
-    color: #000000; /* 文本颜色设置为黑色 */
-    text-decoration: underline; /* 添加下划线 */
-    cursor: pointer; /* 鼠标悬停时显示手形指针 */
-}
-
-a.link:visited {
-    color: #000000; /* 已访问链接颜色保持黑色 */
-}
-
-a.link:hover {
-    color: #000000; /* 悬停时颜色不变 */
-    text-decoration: underline; /* 悬停时保持下划线 */
-}
-
-a.link:active {
-    color: #000000; /* 点击时颜色不变 */
-}
-
-</style>
diff --git a/src/components/Loading.svelte b/src/components/Loading.svelte
deleted file mode 100644
index 5fb6f9a..0000000
--- a/src/components/Loading.svelte
+++ /dev/null
@@ -1,52 +0,0 @@
-<script>
-    import { isLoading } from '../control/loading';
-</script>
-
-{#if $isLoading}
-    <div id="loading">
-        <div class="loading-content">
-            <div class="loading-spinner"></div>
-            <br>
-            <p>加载中...</p>
-        </div>
-    </div>
-{/if}
-
-
-<style>
-    /* 全屏 loading 样式 */
-    #loading {
-        position: fixed;
-        top: 0;
-        left: 0;
-        width: 100%;
-        height: 100%;
-        background: rgba(255, 255, 255, 0.9); /* 半透明背景 */
-        z-index: 9999; /* 确保在最前面 */
-        display: block; /* 默认隐藏 */
-    }
-
-    /* loading 内容区域 */
-    .loading-content {
-        position: absolute;
-        top: 50%;
-        left: 50%;
-        transform: translate(-50%, -50%);
-        text-align: center;
-    }
-
-    /* loading 动画 */
-    .loading-spinner {
-        width: 50px;
-        height: 50px;
-        border: 5px solid #f3f3f3;
-        border-top: 5px solid #3498db;
-        border-radius: 50%;
-        animation: spin 1s linear infinite;
-    }
-
-    @keyframes spin {
-        0% { transform: rotate(0deg); }
-        100% { transform: rotate(360deg); }
-    }
-</style>
diff --git a/src/components/MainPanel.svelte b/src/components/MainPanel.svelte
deleted file mode 100644
index f023dd9..0000000
--- a/src/components/MainPanel.svelte
+++ /dev/null
@@ -1,171 +0,0 @@
-<script lang="js">
-    import CardThumb from './CardThumb.svelte';
-    import { deck, setDeck, deckOps } from '../control/deck';
-    import {
-        parseYdk,
-        genYdk,
-        genYdke,
-        downloadStringAsFile,
-    } from '../utils';
-
-    let fileInput;
-    
-    function openDeck() {
-        fileInput.click();
-    }
-
-    function loadDeck(event) {
-        const file = event.target.files[0];
-        if (file) {
-            const reader = new FileReader();
-            reader.onload = (event) => {
-                let content = event.target.result;
-                setDeck(parseYdk(content));
-            };
-            reader.readAsText(file); 
-        }
-    }
-
-    function saveDeck() {
-        let deckString = genYdk($deck);
-        downloadStringAsFile(deckString)
-    }
-
-    function copyDeck() {
-        let deckString = genYdk($deck);
-        navigator.clipboard.writeText(deckString)
-            .then(() => {
-              alert('YDK卡组码已复制到剪贴板');
-            })
-            .catch(err => {
-              alert("失败！");
-            });
-    }
-
-    function shareDeck() {
-        let url = window.location.href;
-        url = url.split('#')[0]
-        url = url + '#' + genYdke($deck);
-        navigator.clipboard.writeText(url)
-            .then(() => {
-              alert('分享链接已复制到剪贴板');
-            })
-            .catch(err => {
-              alert("失败！");
-            });
-    }
-
-    function onDrop(to, event, targetIdx) {
-        event.preventDefault();
-        event.stopPropagation();
-        const data = JSON.parse(event.dataTransfer.getData('text'));
-        let from = data.area;
-        if (from === 'search') {
-            if (to === 'main') {
-                deckOps.add2main(data.id, targetIdx);
-            } else if (to === 'side') {
-                deckOps.add2side(data.id, targetIdx);
-            } else if (to === 'extra') {
-                deckOps.add2extra(data.id, targetIdx);
-            }
-        } else {
-            deckOps.move(from, to, data.idx, targetIdx);
-        }
-    }
-
-
-</script>
-
-<input bind:this={fileInput} style="display:none;" onchange={loadDeck} type="file" class="file-input" accept=".ydk" />
-
-<div class="middle-panel">
-    <div class="control-bar">
-        <button class="btn" onclick={openDeck}>打开</button>
-        <button class="btn" onclick={saveDeck}>保存</button>
-        <button class="btn" onclick={copyDeck}>复制到剪贴板</button>
-        <button class="btn" onclick={shareDeck}>分享</button>
-    </div>
-    <div class="deck-section">
-        <div class="deck-group">
-            <h3>主卡组（{$deck.main.length}）</h3>
-            <div role="region" ondragover={(e)=>e.preventDefault()} ondrop={(e)=>onDrop("main", e, -1)} class="card-grid main-deck">
-                {#each $deck.main as card, i}
-                    <div class="card-grid-thumb" role="region" ondragover={(e)=>e.preventDefault()} ondrop={(e)=>onDrop("main", e, i)}>
-                        <CardThumb id={card} idx={i} area="main" />
-                    </div>
-                {/each}
-            </div>
-        </div>
-        <div class="deck-group">
-            <h3>额外卡组（{$deck.extra.length}）</h3>
-            <div role="region" ondragover={(e)=>e.preventDefault()} ondrop={(e)=>onDrop("extra", e, -1)} class="card-grid extra-deck">
-                {#each $deck.extra as card, i}
-                    <div class="card-grid-thumb" role="region" ondragover={(e)=>e.preventDefault()} ondrop={(e)=>onDrop("extra", e, i)}>
-                        <CardThumb id={card} idx={i} area="extra" />
-                    </div>
-                {/each}
-            </div>
-        </div>
-        <div class="deck-group">
-            <h3>副卡组（{$deck.side.length}）</h3>
-            <div role="region" ondragover={(e)=>e.preventDefault()} ondrop={(e)=>onDrop("side", e, -1)} class="card-grid side-deck">
-                {#each $deck.side as card, i}
-                    <div class="card-grid-thumb" role="region" ondragover={(e)=>e.preventDefault()} ondrop={(e)=>onDrop("side", e, i)}>
-                        <CardThumb id={card} idx={i} area="side" />
-                    </div>
-                {/each}
-            </div>
-        </div>
-    </div>
-</div>
-
-<style>
-
-.middle-panel {
-    width: 55%;
-    padding: 20px;
-    background-color: #fff;
-    overflow-y: auto;
-}
-
-.control-bar {
-    margin-bottom: 20px;
-}
-
-.btn {
-    padding: 8px 20px;
-    margin-right: 10px;
-    background-color: #4CAF50;
-    color: white;
-    border: none;
-    border-radius: 4px;
-    cursor: pointer;
-}
-
-.deck-group {
-    margin-bottom: 30px;
-}
-
-.deck-group h3 {
-    margin-bottom: 10px;
-    color: #333;
-}
-
-.card-grid {
-    display: grid;
-    grid-template-columns: repeat(auto-fill, minmax(52px, 1fr));
-    grid-auto-flow: dense;
-    overflow-y: auto;
-    padding: 10px;
-    border: 1px solid #ddd;
-    border-radius: 4px;
-    min-height: 80px;
-}
-
-.card-grid-thumb {
-    aspect-ratio: 1/1.4;
-    border-radius: 5px;
-}
-
-
-</style>
diff --git a/src/components/RightPanel.svelte b/src/components/RightPanel.svelte
deleted file mode 100644
index c9904fa..0000000
--- a/src/components/RightPanel.svelte
+++ /dev/null
@@ -1,97 +0,0 @@
-<script lang="js">
-    import CardThumb from './CardThumb.svelte';
-    import { changeInput, showingCards, onPrevPage, onNextPage } from '../control/search'
-    import { deckOps } from '../control/deck';
-
-    function onChange(event) {
-        changeInput(event.target.value);
-    }
-
-    function onDrop(event) {
-        event.preventDefault();
-        const data = JSON.parse(event.dataTransfer.getData('text'));
-        if (data.area === 'main' || data.area === 'side' || data.area === 'extra') {
-            deckOps.deleteCard(data.area, data.idx);
-        }
-    }
-</script>
-
-<div class="right-panel" role="region" ondragover={(e)=>e.preventDefault()} ondrop={onDrop}>
-    <div class="search-bar">
-        <input type="text" placeholder="搜索卡牌..." oninput={onChange}>
-    </div>
-    <div class="card-list">
-           {#each $showingCards as card}
-               <div class="card-item">
-                   <div class="card-thumbnail">
-                       <CardThumb id={card.id} idx={-1} area="search" />
-                   </div>
-                   <span>{card.name}</span>
-               </div>
-           {/each}
-    </div>
-    <div class="pagination">
-        <button class="page-btn" onclick={onPrevPage}>上一页</button>
-        <button class="page-btn" onclick={onNextPage}>下一页</button>
-    </div>
-</div>
-
-
-<style>
-
-.right-panel {
-    width: 20%;
-    padding: 20px;
-    background-color: #f8f8f8;
-    display: flex;
-    flex-direction: column;
-}
-
-.search-bar input {
-    width: 100%;
-    padding: 8px;
-    margin-bottom: 15px;
-    border: 1px solid #ddd;
-    border-radius: 4px;
-}
-
-.card-list {
-    flex-grow: 1;
-    overflow-y: auto;
-    margin-bottom: 15px;
-}
-
-.card-item {
-    display: flex;
-    align-items: center;
-    padding: 8px;
-    margin-bottom: 5px;
-    background-color: white;
-    border-radius: 4px;
-    cursor: pointer;
-}
-
-.pagination {
-    display: flex;
-    gap: 10px;
-}
-
-.page-btn {
-    flex: 1;
-    padding: 8px;
-    background-color: #007bff;
-    color: white;
-    border: none;
-    border-radius: 4px;
-    cursor: pointer;
-}
-
-.card-thumbnail {
-    width: 50px;
-    height: 70px;
-    background-color: #eee;
-    margin-right: 10px;
-    border-radius: 3px;
-}
-
-</style>
diff --git a/src/components/card_thumb.svelte b/src/components/card_thumb.svelte
new file mode 100644
index 0000000..8e701ea
--- /dev/null
+++ b/src/components/card_thumb.svelte
@@ -0,0 +1,33 @@
+<script lang="js">
+
+    import { setLeftPanelCard } from '../left_panel';
+    import { cardImageUrl } from '../utils';
+
+    let {id, area, idx} = $props();
+
+    function onhover() {
+        setLeftPanelCard(id); 
+    }
+
+    function onDragStart(e) {
+        e.dataTransfer.setData('text', JSON.stringify({id, area, idx}))
+    }
+
+</script>
+
+{#if id}
+    <img
+        style="margin:2px;"
+        draggable="true"
+        onmouseover={onhover}
+        onfocus={onhover}
+        ondragstart={onDragStart}
+        height="100%"
+        src={cardImageUrl(id)}
+        alt="yugioh card {id}"
+    />
+{/if}
+
+<style>
+
+</style>
diff --git a/src/components/left_panel.svelte b/src/components/left_panel.svelte
new file mode 100644
index 0000000..d69199e
--- /dev/null
+++ b/src/components/left_panel.svelte
@@ -0,0 +1,80 @@
+<script lang="js">
+
+    import {
+        leftPanelCardId,
+        leftPanelCardDesc,
+    } from '../left_panel';
+
+    import { cardImageUrl } from '../utils';
+
+
+</script>
+
+<div class="left-panel">
+    <div class="card-preview">
+        <div class="card-image-large">
+            {#if $leftPanelCardId}
+                <img height="100%" src={cardImageUrl($leftPanelCardId)} alt="card img">
+            {/if}
+        </div>
+        <div class="card-description">
+            <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>
+                    <a class="link" href="https://github.com/mistivia/ygo-deck-builder">GitHub</a>
+                    <br>关注 <a class="link" href="https://mistivia.com">Mistivia</a> 谢谢喵~
+                </small></p></div>
+            </div>
+        </div>
+    </div>
+</div>
+
+<style>
+
+.left-panel {
+    width: 25%;
+    padding: 2vh 20px;
+    background-color: #f5f5f5;
+}
+
+.card-image-large {
+    height: 40vh;
+    text-align: center;
+    background-color: #ddd;
+    margin-bottom: 20px;
+    border-radius: 8px;
+}
+
+.card-description {
+    line-height: 1.4;
+    font-size: 14px;
+    height: 50vh;
+}
+
+.card-desc-text {
+      height: calc(50vh - 3em); 
+      white-space: pre-wrap;
+      word-wrap: break-word;
+      overflow: auto;
+}
+
+a.link {
+    color: #000000; /* 文本颜色设置为黑色 */
+    text-decoration: underline; /* 添加下划线 */
+    cursor: pointer; /* 鼠标悬停时显示手形指针 */
+}
+
+a.link:visited {
+    color: #000000; /* 已访问链接颜色保持黑色 */
+}
+
+a.link:hover {
+    color: #000000; /* 悬停时颜色不变 */
+    text-decoration: underline; /* 悬停时保持下划线 */
+}
+
+a.link:active {
+    color: #000000; /* 点击时颜色不变 */
+}
+
+</style>
diff --git a/src/components/loading.svelte b/src/components/loading.svelte
new file mode 100644
index 0000000..fb49495
--- /dev/null
+++ b/src/components/loading.svelte
@@ -0,0 +1,52 @@
+<script>
+    import { isLoading } from '../loading';
+</script>
+
+{#if $isLoading}
+    <div id="loading">
+        <div class="loading-content">
+            <div class="loading-spinner"></div>
+            <br>
+            <p>加载中...</p>
+        </div>
+    </div>
+{/if}
+
+
+<style>
+    /* 全屏 loading 样式 */
+    #loading {
+        position: fixed;
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 100%;
+        background: rgba(255, 255, 255, 0.9); /* 半透明背景 */
+        z-index: 9999; /* 确保在最前面 */
+        display: block; /* 默认隐藏 */
+    }
+
+    /* loading 内容区域 */
+    .loading-content {
+        position: absolute;
+        top: 50%;
+        left: 50%;
+        transform: translate(-50%, -50%);
+        text-align: center;
+    }
+
+    /* loading 动画 */
+    .loading-spinner {
+        width: 50px;
+        height: 50px;
+        border: 5px solid #f3f3f3;
+        border-top: 5px solid #3498db;
+        border-radius: 50%;
+        animation: spin 1s linear infinite;
+    }
+
+    @keyframes spin {
+        0% { transform: rotate(0deg); }
+        100% { transform: rotate(360deg); }
+    }
+</style>
diff --git a/src/components/main_panel.svelte b/src/components/main_panel.svelte
new file mode 100644
index 0000000..2db7a34
--- /dev/null
+++ b/src/components/main_panel.svelte
@@ -0,0 +1,171 @@
+<script lang="js">
+    import CardThumb from './card_thumb.svelte';
+    import { deck, setDeck, deckOps } from '../deck';
+    import {
+        parseYdk,
+        genYdk,
+        genYdke,
+        downloadStringAsFile,
+    } from '../utils';
+
+    let fileInput;
+    
+    function openDeck() {
+        fileInput.click();
+    }
+
+    function loadDeck(event) {
+        const file = event.target.files[0];
+        if (file) {
+            const reader = new FileReader();
+            reader.onload = (event) => {
+                let content = event.target.result;
+                setDeck(parseYdk(content));
+            };
+            reader.readAsText(file); 
+        }
+    }
+
+    function saveDeck() {
+        let deckString = genYdk($deck);
+        downloadStringAsFile(deckString)
+    }
+
+    function copyDeck() {
+        let deckString = genYdk($deck);
+        navigator.clipboard.writeText(deckString)
+            .then(() => {
+              alert('YDK卡组码已复制到剪贴板');
+            })
+            .catch(err => {
+              alert("失败！");
+            });
+    }
+
+    function shareDeck() {
+        let url = window.location.href;
+        url = url.split('#')[0]
+        url = url + '#' + genYdke($deck);
+        navigator.clipboard.writeText(url)
+            .then(() => {
+              alert('分享链接已复制到剪贴板');
+            })
+            .catch(err => {
+              alert("失败！");
+            });
+    }
+
+    function onDrop(to, event, targetIdx) {
+        event.preventDefault();
+        event.stopPropagation();
+        const data = JSON.parse(event.dataTransfer.getData('text'));
+        let from = data.area;
+        if (from === 'search') {
+            if (to === 'main') {
+                deckOps.add2main(data.id, targetIdx);
+            } else if (to === 'side') {
+                deckOps.add2side(data.id, targetIdx);
+            } else if (to === 'extra') {
+                deckOps.add2extra(data.id, targetIdx);
+            }
+        } else {
+            deckOps.move(from, to, data.idx, targetIdx);
+        }
+    }
+
+
+</script>
+
+<input bind:this={fileInput} style="display:none;" onchange={loadDeck} type="file" class="file-input" accept=".ydk" />
+
+<div class="middle-panel">
+    <div class="control-bar">
+        <button class="btn" onclick={openDeck}>打开</button>
+        <button class="btn" onclick={saveDeck}>保存</button>
+        <button class="btn" onclick={copyDeck}>复制到剪贴板</button>
+        <button class="btn" onclick={shareDeck}>分享</button>
+    </div>
+    <div class="deck-section">
+        <div class="deck-group">
+            <h3>主卡组（{$deck.main.length}）</h3>
+            <div role="region" ondragover={(e)=>e.preventDefault()} ondrop={(e)=>onDrop("main", e, -1)} class="card-grid main-deck">
+                {#each $deck.main as card, i}
+                    <div class="card-grid-thumb" role="region" ondragover={(e)=>e.preventDefault()} ondrop={(e)=>onDrop("main", e, i)}>
+                        <CardThumb id={card} idx={i} area="main" />
+                    </div>
+                {/each}
+            </div>
+        </div>
+        <div class="deck-group">
+            <h3>额外卡组（{$deck.extra.length}）</h3>
+            <div role="region" ondragover={(e)=>e.preventDefault()} ondrop={(e)=>onDrop("extra", e, -1)} class="card-grid extra-deck">
+                {#each $deck.extra as card, i}
+                    <div class="card-grid-thumb" role="region" ondragover={(e)=>e.preventDefault()} ondrop={(e)=>onDrop("extra", e, i)}>
+                        <CardThumb id={card} idx={i} area="extra" />
+                    </div>
+                {/each}
+            </div>
+        </div>
+        <div class="deck-group">
+            <h3>副卡组（{$deck.side.length}）</h3>
+            <div role="region" ondragover={(e)=>e.preventDefault()} ondrop={(e)=>onDrop("side", e, -1)} class="card-grid side-deck">
+                {#each $deck.side as card, i}
+                    <div class="card-grid-thumb" role="region" ondragover={(e)=>e.preventDefault()} ondrop={(e)=>onDrop("side", e, i)}>
+                        <CardThumb id={card} idx={i} area="side" />
+                    </div>
+                {/each}
+            </div>
+        </div>
+    </div>
+</div>
+
+<style>
+
+.middle-panel {
+    width: 55%;
+    padding: 20px;
+    background-color: #fff;
+    overflow-y: auto;
+}
+
+.control-bar {
+    margin-bottom: 20px;
+}
+
+.btn {
+    padding: 8px 20px;
+    margin-right: 10px;
+    background-color: #4CAF50;
+    color: white;
+    border: none;
+    border-radius: 4px;
+    cursor: pointer;
+}
+
+.deck-group {
+    margin-bottom: 30px;
+}
+
+.deck-group h3 {
+    margin-bottom: 10px;
+    color: #333;
+}
+
+.card-grid {
+    display: grid;
+    grid-template-columns: repeat(auto-fill, minmax(52px, 1fr));
+    grid-auto-flow: dense;
+    overflow-y: auto;
+    padding: 10px;
+    border: 1px solid #ddd;
+    border-radius: 4px;
+    min-height: 80px;
+}
+
+.card-grid-thumb {
+    aspect-ratio: 1/1.4;
+    border-radius: 5px;
+}
+
+
+</style>
diff --git a/src/components/right_panel.svelte b/src/components/right_panel.svelte
new file mode 100644
index 0000000..816687e
--- /dev/null
+++ b/src/components/right_panel.svelte
@@ -0,0 +1,97 @@
+<script lang="js">
+    import CardThumb from './card_thumb.svelte';
+    import { changeInput, showingCards, onPrevPage, onNextPage } from '../search'
+    import { deckOps } from '../deck';
+
+    function onChange(event) {
+        changeInput(event.target.value);
+    }
+
+    function onDrop(event) {
+        event.preventDefault();
+        const data = JSON.parse(event.dataTransfer.getData('text'));
+        if (data.area === 'main' || data.area === 'side' || data.area === 'extra') {
+            deckOps.deleteCard(data.area, data.idx);
+        }
+    }
+</script>
+
+<div class="right-panel" role="region" ondragover={(e)=>e.preventDefault()} ondrop={onDrop}>
+    <div class="search-bar">
+        <input type="text" placeholder="搜索卡牌..." oninput={onChange}>
+    </div>
+    <div class="card-list">
+           {#each $showingCards as card}
+               <div class="card-item">
+                   <div class="card-thumbnail">
+                       <CardThumb id={card.id} idx={-1} area="search" />
+                   </div>
+                   <span>{card.name}</span>
+               </div>
+           {/each}
+    </div>
+    <div class="pagination">
+        <button class="page-btn" onclick={onPrevPage}>上一页</button>
+        <button class="page-btn" onclick={onNextPage}>下一页</button>
+    </div>
+</div>
+
+
+<style>
+
+.right-panel {
+    width: 20%;
+    padding: 20px;
+    background-color: #f8f8f8;
+    display: flex;
+    flex-direction: column;
+}
+
+.search-bar input {
+    width: 100%;
+    padding: 8px;
+    margin-bottom: 15px;
+    border: 1px solid #ddd;
+    border-radius: 4px;
+}
+
+.card-list {
+    flex-grow: 1;
+    overflow-y: auto;
+    margin-bottom: 15px;
+}
+
+.card-item {
+    display: flex;
+    align-items: center;
+    padding: 8px;
+    margin-bottom: 5px;
+    background-color: white;
+    border-radius: 4px;
+    cursor: pointer;
+}
+
+.pagination {
+    display: flex;
+    gap: 10px;
+}
+
+.page-btn {
+    flex: 1;
+    padding: 8px;
+    background-color: #007bff;
+    color: white;
+    border: none;
+    border-radius: 4px;
+    cursor: pointer;
+}
+
+.card-thumbnail {
+    width: 50px;
+    height: 70px;
+    background-color: #eee;
+    margin-right: 10px;
+    border-radius: 3px;
+}
+
+</style>
diff --git a/src/control/deck.js b/src/control/deck.js
deleted file mode 100644
index 43c784f..0000000
--- a/src/control/deck.js
+++ /dev/null
@@ -1,134 +0,0 @@
-import { writable } from "svelte/store";
-import { parseYdke } from '../utils';
-import { getCardDb, getAltId } from '../data/cardDb';
-
-let deck = writable({main: [], extra: [], side: []});
-let deckState = {main: [], extra: [], side: []};
-
-function sanitizeDeck(deck) {
-    let cardDb = getCardDb();
-    let altId = getAltId();
-    let ret = [];
-    for (let id of deck) {
-        if (altId[id] !== undefined) {
-            id = altId[id];
-        }
-        if (cardDb[id] !== undefined) {
-            ret.push(id);
-        }
-    }
-    return ret;
-}
-
-function setDeck(d) {
-    d.main = sanitizeDeck(d.main);
-    d.side = sanitizeDeck(d.side);
-    d.extra = sanitizeDeck(d.extra);
-    deckState = d;
-    deck.set(d);
-    localStorage.setItem('cachedDeck', JSON.stringify(d));
-};
-
-function canAdd(d, id) {
-    let count = 0;
-    for (let c of d.main) {
-        if (c === id) count += 1;
-    }
-    for (let c of d.side) {
-        if (c === id) count += 1;
-    }
-    for (let c of d.extra) {
-        if (c === id) count += 1;
-    }
-    if (count + 1 > 3) return false;
-    return true;
-}
-
-let deckOps = {
-    "deleteCard": (from, idx) => {
-        if (from === 'main') {
-            deckState.main.splice(idx, 1);
-            setDeck(deckState);
-        } else if (from === 'side') {
-            deckState.side.splice(idx, 1);
-            setDeck(deckState);
-        } else if (from === 'extra') {
-            deckState.extra.splice(idx, 1);
-            setDeck(deckState);
-        }
-    },
-    "move": (from, to, fromIdx, toIdx) => {
-        let cardDb = getCardDb();
-        let id = deckState[from][fromIdx];
-        if (cardDb[id].isExtra && to === 'main') return;
-        if (!cardDb[id].isExtra && to === 'extra') return;
-        deckState[from].splice(fromIdx, 1);
-        if (toIdx === -1) deckState[to].push(id);
-        else deckState[to].splice(toIdx, 0, id);
-        setDeck(deckState);
-    },
-    "add2extra": (id, targetIdx) => {
-        let cardDb = getCardDb();
-        if (!cardDb[id].isExtra) return;
-        let d = deckState;
-        if (canAdd(d, id)) {
-            if (targetIdx === -1) 
-                d.extra.push(id);
-            else {
-                d.extra.splice(targetIdx, 0, id);
-            }
-            setDeck(d);
-        }
-    },
-    "add2main": (id, targetIdx) => {
-        let cardDb = getCardDb();
-        console.log(targetIdx);
-        if (cardDb[id].isExtra) return;
-        let d = deckState;
-        if (canAdd(d, id)) {
-            if (targetIdx === -1) 
-                d.main.push(id);
-            else {
-                d.main.splice(targetIdx, 0, id);
-            }
-            setDeck(d);
-        }
-    },
-    "add2side": (id, targetIdx) => {
-        let d = deckState;
-        if (canAdd(d, id)) {
-            if (targetIdx === -1) 
-                d.side.push(id);
-            else {
-                d.side.splice(targetIdx, 0, id);
-            }
-            setDeck(d);
-        }
-    }
-};
-
-function initDeck() {
-    let url = window.location.href.split('#');
-    if (url.length === 2) {
-        let deck = parseYdke(url[1]);
-        if (deck.main.length > 0 || deck.extra.length > 0 || deck.extra.length > 0) {
-            setDeck(deck);
-            window.location.href = url[0];
-            return;
-        }
-    }
-    let cachedDeck = localStorage.getItem('cachedDeck');
-    if (cachedDeck !== null) {
-        cachedDeck = JSON.parse(cachedDeck); 
-        setDeck(cachedDeck)
-        return;
-    }
-}
-
-export {
-    deck,
-    setDeck,
-    deckOps,
-    initDeck,
-};
-
diff --git a/src/control/left_panel.js b/src/control/left_panel.js
deleted file mode 100644
index 87d8a4a..0000000
--- a/src/control/left_panel.js
+++ /dev/null
@@ -1,45 +0,0 @@
-import { writable } from 'svelte/store';
-
-let leftPanelCardId = writable('');
-let leftPanelCardDesc = writable('');
-
-let curVersion = 0;
-let descCache = new Map();
-
-function setLeftPanelCard(id) {
-    leftPanelCardId.set(id);
-    curVersion += 1;
-    leftPanelCardDesc.set('加载中...');
-    let ver = curVersion;
-    setDesc(ver, id);
-}
-
-function setDesc(version, id) {
-    if (descCache.has(id)) {
-        leftPanelCardDesc.set(descCache.get(id));
-        return;
-    }
-    let descUrl = 'https://oss.nebula.moe/ygo-card-text/' + 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);
-        });
-}
-
-export {
-    leftPanelCardId,
-    leftPanelCardDesc,
-    setLeftPanelCard,
-};
diff --git a/src/control/loading.js b/src/control/loading.js
deleted file mode 100644
index 2d0b1ce..0000000
--- a/src/control/loading.js
+++ /dev/null
@@ -1,57 +0,0 @@
-import { writable } from 'svelte/store';
-import { initSearch } from './search';
-import { initDeck } from './deck';
-import { setCardDb, setAltId } from '../data/cardDb';
-
-let isLoading = writable(true);
-
-async function fetchCardDb() {
-    let localVer = localStorage.getItem('card_db_ver');
-    try {
-        // load card db
-        let response = await fetch("https://raye.mistivia.com/card_db_parts/version");
-        if (!response.ok) {
-            throw new Error('Network response was not ok');
-        }
-        let data = await response.json();
-        data = String(data)
-        if (localVer === data && localStorage.getItem('card_db') !== null) {
-            setCardDb(JSON.parse(localStorage.getItem('card_db')));
-        } else {
-            localVer = data;
-            response = await fetch("https://raye.mistivia.com/card_db_parts/index.json");
-            if (!response.ok) {
-                throw new Error('Network response was not ok');
-            }
-            data = await response.json();
-            let tasks = data.map((i)=>fetch('https://raye.mistivia.com/card_db_parts/' + i));
-            let datas = await Promise.all(tasks);
-            datas = await Promise.all(datas.map((x) => x.text()));
-            data = JSON.parse(datas.join(''));
-            setCardDb(data);
-            localStorage.setItem('card_db_ver', localVer);
-            localStorage.setItem('card_db', datas.join(''));
-        }
-
-        // load alt id
-        response = await fetch("https://ygocdb.com/api/v0/idChangelog.jsonp");
-        if (!response.ok) {
-            throw new Error('Network response was not ok');
-        }
-        data = await response.json();
-        setAltId(data);
-    } catch (error) {
-        console.log(error);
-        alert("加载失败！请刷新重试");
-        return;
-    }
-    isLoading.set(false);
-    initDeck();
-    initSearch();
-}
-
-fetchCardDb();
-
-export {
-    isLoading,
-};
diff --git a/src/control/search.js b/src/control/search.js
deleted file mode 100644
index 5927950..0000000
--- a/src/control/search.js
+++ /dev/null
@@ -1,79 +0,0 @@
-import { writable } from 'svelte/store';
-import { getCardDb} from '../data/cardDb';
-
-let showingCards = writable([]);
-let resultCards = [];
-
-let curVer = 0;
-let curPage = 0;
-
-function showCards() {
-    showingCards.set(resultCards.slice(curPage * 10, curPage * 10 + 10));
-}
-
-function changeInput(query) {
-    curVer += 1;
-    query = query.split(' ');
-    setTimeout(()=>{doSearch(curVer, query)}, 0);
-}
-
-function doSearch(ver, query) {
-    let cardDb = getCardDb();
-    let result = [];
-    for (let key in cardDb) {
-        if (ver !== curVer) {
-            return;
-        }
-        let hit = true;
-        for (let word of query) {
-            let matched = false;
-            for (let name of cardDb[key].names) {
-                if (name.toLowerCase().includes(word.toLowerCase())) {
-                    matched = true;
-                    break;
-                }
-            }
-            if (!matched) {
-                hit = false;
-                break;
-            }
-        }
-        if (hit) {
-            result.push({"id": key, "name": cardDb[key].names[0]})
-        }
-    }
-    if (ver !== curVer) return;
-    result.sort((a, b) => {
-        return cardDb[a.id].cid - cardDb[b.id].cid;
-    });
-    resultCards = result;
-    curPage = 0;
-    showCards();
-}
-
-function initSearch() {
-    doSearch(curVer, "");
-}
-
-function onPrevPage() {
-    if (curPage > 0) {
-        curPage -= 1;
-        showCards();
-    }
-}
-
-function onNextPage() {
-    if (curPage < Math.floor((resultCards.length - 1) / 10)) {
-        curPage += 1;
-        showCards();
-    }
-}
-
-
-export {
-    changeInput,
-    onPrevPage,
-    onNextPage,
-    showingCards,
-    initSearch,
-};
diff --git a/src/data/cardDb.js b/src/data/cardDb.js
deleted file mode 100644
index b9b991f..0000000
--- a/src/data/cardDb.js
+++ /dev/null
@@ -1,25 +0,0 @@
-let cardDb = {};
-let altId = {};
-
-function setCardDb(d) {
-    cardDb = d;
-}
-
-function getCardDb() {
-    return cardDb;
-}
-
-function getAltId() {
-    return altId;
-}
-
-function setAltId(x) {
-    altId = x;
-}
-
-export {
-    getCardDb,
-    setCardDb,
-    getAltId,
-    setAltId,
-};
diff --git a/src/deck.js b/src/deck.js
new file mode 100644
index 0000000..285320b
--- /dev/null
+++ b/src/deck.js
@@ -0,0 +1,134 @@
+import { writable } from "svelte/store";
+import { parseYdke } from './utils';
+import { getCardDb, getAltId } from './card_db';
+
+let deck = writable({main: [], extra: [], side: []});
+let deckState = {main: [], extra: [], side: []};
+
+function sanitizeDeck(deck) {
+    let cardDb = getCardDb();
+    let altId = getAltId();
+    let ret = [];
+    for (let id of deck) {
+        if (altId[id] !== undefined) {
+            id = altId[id];
+        }
+        if (cardDb[id] !== undefined) {
+            ret.push(id);
+        }
+    }
+    return ret;
+}
+
+function setDeck(d) {
+    d.main = sanitizeDeck(d.main);
+    d.side = sanitizeDeck(d.side);
+    d.extra = sanitizeDeck(d.extra);
+    deckState = d;
+    deck.set(d);
+    localStorage.setItem('cachedDeck', JSON.stringify(d));
+};
+
+function canAdd(d, id) {
+    let count = 0;
+    for (let c of d.main) {
+        if (c === id) count += 1;
+    }
+    for (let c of d.side) {
+        if (c === id) count += 1;
+    }
+    for (let c of d.extra) {
+        if (c === id) count += 1;
+    }
+    if (count + 1 > 3) return false;
+    return true;
+}
+
+let deckOps = {
+    "deleteCard": (from, idx) => {
+        if (from === 'main') {
+            deckState.main.splice(idx, 1);
+            setDeck(deckState);
+        } else if (from === 'side') {
+            deckState.side.splice(idx, 1);
+            setDeck(deckState);
+        } else if (from === 'extra') {
+            deckState.extra.splice(idx, 1);
+            setDeck(deckState);
+        }
+    },
+    "move": (from, to, fromIdx, toIdx) => {
+        let cardDb = getCardDb();
+        let id = deckState[from][fromIdx];
+        if (cardDb[id].isExtra && to === 'main') return;
+        if (!cardDb[id].isExtra && to === 'extra') return;
+        deckState[from].splice(fromIdx, 1);
+        if (toIdx === -1) deckState[to].push(id);
+        else deckState[to].splice(toIdx, 0, id);
+        setDeck(deckState);
+    },
+    "add2extra": (id, targetIdx) => {
+        let cardDb = getCardDb();
+        if (!cardDb[id].isExtra) return;
+        let d = deckState;
+        if (canAdd(d, id)) {
+            if (targetIdx === -1) 
+                d.extra.push(id);
+            else {
+                d.extra.splice(targetIdx, 0, id);
+            }
+            setDeck(d);
+        }
+    },
+    "add2main": (id, targetIdx) => {
+        let cardDb = getCardDb();
+        console.log(targetIdx);
+        if (cardDb[id].isExtra) return;
+        let d = deckState;
+        if (canAdd(d, id)) {
+            if (targetIdx === -1) 
+                d.main.push(id);
+            else {
+                d.main.splice(targetIdx, 0, id);
+            }
+            setDeck(d);
+        }
+    },
+    "add2side": (id, targetIdx) => {
+        let d = deckState;
+        if (canAdd(d, id)) {
+            if (targetIdx === -1) 
+                d.side.push(id);
+            else {
+                d.side.splice(targetIdx, 0, id);
+            }
+            setDeck(d);
+        }
+    }
+};
+
+function initDeck() {
+    let url = window.location.href.split('#');
+    if (url.length === 2) {
+        let deck = parseYdke(url[1]);
+        if (deck.main.length > 0 || deck.extra.length > 0 || deck.extra.length > 0) {
+            setDeck(deck);
+            window.location.href = url[0];
+            return;
+        }
+    }
+    let cachedDeck = localStorage.getItem('cachedDeck');
+    if (cachedDeck !== null) {
+        cachedDeck = JSON.parse(cachedDeck); 
+        setDeck(cachedDeck)
+        return;
+    }
+}
+
+export {
+    deck,
+    setDeck,
+    deckOps,
+    initDeck,
+};
+
diff --git a/src/left_panel.js b/src/left_panel.js
new file mode 100644
index 0000000..87d8a4a
--- /dev/null
+++ b/src/left_panel.js
@@ -0,0 +1,45 @@
+import { writable } from 'svelte/store';
+
+let leftPanelCardId = writable('');
+let leftPanelCardDesc = writable('');
+
+let curVersion = 0;
+let descCache = new Map();
+
+function setLeftPanelCard(id) {
+    leftPanelCardId.set(id);
+    curVersion += 1;
+    leftPanelCardDesc.set('加载中...');
+    let ver = curVersion;
+    setDesc(ver, id);
+}
+
+function setDesc(version, id) {
+    if (descCache.has(id)) {
+        leftPanelCardDesc.set(descCache.get(id));
+        return;
+    }
+    let descUrl = 'https://oss.nebula.moe/ygo-card-text/' + 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);
+        });
+}
+
+export {
+    leftPanelCardId,
+    leftPanelCardDesc,
+    setLeftPanelCard,
+};
diff --git a/src/loading.js b/src/loading.js
new file mode 100644
index 0000000..2fed39f
--- /dev/null
+++ b/src/loading.js
@@ -0,0 +1,57 @@
+import { writable } from 'svelte/store';
+import { initSearch } from './search';
+import { initDeck } from './deck';
+import { setCardDb, setAltId } from './card_db';
+
+let isLoading = writable(true);
+
+async function fetchCardDb() {
+    let localVer = localStorage.getItem('card_db_ver');
+    try {
+        // load card db
+        let response = await fetch("https://raye.mistivia.com/card_db_parts/version");
+        if (!response.ok) {
+            throw new Error('Network response was not ok');
+        }
+        let data = await response.json();
+        data = String(data)
+        if (localVer === data && localStorage.getItem('card_db') !== null) {
+            setCardDb(JSON.parse(localStorage.getItem('card_db')));
+        } else {
+            localVer = data;
+            response = await fetch("https://raye.mistivia.com/card_db_parts/index.json");
+            if (!response.ok) {
+                throw new Error('Network response was not ok');
+            }
+            data = await response.json();
+            let tasks = data.map((i)=>fetch('https://raye.mistivia.com/card_db_parts/' + i));
+            let datas = await Promise.all(tasks);
+            datas = await Promise.all(datas.map((x) => x.text()));
+            data = JSON.parse(datas.join(''));
+            setCardDb(data);
+            localStorage.setItem('card_db_ver', localVer);
+            localStorage.setItem('card_db', datas.join(''));
+        }
+
+        // load alt id
+        response = await fetch("https://ygocdb.com/api/v0/idChangelog.jsonp");
+        if (!response.ok) {
+            throw new Error('Network response was not ok');
+        }
+        data = await response.json();
+        setAltId(data);
+    } catch (error) {
+        console.log(error);
+        alert("加载失败！请刷新重试");
+        return;
+    }
+    isLoading.set(false);
+    initDeck();
+    initSearch();
+}
+
+fetchCardDb();
+
+export {
+    isLoading,
+};
diff --git a/src/main.ts b/src/main.ts
index 928b6c5..7b9d006 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,5 +1,5 @@
 import { mount } from 'svelte'
-import App from './App.svelte'
+import App from './app.svelte'
 
 const app = mount(App, {
   target: document.getElementById('app')!,
diff --git a/src/search.js b/src/search.js
new file mode 100644
index 0000000..f1f7dbd
--- /dev/null
+++ b/src/search.js
@@ -0,0 +1,79 @@
+import { writable } from 'svelte/store';
+import { getCardDb } from './card_db';
+
+let showingCards = writable([]);
+let resultCards = [];
+
+let curVer = 0;
+let curPage = 0;
+
+function showCards() {
+    showingCards.set(resultCards.slice(curPage * 10, curPage * 10 + 10));
+}
+
+function changeInput(query) {
+    curVer += 1;
+    query = query.split(' ');
+    setTimeout(()=>{doSearch(curVer, query)}, 0);
+}
+
+function doSearch(ver, query) {
+    let cardDb = getCardDb();
+    let result = [];
+    for (let key in cardDb) {
+        if (ver !== curVer) {
+            return;
+        }
+        let hit = true;
+        for (let word of query) {
+            let matched = false;
+            for (let name of cardDb[key].names) {
+                if (name.toLowerCase().includes(word.toLowerCase())) {
+                    matched = true;
+                    break;
+                }
+            }
+            if (!matched) {
+                hit = false;
+                break;
+            }
+        }
+        if (hit) {
+            result.push({"id": key, "name": cardDb[key].names[0]})
+        }
+    }
+    if (ver !== curVer) return;
+    result.sort((a, b) => {
+        return cardDb[a.id].cid - cardDb[b.id].cid;
+    });
+    resultCards = result;
+    curPage = 0;
+    showCards();
+}
+
+function initSearch() {
+    doSearch(curVer, "");
+}
+
+function onPrevPage() {
+    if (curPage > 0) {
+        curPage -= 1;
+        showCards();
+    }
+}
+
+function onNextPage() {
+    if (curPage < Math.floor((resultCards.length - 1) / 10)) {
+        curPage += 1;
+        showCards();
+    }
+}
+
+
+export {
+    changeInput,
+    onPrevPage,
+    onNextPage,
+    showingCards,
+    initSearch,
+};
-- 
cgit v1.0

