Bladeren bron

开发:支持新建关卡和实例

xlxin 1 jaar geleden
bovenliggende
commit
330d474056
31 gewijzigde bestanden met toevoegingen van 1970 en 29 verwijderingen
  1. 386 20
      TileManor/scripts/electron/index.html
  2. 11 0
      TileManor/scripts/electron/instance.html
  3. 12 4
      TileManor/scripts/electron/level.html
  4. 56 2
      TileManor/scripts/electron/main.js
  5. 14 0
      TileManor/scripts/electron/node_modules/ansi-regex/index.js
  6. 9 0
      TileManor/scripts/electron/node_modules/ansi-regex/license
  7. 85 0
      TileManor/scripts/electron/node_modules/ansi-regex/package.json
  8. 87 0
      TileManor/scripts/electron/node_modules/ansi-regex/readme.md
  9. 20 0
      TileManor/scripts/electron/node_modules/electron-prompt/.editorconfig
  10. 14 0
      TileManor/scripts/electron/node_modules/electron-prompt/.github/workflows/ci.yml
  11. 21 0
      TileManor/scripts/electron/node_modules/electron-prompt/LICENSE
  12. 78 0
      TileManor/scripts/electron/node_modules/electron-prompt/README.md
  13. 147 0
      TileManor/scripts/electron/node_modules/electron-prompt/lib/index.js
  14. 72 0
      TileManor/scripts/electron/node_modules/electron-prompt/lib/page/prompt.css
  15. 18 0
      TileManor/scripts/electron/node_modules/electron-prompt/lib/page/prompt.html
  16. 160 0
      TileManor/scripts/electron/node_modules/electron-prompt/lib/page/prompt.js
  17. 70 0
      TileManor/scripts/electron/node_modules/electron-prompt/package.json
  18. 21 0
      TileManor/scripts/electron/node_modules/prompt-sync/LICENSE
  19. 118 0
      TileManor/scripts/electron/node_modules/prompt-sync/README.md
  20. 243 0
      TileManor/scripts/electron/node_modules/prompt-sync/index.js
  21. 70 0
      TileManor/scripts/electron/node_modules/prompt-sync/package.json
  22. 38 0
      TileManor/scripts/electron/node_modules/prompt-sync/test.js
  23. 15 0
      TileManor/scripts/electron/node_modules/strip-ansi/index.d.ts
  24. 7 0
      TileManor/scripts/electron/node_modules/strip-ansi/index.js
  25. 9 0
      TileManor/scripts/electron/node_modules/strip-ansi/license
  26. 86 0
      TileManor/scripts/electron/node_modules/strip-ansi/package.json
  27. 61 0
      TileManor/scripts/electron/node_modules/strip-ansi/readme.md
  28. 26 0
      TileManor/scripts/electron/package-lock.json
  29. 3 1
      TileManor/scripts/electron/package.json
  30. 2 2
      TileManor/scripts/electron/preload.js
  31. 11 0
      TileManor/scripts/electron/template.html

+ 386 - 20
TileManor/scripts/electron/index.html

@@ -30,30 +30,345 @@
 
     <script>
         var levels = null;
+        var instances = null;
+        var templateFNs = null;
 
-        function loadAndFillLvInfo() {
+        function getInstanceByName(name) {
+            for (let i = 0; i < instances.length; i++) {
+                if (instances[i].lvinst == name) {
+                    return instances[i];
+                }
+            }
+            return null;
+        }
+
+        function getTemplateByName(name) {
+            for (let i = 0; i < templateFNs.length; i++) {
+                if (templateFNs[i] == name) {
+                    return templateFNs[i];
+                }
+            }
+            return null;
+        }
+
+        function getAllLvIdsByInstName(name) {
+            // 返回所有用到了该实例的关卡的id
+            const ids = [];
+            for (let i = 0; i < levels.length; i++) {
+                if (levels[i].lvinst == name) {
+                    ids.push(levels[i].lvid);
+                }
+            }
+            return ids;
+        }
+
+        function showInstanceInfoAt(div, inst) {
+            // 将其显示在页面上,其中template应该是一个下拉框
+            // try1-10应该是一个表格, 表格的头部应该是:尝试次数,瓦片数,种子,参数模板,表格里面的每个值是可以编辑的
+            div.innerHTML = `
+                <h2>实例名: ${inst.lvinst}</h2>
+            `;
+            // 显示所有用到了该实例的关卡
+            const lvIds = getAllLvIdsByInstName(inst.lvinst);
+            div.innerHTML += `
+                <p>用到了该实例的关卡: ${lvIds}</p>
+            `;
+            // 加入 template 下拉框
+            div.innerHTML += `
+                <p>模板名称: </p>
+            `;
+            const templateSelect = document.createElement('select');
+            templateSelect.id = 'template-select';
+            templateFNs.forEach(fn => {
+                const option = document.createElement('option');
+                option.value = fn;
+                option.textContent = fn;
+                if (fn == inst.template) {
+                    option.selected = true;
+                }
+                templateSelect.appendChild(option);
+            });
+            div.appendChild(templateSelect);
+            // 加入表格
+            const table = document.createElement('table');
+            const thead = document.createElement('thead');
+            const tbody = document.createElement('tbody');
+            const tr = document.createElement('tr');
+            const th1 = document.createElement('th');
+            const th2 = document.createElement('th');
+            const th3 = document.createElement('th');
+            const th4 = document.createElement('th');
+            th1.textContent = '尝试次数';
+            th2.textContent = '瓦片数';
+            th3.textContent = '种子';
+            th4.textContent = '参数模板';
+            tr.appendChild(th1);
+            tr.appendChild(th2);
+            tr.appendChild(th3);
+            tr.appendChild(th4);
+            thead.appendChild(tr);
+            table.appendChild(thead);
+            table.appendChild(tbody);
+            div.appendChild(table);
+            // 加入表格内容
+            for (let i = 1; i <= 10; i++) {
+                var trynParams = inst['try' + i + '(tileCnt|seed|param_template)'].split('|');
+                const tr = document.createElement('tr');
+                const td1 = document.createElement('td');
+                const td2 = document.createElement('td');
+                const td3 = document.createElement('td');
+                const td4 = document.createElement('td');
+                const input1 = document.createElement('input');
+                const input2 = document.createElement('input');
+                const input3 = document.createElement('input');
+                const input4 = document.createElement('input');
+                input1.type = 'text';
+                input1.value = trynParams[0];
+                input2.type = 'text';
+                input2.value = trynParams[1];
+                input3.type = 'text';
+                input3.value = trynParams[2];
+                td1.textContent = i;
+                td2.appendChild(input1);
+                td3.appendChild(input2);
+                td4.appendChild(input3);
+                tr.appendChild(td1);
+                tr.appendChild(td2);
+                tr.appendChild(td3);
+                tr.appendChild(td4);
+                tbody.appendChild(tr);
+            }
+
+            // 增加一个按钮,点击后可以保存
+            const saveButton = document.createElement('button');
+            saveButton.id = 'save-instance-button';
+            saveButton.textContent = '保存';
+            div.appendChild(saveButton);
+            saveButton.addEventListener('click', () => {
+                // 保存实例信息
+            });
+        }
+
+        function showTemplateInfoAt(div, templateInfo) {
+            
+        }
+
+        function initData() {
             // 读取关卡信息并填充到页面
             if (levels == null) {
                 window.electron.send('parse-csv', '../../conf/levelInfo.csv')
                 window.electron.receive('csv-data', (data) => {
-                    const levelList = document.getElementById('level-list');
-                    const levelDetails = document.getElementById('level-details');
-                    // 将levels里面的信息填充到页面
-                    data.forEach(level => {
-                        const listItem = document.createElement('div');
-                        listItem.textContent = level.lvid;
-                        listItem.addEventListener('click', () => {
-                            levelDetails.innerHTML = `
-                                <h2>关卡ID: ${level.lvid}</h2>
-                                <p>关卡说明: ${level.lvinst}</p>
-                                <p>类型: ${level.type}</p>
-                                <p>DDA类型: ${level.dda_type}</p>
-                                <p>DDA参数: ${level.dda_para}</p>
-                            `;
+                    if (levels == null) {
+                        levels = data;
+                        window.electron.send('parse-csv', '../../conf/levelInstInfo.csv')
+                    } else {
+                        if (instances == null) {
+                            instances = data
+                        }
+                    }
+                });
+            }
+            if (templateFNs == null) {
+                window.electron.send('get-file-list', '../../tf_templates')
+                window.electron.receive('file-list-error', (data) => {
+                    console.error('Error reading directory: ', err);
+                });
+                window.electron.receive('file-list-data', (data) => {
+                    // 过滤掉非 .json 后缀的文件
+                    templateFNs = [];
+                    for (let i = 0; i < data.length; i++) {
+                        if (data[i].endsWith('.json')) {
+                            templateFNs.push(data[i]);
+                        }
+                    }
+                });
+            }
+            // 
+        }
+
+        // 创建一个新实例,并将其关联到指定的关卡中
+        function addNewInst(level, instDetails) {
+            // 获取实例名称
+            window.electron.send('open-newinst-prompt', 'some data');
+            window.electron.receive('prompt-newinst-reply', (event, arg) => {
+                const instName = arg;
+                // 检查是否已经存在
+                for (let i = 0; i < instances.length; i++) {
+                    if (instances[i].lvinst == instName) {
+                        alert('实例已经存在');
+                        return;
+                    }
+                }
+                // 不存在则加入
+                const inst = {
+                    lvinst: instName,
+                    template: '',
+                    'try1(tileCnt|seed|param_template)': '0|0|0',
+                    'try2(tileCnt|seed|param_template)': '0|0|0',
+                    'try3(tileCnt|seed|param_template)': '0|0|0',
+                    'try4(tileCnt|seed|param_template)': '0|0|0',
+                    'try5(tileCnt|seed|param_template)': '0|0|0',
+                    'try6(tileCnt|seed|param_template)': '0|0|0',
+                    'try7(tileCnt|seed|param_template)': '0|0|0',
+                    'try8(tileCnt|seed|param_template)': '0|0|0',
+                    'try9(tileCnt|seed|param_template)': '0|0|0',
+                    'try10(tileCnt|seed|param_template)': '0|0|0'
+                };
+                instances.push(inst);
+                showInstanceInfoAt(instDetails, inst);
+            });
+        }
+
+        function saveLvInfo(lvDetails) {
+            // 保存关卡的详细信息
+            
+        }
+
+        function fillLvInfo() {
+            // 读取关卡信息并填充到页面
+            if (levels != null) {
+                const levelList = document.getElementById('level-list');
+                const levelDetails = document.getElementById('level-details');
+                const instDetails = document.getElementById('instance-details');
+                const templateDetails = document.getElementById('tempalte-details');
+                // 将levels里面的信息填充到页面
+                levels.forEach(level => {
+                    const listItem = document.createElement('li');
+                    listItem.textContent = level.lvid;
+                    listItem.addEventListener('click', () => {
+                        // 将选中的关卡id条目的背景颜色设置为蓝色
+                        Array.from(levelList.children).forEach(node => {
+                            if (node.textContent == level.lvid) {
+                                node.style.backgroundColor = 'lightblue';
+                            } else {
+                                node.style.backgroundColor = 'white';
+                            }
                         });
-                        levelList.appendChild(listItem);
+                        levelDetails.innerHTML = `
+                            <h2>关卡ID: ${level.lvid}</h2>
+                            <p>关卡实例名称: ${level.lvinst}</p>`
+                        // 
+                        if (level.lvinst == '') {
+                            // 实例不存在,
+                            // 显示一个下来列表,选择实例
+                            const instSelect = document.createElement('select');
+                            instSelect.id = 'inst-select';
+                            instances.forEach(inst => {
+                                const option = document.createElement('option');
+                                option.value = inst.lvinst;
+                                option.textContent = inst.lvinst;
+                                instSelect.appendChild(option);
+                            });
+                            levelDetails.appendChild(instSelect);
+                        }
+                        // 加入一个按钮,点击后可以创建实例
+                        const createInstButton = document.createElement('button');
+                        createInstButton.id = 'create-instance-button';
+                        createInstButton.textContent = '创建新的实例';
+                        levelDetails.appendChild(createInstButton);
+                        // createInstButton.addEventListener('click', () => {
+                        //     addNewInst(level);
+                        // });
+                        console.log(createInstButton.onclick);
+
+                        // 增加一个下拉框,选择类型:normal,hard,super-hard
+                        const typeSelect = document.createElement('select');
+                        typeSelect.id = 'type-select';
+                        const normalOption = document.createElement('option');
+                        normalOption.value = 'normal';
+                        normalOption.textContent = 'normal';
+                        const hardOption = document.createElement('option');
+                        hardOption.value = 'hard';
+                        hardOption.textContent = 'hard';
+                        const superHardOption = document.createElement('option');
+                        superHardOption.value = 'super-hard';
+                        superHardOption.textContent = 'super-hard';
+                        typeSelect.appendChild(normalOption);
+                        typeSelect.appendChild(hardOption);
+                        typeSelect.appendChild(superHardOption);
+                        levelDetails.innerHTML += `
+                            <p>关卡难度级别:</p>`
+                        levelDetails.appendChild(typeSelect);
+                        // 如果 type(normal=0,hard=1,superHard=2)
+                        type = level[2];
+                        if (type == 0) {
+                            normalOption.selected = true;
+                        } else if (type == 1) {
+                            hardOption.selected = true;
+                        } else if (type == 2) {
+                            superHardOption.selected = true;
+                        }
+                        // 显示dda类型和参数
+                        levelDetails.innerHTML +=
+                            `
+                            <p>DDA类型: ${level.dda_type}</p>
+                            <p>DDA参数: ${level.dda_para}</p>
+                        `;
+
+                        // 增加一个按钮,点击后可以保存
+                        const saveButton = document.createElement('button');
+                        saveButton.id = 'save-lv-button';
+                        saveButton.textContent = '保存';
+                        levelDetails.appendChild(saveButton);
+
+                        levelDetails.addEventListener('click', (event) => {
+                            if (event.target.id === 'create-instance-button') {
+                                addNewInst(level, instDetails);
+                            } else if (event.target.id === 'save-lv-button') {
+                                saveLvInfo(levelDetails);
+                            }
+                        });
+
+                        // 显示实例信息
+                        const inst = getInstanceByName(level.lvinst);
+                        if (inst != null) {
+                            showInstanceInfoAt(instDetails, inst);
+                            // 显示模板信息
+                            const template = getTemplateByName(inst.template);
+                            if (template != null) {
+                                showTemplateInfoAt(templateDetails, getTemplateByName(inst.template));
+                            }
+                        } else {
+                            instDetails.innerHTML = `
+                                <h2>实例名: Error:未定义</h2>
+                            `
+                        }
                     });
-                })
+                    levelList.appendChild(listItem);
+                });
+            }
+        }
+
+        function fillInstanceInfo() {
+            // 读取关卡信息并填充到页面
+            if (instances != null) {
+                const lst = document.getElementById('instance-list');
+                const details = document.getElementById('instance-details');
+                // 将levels里面的信息填充到页面
+                instances.forEach(inst => {
+                    const listItem = document.createElement('li');
+                    listItem.textContent = inst.lvinst;
+                    listItem.addEventListener('click', () => {
+                        showInstanceInfoAt(details, inst)
+                    });
+                    lst.appendChild(listItem);
+                });
+            }
+        }
+
+        function fillTemplateInfo() {
+            if (templateFNs != null) {
+                const lst = document.getElementById('template-list');
+                const details = document.getElementById('template-details');
+                // 将levels里面的信息填充到页面
+                templateFNs.forEach(fn => {
+                    const listItem = document.createElement('li');
+                    listItem.textContent = fn;
+                    listItem.addEventListener('click', () => {
+                        // 放入截图
+                    });
+                    lst.appendChild(listItem);
+                });
             }
         }
 
@@ -62,18 +377,69 @@
                 .then(response => response.text())
                 .then(data => {
                     document.getElementById('content').innerHTML = data;
-                    loadAndFillLvInfo();
+                    fillLvInfo();
+                    // 绑定新增关卡按钮
+                    document.getElementById('add-level-button').addEventListener('click', function() {
+                        window.electron.send('open-newlv-prompt', 'some data')
+                        window.electron.receive('prompt-newlv-reply', (event, arg) => {
+                            const lvid = arg;
+                            // 检查是否已经存在
+                            for (let i = 0; i < levels.length; i++) {
+                                if (levels[i].lvid == lvid) {
+                                    alert('关卡id已经存在');
+                                    return;
+                                }
+                            }
+                            // 不存在则加入
+                            const level = {
+                                lvid: lvid,
+                                lvinst: '',
+                                type: 0,
+                                dda_type: '0',
+                                dda_para: '0|10000'
+                            };
+                            levels.push(level);
+                            // 刷新页面
+                            fillLvInfo();
+                            // 选中该关卡
+                            const levelList = document.getElementById('level-list');
+                            levelList.childNodes.forEach(node => {
+                                if (node.textContent == lvid) {
+                                    node.click();
+                                }
+                            });
+                        })
+                    });
                 })
                 .catch(error => {
                     console.error('Error loading the levels content:', error);
                 });
         });
         document.getElementById('examples').addEventListener('click', function() {
-            // 更改content区域为实例布局
+            fetch('instance.html')
+                .then(response => response.text())
+                .then(data => {
+                    document.getElementById('content').innerHTML = data;
+                    fillInstanceInfo();
+                })
+                .catch(error => {
+                    console.error('Error loading the intance content:', error);
+                });
         });
         document.getElementById('templates').addEventListener('click', function() {
-            // 更改content区域为模板布局
+            fetch('template.html')
+                .then(response => response.text())
+                .then(data => {
+                    document.getElementById('content').innerHTML = data;
+                    fillTemplateInfo();
+                })
+                .catch(error => {
+                    console.error('Error loading the intance content:', error);
+                });
         });
+
+        // 初始化数据
+        initData();
     </script>
 </body>
 </html>

+ 11 - 0
TileManor/scripts/electron/instance.html

@@ -0,0 +1,11 @@
+<!-- level.html -->
+<div style="display: flex; flex-direction: row; height: 100vh; width: 100%;">
+    <div style="overflow-y: scroll; height: 100%; width: 30%;">
+        <ul id="instance-list" style="height: 100%;">
+        </ul>
+    </div>
+    <div id="instance-details" style="overflow-y: scroll; height: 100%; width: 60%;">
+        
+    </div>
+
+</div>

+ 12 - 4
TileManor/scripts/electron/level.html

@@ -1,5 +1,13 @@
 <!-- level.html -->
-<div>
-    <div id="level-list"></div>
-    <div id="level-details"></div>
-</div>
+<button id="add-level-button">新增关卡</button>
+<div style="display: flex; flex-direction: row; height: 100vh; width: 100%;">
+    <div style="overflow-y: scroll; height: 100%; width: 20%;">
+        <ul id="level-list" style="height: 100%;">
+        </ul>
+    </div>
+    <div style="overflow-y: scroll; height: 100%; width: 70%;">
+        <div id="level-details" ></div>
+        <div id="instance-details" ></div>
+        <div id="template-details" ></div>
+    </div>
+</div>

+ 56 - 2
TileManor/scripts/electron/main.js

@@ -1,6 +1,7 @@
 const path = require('path');
 const fs = require('fs')
 const csv = require('csv-parser')
+const prompt = require('electron-prompt');
 const { app, BrowserWindow } = require('electron');
 const { ipcMain } = require('electron')
 
@@ -15,10 +16,9 @@ function createWindow() {
             preload: path.join(__dirname, 'preload.js')
         }
     })
+    win.webContents.openDevTools() // 打开开发者工具
     // 加载index.html文件
     win.loadFile('index.html');
-
-    win.webContents.openDevTools() // 打开开发者工具
 }
 
 app.on('ready', createWindow);
@@ -34,3 +34,57 @@ ipcMain.on('parse-csv', (event, path) => {
         event.reply('csv-data', results)
       })
 })
+
+ipcMain.on('get-file-list', (event, dirPath) => {
+    console.error('Will read directory: ', dirPath);
+    fs.readdir(dirPath, (err, files) => {
+        if (err) {
+            console.error('Error reading directory: ', err);
+            event.reply('file-list-error', err);
+        } else {
+            event.reply('file-list-data', files);
+        }
+    });
+});
+
+ipcMain.on('open-newlv-prompt', (event, arg) => {
+    prompt({
+        title: 'Prompt',
+        label: '请输入关卡id:',
+        value: '',
+        inputAttrs: {
+            type: 'text'
+        },
+        type: 'input'
+    })
+    .then((r) => {
+        if(r === null) {
+            console.log('user cancelled');
+        } else {
+            console.log('result', r);
+            event.reply('prompt-newlv-reply', r);
+        }
+    })
+    .catch(console.error);
+})
+
+ipcMain.on('open-newinst-prompt', (event, arg) => {
+    prompt({
+        title: 'Prompt',
+        label: '请输入实例id:',
+        value: '',
+        inputAttrs: {
+            type: 'text'
+        },
+        type: 'input'
+    })
+    .then((r) => {
+        if(r === null) {
+            console.log('user cancelled');
+        } else {
+            console.log('result', r);
+            event.reply('prompt-newinst-reply', r);
+        }
+    })
+    .catch(console.error);
+})

+ 14 - 0
TileManor/scripts/electron/node_modules/ansi-regex/index.js

@@ -0,0 +1,14 @@
+'use strict';
+
+module.exports = options => {
+	options = Object.assign({
+		onlyFirst: false
+	}, options);
+
+	const pattern = [
+		'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
+		'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))'
+	].join('|');
+
+	return new RegExp(pattern, options.onlyFirst ? undefined : 'g');
+};

+ 9 - 0
TileManor/scripts/electron/node_modules/ansi-regex/license

@@ -0,0 +1,9 @@
+MIT License
+
+Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 85 - 0
TileManor/scripts/electron/node_modules/ansi-regex/package.json

@@ -0,0 +1,85 @@
+{
+  "_from": "ansi-regex@^4.1.0",
+  "_id": "ansi-regex@4.1.1",
+  "_inBundle": false,
+  "_integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
+  "_location": "/ansi-regex",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "range",
+    "registry": true,
+    "raw": "ansi-regex@^4.1.0",
+    "name": "ansi-regex",
+    "escapedName": "ansi-regex",
+    "rawSpec": "^4.1.0",
+    "saveSpec": null,
+    "fetchSpec": "^4.1.0"
+  },
+  "_requiredBy": [
+    "/strip-ansi"
+  ],
+  "_resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+  "_shasum": "164daac87ab2d6f6db3a29875e2d1766582dabed",
+  "_spec": "ansi-regex@^4.1.0",
+  "_where": "/Users/xulianxin/Documents/develop/game/TileMatch/TileManor.Lv/TileManor/scripts/electron/node_modules/strip-ansi",
+  "author": {
+    "name": "Sindre Sorhus",
+    "email": "sindresorhus@gmail.com",
+    "url": "sindresorhus.com"
+  },
+  "bugs": {
+    "url": "https://github.com/chalk/ansi-regex/issues"
+  },
+  "bundleDependencies": false,
+  "deprecated": false,
+  "description": "Regular expression for matching ANSI escape codes",
+  "devDependencies": {
+    "ava": "^0.25.0",
+    "xo": "^0.23.0"
+  },
+  "engines": {
+    "node": ">=6"
+  },
+  "files": [
+    "index.js"
+  ],
+  "homepage": "https://github.com/chalk/ansi-regex#readme",
+  "keywords": [
+    "ansi",
+    "styles",
+    "color",
+    "colour",
+    "colors",
+    "terminal",
+    "console",
+    "cli",
+    "string",
+    "tty",
+    "escape",
+    "formatting",
+    "rgb",
+    "256",
+    "shell",
+    "xterm",
+    "command-line",
+    "text",
+    "regex",
+    "regexp",
+    "re",
+    "match",
+    "test",
+    "find",
+    "pattern"
+  ],
+  "license": "MIT",
+  "name": "ansi-regex",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/chalk/ansi-regex.git"
+  },
+  "scripts": {
+    "test": "xo && ava",
+    "view-supported": "node fixtures/view-codes.js"
+  },
+  "version": "4.1.1"
+}

+ 87 - 0
TileManor/scripts/electron/node_modules/ansi-regex/readme.md

@@ -0,0 +1,87 @@
+# ansi-regex [![Build Status](https://travis-ci.org/chalk/ansi-regex.svg?branch=master)](https://travis-ci.org/chalk/ansi-regex)
+
+> Regular expression for matching [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code)
+
+---
+
+<div align="center">
+	<b>
+		<a href="https://tidelift.com/subscription/pkg/npm-ansi-regex?utm_source=npm-ansi-regex&utm_medium=referral&utm_campaign=readme">Get professional support for this package with a Tidelift subscription</a>
+	</b>
+	<br>
+	<sub>
+		Tidelift helps make open source sustainable for maintainers while giving companies<br>assurances about security, maintenance, and licensing for their dependencies.
+	</sub>
+</div>
+
+---
+
+
+## Install
+
+```
+$ npm install ansi-regex
+```
+
+
+## Usage
+
+```js
+const ansiRegex = require('ansi-regex');
+
+ansiRegex().test('\u001B[4mcake\u001B[0m');
+//=> true
+
+ansiRegex().test('cake');
+//=> false
+
+'\u001B[4mcake\u001B[0m'.match(ansiRegex());
+//=> ['\u001B[4m', '\u001B[0m']
+
+'\u001B[4mcake\u001B[0m'.match(ansiRegex({onlyFirst: true}));
+//=> ['\u001B[4m']
+
+'\u001B]8;;https://github.com\u0007click\u001B]8;;\u0007'.match(ansiRegex());
+//=> ['\u001B]8;;https://github.com\u0007', '\u001B]8;;\u0007']
+```
+
+
+## API
+
+### ansiRegex([options])
+
+Returns a regex for matching ANSI escape codes.
+
+#### options
+
+##### onlyFirst
+
+Type: `boolean`<br>
+Default: `false` *(Matches any ANSI escape codes in a string)*
+
+Match only the first ANSI escape.
+
+
+## FAQ
+
+### Why do you test for codes not in the ECMA 48 standard?
+
+Some of the codes we run as a test are codes that we acquired finding various lists of non-standard or manufacturer specific codes. We test for both standard and non-standard codes, as most of them follow the same or similar format and can be safely matched in strings without the risk of removing actual string content. There are a few non-standard control codes that do not follow the traditional format (i.e. they end in numbers) thus forcing us to exclude them from the test because we cannot reliably match them.
+
+On the historical side, those ECMA standards were established in the early 90's whereas the VT100, for example, was designed in the mid/late 70's. At that point in time, control codes were still pretty ungoverned and engineers used them for a multitude of things, namely to activate hardware ports that may have been proprietary. Somewhere else you see a similar 'anarchy' of codes is in the x86 architecture for processors; there are a ton of "interrupts" that can mean different things on certain brands of processors, most of which have been phased out.
+
+
+## Security
+
+To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure.
+
+
+## Maintainers
+
+- [Sindre Sorhus](https://github.com/sindresorhus)
+- [Josh Junon](https://github.com/qix-)
+
+
+## License
+
+MIT

+ 20 - 0
TileManor/scripts/electron/node_modules/electron-prompt/.editorconfig

@@ -0,0 +1,20 @@
+root = true
+
+[*]
+indent_style = tab
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.json]
+indent_style = space
+indent_size = 2
+
+[*.yml]
+indent_style = space
+indent_size = 2
+
+[*.md]
+indent_style = space
+indent_size = 4

+ 14 - 0
TileManor/scripts/electron/node_modules/electron-prompt/.github/workflows/ci.yml

@@ -0,0 +1,14 @@
+name: CI
+
+on: [push, pull_request, workflow_dispatch]
+
+jobs:
+  test:
+    runs-on: ubuntu-latest
+
+    steps:
+    - uses: actions/checkout@v2
+    - uses: actions/setup-node@v2
+      with: {node-version: 16.x}
+    - run: npm install
+    - run: npm test

+ 21 - 0
TileManor/scripts/electron/node_modules/electron-prompt/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 p-sam
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 78 - 0
TileManor/scripts/electron/node_modules/electron-prompt/README.md

@@ -0,0 +1,78 @@
+# electron-prompt
+
+Electron helper to prompt for a value via input or select
+
+[![Build Status](https://travis-ci.com/p-sam/electron-prompt.svg?branch=master)](https://travis-ci.com/p-sam/electron-prompt) [![XO code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/xojs/xo)
+
+<p align="center"><img width="482" alt="prompt-preview" src="https://user-images.githubusercontent.com/17620180/111753337-09c0c680-8897-11eb-8ce8-43de29c143bd.png"></p>
+
+## Usage
+
+```sh
+npm install electron-prompt --save
+```
+
+```js
+prompt([options, parentBrowserWindow]).then(...).catch(...)
+```
+
+## Example
+
+```js
+const prompt = require('electron-prompt');
+
+prompt({
+    title: 'Prompt example',
+    label: 'URL:',
+    value: 'http://example.org',
+    inputAttrs: {
+        type: 'url'
+    },
+    type: 'input'
+})
+.then((r) => {
+    if(r === null) {
+        console.log('user cancelled');
+    } else {
+        console.log('result', r);
+    }
+})
+.catch(console.error);
+```
+
+## Documentation
+
+Primary method:
+
+```js
+prompt([options, parentBrowserWindow]).then(...).catch(...)
+```
+
+### Options object (optional)
+
+| Key  | Explanation |
+| ------------- | ------------- |
+| title  | (optional, string) The title of the prompt window. Defaults to 'Prompt'. |
+| label  | (optional, string) The label which appears on the prompt for the input field. Defaults to 'Please input a value:'. |
+| buttonLabels | (optional, object) The text for the OK/cancel buttons. Properties are 'ok' and 'cancel'. Defaults to null. |
+| value  | (optional, string) The default value for the input field. Defaults to null.|
+| type   | (optional, string) The type of input field, either 'input' for a standard text input field or 'select' for a dropdown type input. Defaults to 'input'.|
+| inputAttrs  | (optional, object) The attributes of the input field, analagous to the HTML attributes: `{type: 'text', required: true}` -> `<input type="text" required>`. Used if the type is 'input' |
+| selectOptions  | (optional, object) The items for the select dropdown if using the 'select' type in the format 'value': 'display text', where the value is what will be given to the then block and the display text is what the user will see. |
+| useHtmlLabel | (optional, boolean) Whether the label should be interpreted as HTML or not. Defaults to false. |
+| width  | (optional, integer) The width of the prompt window. Defaults to 370. |
+| minWidth  | (optional, integer) The minimum allowed width for the prompt window. Same default value as width. |
+| height  | (optional, integer) The height of the prompt window. Defaults to 130. |
+| minHeight  | (optional, integer) The minimum allowed height for the prompt window. Same default value as height. |
+| resizable  | (optional, boolean) Whether the prompt window can be resized or not (also sets useContentSize). Defaults to false. |
+| alwaysOnTop | (optional, boolean) Whether the window should always stay on top of other windows. Defaults to false |
+| icon | (optional, string) The path to an icon image to use in the title bar. Defaults to null and uses electron's icon. |
+| customStylesheet  | (optional, string) The local path of a CSS file to stylize the prompt window. Defaults to null. |
+| menuBarVisible | (optional, boolean) Whether to show the menubar or not. Defaults to false. |
+| skipTaskbar | (optional, boolean) Whether to show the prompt window icon in taskbar. Defaults to true. |
+
+If not supplied, it uses the defaults listed in the table above.
+
+### parentBrowserWindow (optional)
+
+The window in which to display the prompt on. If not supplied, the parent window of the prompt will be null.

+ 147 - 0
TileManor/scripts/electron/node_modules/electron-prompt/lib/index.js

@@ -0,0 +1,147 @@
+const path = require('path');
+const electron = require('electron');
+
+const DEFAULT_WIDTH = 370;
+const DEFAULT_HEIGHT = 160;
+
+function getElectronMainExport(id) {
+	if (electron[id]) {
+		return electron[id];
+	}
+
+	let remote = electron.remote;
+	if (!remote) {
+		try {
+			remote = require('@electron/remote');
+		} catch (originalError) {
+			const error = new Error(
+				'Install and set-up package `@electron/remote` to use this module from a renderer processs.\n'
+				+ 'It is preferable to set up message exchanges for this using `ipcMain.handle()` and `ipcRenderer.invoke()`,\n'
+				+ 'avoiding remote IPC overhead costs, and one morepackage dependancy.\n\n'
+				+ 'Original error message:\n\n'
+				+ originalError.message,
+			);
+
+			error.originalError = originalError;
+			throw error;
+		}
+	}
+
+	if (remote && remote[id]) {
+		return remote[id];
+	}
+
+	throw new Error('Unknown electron export: ' + String(id));
+}
+
+const BrowserWindow = getElectronMainExport('BrowserWindow');
+const ipcMain = getElectronMainExport('ipcMain');
+
+function electronPrompt(options, parentWindow) {
+	return new Promise((resolve, reject) => {
+		const id = `${Date.now()}-${Math.random()}`;
+
+		const options_ = Object.assign(
+			{
+				width: DEFAULT_WIDTH,
+				height: DEFAULT_HEIGHT,
+				minWidth: DEFAULT_WIDTH,
+				minHeight: DEFAULT_HEIGHT,
+				resizable: false,
+				title: 'Prompt',
+				label: 'Please input a value:',
+				buttonLabels: null,
+				alwaysOnTop: false,
+				value: null,
+				type: 'input',
+				selectOptions: null,
+				icon: null,
+				useHtmlLabel: false,
+				customStylesheet: null,
+				menuBarVisible: false,
+				skipTaskbar: true,
+			},
+			options || {},
+		);
+
+		if (options_.type === 'select' && (options_.selectOptions === null || typeof options_.selectOptions !== 'object')) {
+			reject(new Error('"selectOptions" must be an object'));
+			return;
+		}
+
+		let promptWindow = new BrowserWindow({
+			width: options_.width,
+			height: options_.height,
+			minWidth: options_.minWidth,
+			minHeight: options_.minHeight,
+			resizable: options_.resizable,
+			minimizable: false,
+			fullscreenable: false,
+			maximizable: false,
+			parent: parentWindow,
+			skipTaskbar: options_.skipTaskbar,
+			alwaysOnTop: options_.alwaysOnTop,
+			useContentSize: options_.resizable,
+			modal: Boolean(parentWindow),
+			title: options_.title,
+			icon: options_.icon || undefined,
+			webPreferences: {
+				nodeIntegration: true,
+				contextIsolation: false,
+			},
+		});
+
+		promptWindow.setMenu(null);
+		promptWindow.setMenuBarVisibility(options_.menuBarVisible);
+
+		const getOptionsListener = event => {
+			event.returnValue = JSON.stringify(options_);
+		};
+
+		const cleanup = () => {
+			ipcMain.removeListener('prompt-get-options:' + id, getOptionsListener);
+			ipcMain.removeListener('prompt-post-data:' + id, postDataListener);
+			ipcMain.removeListener('prompt-error:' + id, errorListener);
+
+			if (promptWindow) {
+				promptWindow.close();
+				promptWindow = null;
+			}
+		};
+
+		const postDataListener = (event, value) => {
+			resolve(value);
+			event.returnValue = null;
+			cleanup();
+		};
+
+		const unresponsiveListener = () => {
+			reject(new Error('Window was unresponsive'));
+			cleanup();
+		};
+
+		const errorListener = (event, message) => {
+			reject(new Error(message));
+			event.returnValue = null;
+			cleanup();
+		};
+
+		ipcMain.on('prompt-get-options:' + id, getOptionsListener);
+		ipcMain.on('prompt-post-data:' + id, postDataListener);
+		ipcMain.on('prompt-error:' + id, errorListener);
+		promptWindow.on('unresponsive', unresponsiveListener);
+
+		promptWindow.on('closed', () => {
+			promptWindow = null;
+			cleanup();
+			resolve(null);
+		});
+
+		promptWindow.loadFile(
+			path.join(__dirname, 'page', 'prompt.html'),
+			{hash: id},
+		);
+	});
+}
+
+module.exports = electronPrompt;

+ 72 - 0
TileManor/scripts/electron/node_modules/electron-prompt/lib/page/prompt.css

@@ -0,0 +1,72 @@
+body {
+    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif;
+    line-height: 1.5em;
+    color: #333;
+    background-color: #fff;
+}
+
+#container {
+    align-items: center;
+    justify-content: center;
+    display: flex;
+    height: 100%;
+    overflow: auto;
+}
+
+#form {
+    width: 100%;
+    padding-top: .5em;
+}
+
+#label {
+    max-width: 100%;
+    max-height: 100%;
+    margin-bottom: .8em;
+    padding: 0 .5em;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+}
+
+#data {
+    border-radius: 2px;
+    background: #fff;
+    width: 90%;
+    padding: .4em .5em;
+    border: 1px solid black;
+    min-height: 2em;
+    margin: 0 0 1.2em;
+}
+
+select#data {
+    height: 2em;
+}
+
+#data-container {
+    text-align: center;
+}
+
+#buttons {
+    text-align: right;
+    padding: 0 .5em 0 0;
+}
+
+#buttons > button,
+#buttons > input[type=submit] {
+    border-radius: 2px;
+    border: 0;
+    margin: 0 0 0 .5em;
+    font-size: .8em;
+    line-height: 1em;
+    padding: .6em 1em
+}
+
+#ok {
+    background-color: #3879D9;
+    color: white;
+}
+
+#cancel {
+    background-color: #DDD;
+    color: black;
+}

+ 18 - 0
TileManor/scripts/electron/node_modules/electron-prompt/lib/page/prompt.html

@@ -0,0 +1,18 @@
+<html>
+    <head>
+        <link href="prompt.css" rel="stylesheet" />
+    </head>
+    <body>
+        <div id="container">
+            <form id="form">
+                <div id="label">...</div>
+                <div id="data-container"></div>
+                <div id="buttons">
+                    <button id="cancel">Cancel</button>
+                    <button type="submit" id="ok">OK</button>
+                </div>
+            </form>
+        </div>
+        <script src="prompt.js"></script>
+    </body>
+</html>

+ 160 - 0
TileManor/scripts/electron/node_modules/electron-prompt/lib/page/prompt.js

@@ -0,0 +1,160 @@
+const fs = require('fs');
+const {ipcRenderer} = require('electron');
+
+let promptId = null;
+let promptOptions = null;
+
+function promptError(error) {
+	if (error instanceof Error) {
+		error = error.message;
+	}
+
+	ipcRenderer.sendSync('prompt-error:' + promptId, error);
+}
+
+function promptCancel() {
+	ipcRenderer.sendSync('prompt-post-data:' + promptId, null);
+}
+
+function promptSubmit() {
+	const dataElement = document.querySelector('#data');
+	let data = null;
+
+	if (promptOptions.type === 'input') {
+		data = dataElement.value;
+	} else if (promptOptions.type === 'select') {
+		if (promptOptions.selectMultiple) {
+			data = dataElement.querySelectorAll('option[selected]').map(o => o.getAttribute('value'));
+		} else {
+			data = dataElement.value;
+		}
+	}
+
+	ipcRenderer.sendSync('prompt-post-data:' + promptId, data);
+}
+
+function promptCreateInput() {
+	const dataElement = document.createElement('input');
+	dataElement.setAttribute('type', 'text');
+
+	if (promptOptions.value) {
+		dataElement.value = promptOptions.value;
+	} else {
+		dataElement.value = '';
+	}
+
+	if (promptOptions.inputAttrs && typeof (promptOptions.inputAttrs) === 'object') {
+		for (const k in promptOptions.inputAttrs) {
+			if (!Object.prototype.hasOwnProperty.call(promptOptions.inputAttrs, k)) {
+				continue;
+			}
+
+			dataElement.setAttribute(k, promptOptions.inputAttrs[k]);
+		}
+	}
+
+	dataElement.addEventListener('keyup', event => {
+		if (event.key === 'Escape') {
+			promptCancel();
+		}
+	});
+
+	dataElement.addEventListener('keypress', event => {
+		if (event.key === 'Enter') {
+			event.preventDefault();
+			document.querySelector('#ok').click();
+		}
+	});
+
+	return dataElement;
+}
+
+function promptCreateSelect() {
+	const dataElement = document.createElement('select');
+	let optionElement;
+
+	for (const k in promptOptions.selectOptions) {
+		if (!Object.prototype.hasOwnProperty.call(promptOptions.selectOptions, k)) {
+			continue;
+		}
+
+		optionElement = document.createElement('option');
+		optionElement.setAttribute('value', k);
+		optionElement.textContent = promptOptions.selectOptions[k];
+		if (k === promptOptions.value) {
+			optionElement.setAttribute('selected', 'selected');
+		}
+
+		dataElement.append(optionElement);
+	}
+
+	return dataElement;
+}
+
+function promptRegister() {
+	promptId = document.location.hash.replace('#', '');
+
+	try {
+		promptOptions = JSON.parse(ipcRenderer.sendSync('prompt-get-options:' + promptId));
+	} catch (error) {
+		return promptError(error);
+	}
+
+	if (promptOptions.useHtmlLabel) {
+		document.querySelector('#label').innerHTML = promptOptions.label;
+	} else {
+		document.querySelector('#label').textContent = promptOptions.label;
+	}
+
+	if (promptOptions.buttonLabels && promptOptions.buttonLabels.ok) {
+		document.querySelector('#ok').textContent = promptOptions.buttonLabels.ok;
+	}
+
+	if (promptOptions.buttonLabels && promptOptions.buttonLabels.cancel) {
+		document.querySelector('#cancel').textContent = promptOptions.buttonLabels.cancel;
+	}
+
+	try {
+		if (promptOptions.customStylesheet) {
+			const customStyleContent = fs.readFileSync(promptOptions.customStylesheet);
+			if (customStyleContent) {
+				const customStyle = document.createElement('style');
+				customStyle.setAttribute('rel', 'stylesheet');
+				customStyle.append(document.createTextNode(customStyleContent));
+				document.head.append(customStyle);
+			}
+		}
+	} catch (error) {
+		return promptError(error);
+	}
+
+	document.querySelector('#form').addEventListener('submit', promptSubmit);
+	document.querySelector('#cancel').addEventListener('click', promptCancel);
+
+	const dataContainerElement = document.querySelector('#data-container');
+
+	let dataElement;
+	if (promptOptions.type === 'input') {
+		dataElement = promptCreateInput();
+	} else if (promptOptions.type === 'select') {
+		dataElement = promptCreateSelect();
+	} else {
+		return promptError(`Unhandled input type '${promptOptions.type}'`);
+	}
+
+	dataContainerElement.append(dataElement);
+	dataElement.setAttribute('id', 'data');
+
+	dataElement.focus();
+	if (promptOptions.type === 'input') {
+		dataElement.select();
+	}
+}
+
+window.addEventListener('error', error => {
+	if (promptId) {
+		promptError('An error has occured on the prompt window: \n' + error);
+	}
+});
+
+document.addEventListener('DOMContentLoaded', promptRegister);

+ 70 - 0
TileManor/scripts/electron/node_modules/electron-prompt/package.json

@@ -0,0 +1,70 @@
+{
+  "_from": "electron-prompt",
+  "_id": "electron-prompt@1.7.0",
+  "_inBundle": false,
+  "_integrity": "sha512-IfqJYEgcRO6NuyPROo8AtdkAiZ6N9I1lQEf4dJAkPuhV5YgOHdmLqZJf6OXumZJfzrjpzCM5jHeYOrhGdgbnEA==",
+  "_location": "/electron-prompt",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "tag",
+    "registry": true,
+    "raw": "electron-prompt",
+    "name": "electron-prompt",
+    "escapedName": "electron-prompt",
+    "rawSpec": "",
+    "saveSpec": null,
+    "fetchSpec": "latest"
+  },
+  "_requiredBy": [
+    "#USER",
+    "/"
+  ],
+  "_resolved": "https://registry.npmjs.org/electron-prompt/-/electron-prompt-1.7.0.tgz",
+  "_shasum": "6f9dedb2dac1dd6bad2e6592ab81cb87c37f2a35",
+  "_spec": "electron-prompt",
+  "_where": "/Users/xulianxin/Documents/develop/game/TileMatch/TileManor.Lv/TileManor/scripts/electron",
+  "author": {
+    "name": "p-sam",
+    "email": "p-sam@users.noreply.github.com",
+    "url": "https://github.com/p-sam"
+  },
+  "bugs": {
+    "url": "https://github.com/p-sam/electron-prompt/issues"
+  },
+  "bundleDependencies": false,
+  "deprecated": false,
+  "description": "Electron helper to prompt for a value via input or select",
+  "devDependencies": {
+    "xo": "^0.44.0"
+  },
+  "homepage": "https://github.com/p-sam/electron-prompt#readme",
+  "keywords": [
+    "electron",
+    "prompt",
+    "string"
+  ],
+  "license": "MIT",
+  "main": "lib/index.js",
+  "name": "electron-prompt",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/p-sam/electron-prompt.git"
+  },
+  "scripts": {
+    "lint": "xo",
+    "lint:fix": "xo --fix",
+    "test": "npm run lint"
+  },
+  "version": "1.7.0",
+  "xo": {
+    "esnext": true,
+    "env": [
+      "node",
+      "browser"
+    ],
+    "rules": {
+      "unicorn/prefer-ternary": 0,
+      "unicorn/prefer-module": 0
+    }
+  }
+}

+ 21 - 0
TileManor/scripts/electron/node_modules/prompt-sync/LICENSE

@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014-2019 Paolo Fragomeni & David Mark Clements
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 118 - 0
TileManor/scripts/electron/node_modules/prompt-sync/README.md

@@ -0,0 +1,118 @@
+# SYNOPSIS
+A sync prompt for node. very simple. no C++ bindings and no bash scripts.
+
+Works on Linux, OS X and Windows.
+
+# BASIC MODE
+```js
+
+var prompt = require('prompt-sync')();
+//
+// get input from the user.
+//
+var n = prompt('How many more times? ');
+```
+# WITH HISTORY
+
+History is an optional extra, to use simply install the history plugin. 
+
+```sh
+npm install --save prompt-sync-history
+```
+
+```js
+var prompt = require('prompt-sync')({
+  history: require('prompt-sync-history')() //open history file
+});
+//get some user input
+var input = prompt()
+prompt.history.save() //save history back to file
+```
+
+See the [prompt-sync-history](http://npm.im/prompt-sync-history) module
+for options, or fork it for customized behaviour. 
+
+# API
+
+## `require('prompt-sync')(config) => prompt` 
+
+Returns an instance of the `prompt` function.
+Takes `config` option with the following possible properties
+
+`sigint`: Default is `false`. A ^C may be pressed during the input process to abort the text entry. If sigint it `false`, prompt returns `null`. If sigint is `true` the ^C will be handled in the traditional way: as a SIGINT signal causing process to exit with code 130.
+
+`eot`: Default is `false`. A ^D pressed as the first character of an input line causes prompt-sync to echo `exit` and exit the process with code 0.
+
+`autocomplete`: A completer function that will be called when user enters TAB to allow for autocomplete. It takes a string as an argument an returns an array of strings that are possible matches for completion. An empty array is returned if there are no matches.
+
+`history`: Takes an object that supplies a "history interface", see [prompt-sync-history](http://npm.im/prompt-sync-history) for an example.
+
+## `prompt(ask, value, opts)`
+
+`ask` is the label of the prompt, `value` is the default value
+in absence of a response. 
+
+The `opts` argument can also be in the first or second parameter position.
+
+Opts can have the following properties
+
+`echo`: Default is `'*'`. If set the password will be masked with the specified character. For hidden input, set echo to `''` (or use `prompt.hide`).
+
+`autocomplete`: Overrides the instance `autocomplete` function to allow for custom 
+autocompletion of a particular prompt.
+
+`value`: Same as the `value` parameter, the default value for the prompt. If `opts`
+is in the third position, this property will *not* overwrite the `value` parameter.
+
+`ask`: Sames as the `value` parameter. The prompt label. If `opts` is not in the first position, the `ask` parameter will *not* be overridden by this property.
+
+## `prompt.hide(ask)`
+
+Convenience method for creating a standard hidden password prompt, 
+this is the same as `prompt(ask, {echo: ''})`
+
+
+# LINE EDITING
+Line editing is enabled in the non-hidden mode. (use up/down arrows for history and backspace and left/right arrows for editing)
+
+History is not set when using hidden mode.
+
+# EXAMPLES
+
+```js
+  //basic:
+  console.log(require('prompt-sync')()('tell me something about yourself: '))
+
+  var prompt = require('prompt-sync')({
+    history: require('prompt-sync-history')(),
+    autocomplete: complete(['hello1234', 'he', 'hello', 'hello12', 'hello123456']),
+    sigint: false
+  });
+
+  var value = 'frank';
+  var name = prompt('enter name: ', value);
+  console.log('enter echo * password');
+  var pw = prompt({echo: '*'});
+  var pwb = prompt('enter hidden password (or don\'t): ', {echo: '', value: '*pwb default*'})
+  var pwc = prompt.hide('enter another hidden password: ')
+  var autocompleteTest = prompt('custom autocomplete: ', {
+    autocomplete: complete(['bye1234', 'by', 'bye12', 'bye123456'])
+  });
+
+  prompt.history.save();
+
+  console.log('\nName: %s\nPassword *: %s\nHidden password: %s\nAnother Hidden password: %s', name, pw, pwb, pwc);
+  console.log('autocomplete2: ', autocompleteTest);
+
+  function complete(commands) {
+    return function (str) {
+      var i;
+      var ret = [];
+      for (i=0; i< commands.length; i++) {
+        if (commands[i].indexOf(str) == 0)
+          ret.push(commands[i]);
+      }
+      return ret;
+    };
+  };
+```

+ 243 - 0
TileManor/scripts/electron/node_modules/prompt-sync/index.js

@@ -0,0 +1,243 @@
+'use strict'
+
+var fs = require('fs');
+var stripAnsi = require('strip-ansi');
+var term = 13; // carriage return
+
+/**
+ * create -- sync function for reading user input from stdin
+ * @param   {Object} config {
+ *   sigint: {Boolean} exit on ^C
+ *   autocomplete: {StringArray} function({String})
+ *   history: {String} a history control object (see `prompt-sync-history`)
+ * }
+ * @returns {Function} prompt function
+ */
+
+ // for ANSI escape codes reference see https://en.wikipedia.org/wiki/ANSI_escape_code
+
+function create(config) {
+
+  config = config || {};
+  var sigint = config.sigint;
+  var eot = config.eot;
+  var autocomplete = config.autocomplete =
+    config.autocomplete || function(){return []};
+  var history = config.history;
+  prompt.history = history || {save: function(){}};
+  prompt.hide = function (ask) { return prompt(ask, {echo: ''}) };
+
+  return prompt;
+
+
+  /**
+   * prompt -- sync function for reading user input from stdin
+   *  @param {String} ask opening question/statement to prompt for
+   *  @param {String} value initial value for the prompt
+   *  @param   {Object} opts {
+   *   echo: set to a character to be echoed, default is '*'. Use '' for no echo
+   *   value: {String} initial value for the prompt
+   *   ask: {String} opening question/statement to prompt for, does not override ask param
+   *   autocomplete: {StringArray} function({String})
+   * }
+   *
+   * @returns {string} Returns the string input or (if sigint === false)
+   *                   null if user terminates with a ^C
+   */
+
+
+  function prompt(ask, value, opts) {
+    var insert = 0, savedinsert = 0, res, i, savedstr;
+    opts = opts || {};
+
+    if (Object(ask) === ask) {
+      opts = ask;
+      ask = opts.ask;
+    } else if (Object(value) === value) {
+      opts = value;
+      value = opts.value;
+    }
+    ask = ask || '';
+    var echo = opts.echo;
+    var masked = 'echo' in opts;
+    autocomplete = opts.autocomplete || autocomplete;
+
+    var fd = (process.platform === 'win32') ?
+      process.stdin.fd :
+      fs.openSync('/dev/tty', 'rs');
+
+    var wasRaw = process.stdin.isRaw;
+    if (!wasRaw) { process.stdin.setRawMode && process.stdin.setRawMode(true); }
+
+    var buf = Buffer.alloc(3);
+    var str = '', character, read;
+
+    savedstr = '';
+
+    if (ask) {
+      process.stdout.write(ask);
+    }
+
+    var cycle = 0;
+    var prevComplete;
+
+    while (true) {
+      read = fs.readSync(fd, buf, 0, 3);
+      if (read > 1) { // received a control sequence
+        switch(buf.toString()) {
+          case '\u001b[A':  //up arrow
+            if (masked) break;
+            if (!history) break;
+            if (history.atStart()) break;
+
+            if (history.atEnd()) {
+              savedstr = str;
+              savedinsert = insert;
+            }
+            str = history.prev();
+            insert = str.length;
+            process.stdout.write('\u001b[2K\u001b[0G' + ask + str);
+            break;
+          case '\u001b[B':  //down arrow
+            if (masked) break;
+            if (!history) break;
+            if (history.pastEnd()) break;
+
+            if (history.atPenultimate()) {
+              str = savedstr;
+              insert = savedinsert;
+              history.next();
+            } else {
+              str = history.next();
+              insert = str.length;
+            }
+            process.stdout.write('\u001b[2K\u001b[0G'+ ask + str + '\u001b['+(insert+ask.length+1)+'G');
+            break;
+          case '\u001b[D': //left arrow
+            if (masked) break;
+            var before = insert;
+            insert = (--insert < 0) ? 0 : insert;
+            if (before - insert)
+              process.stdout.write('\u001b[1D');
+            break;
+          case '\u001b[C': //right arrow
+            if (masked) break;
+            insert = (++insert > str.length) ? str.length : insert;
+            process.stdout.write('\u001b[' + (insert+ask.length+1) + 'G');
+            break;
+          default:
+            if (buf.toString()) {
+              str = str + buf.toString();
+              str = str.replace(/\0/g, '');
+              insert = str.length;
+              promptPrint(masked, ask, echo, str, insert);
+              process.stdout.write('\u001b[' + (insert+ask.length+1) + 'G');
+              buf = Buffer.alloc(3);
+            }
+        }
+        continue; // any other 3 character sequence is ignored
+      }
+
+      // if it is not a control character seq, assume only one character is read
+      character = buf[read-1];
+
+      // catch a ^C and return null
+      if (character == 3){
+        process.stdout.write('^C\n');
+        fs.closeSync(fd);
+
+        if (sigint) process.exit(130);
+
+        process.stdin.setRawMode && process.stdin.setRawMode(wasRaw);
+
+        return null;
+      }
+
+      // catch a ^D and exit
+      if (character == 4) {
+        if (str.length == 0 && eot) {
+          process.stdout.write('exit\n');
+          process.exit(0);
+        }
+      }
+
+      // catch the terminating character
+      if (character == term) {
+        fs.closeSync(fd);
+        if (!history) break;
+        if (!masked && str.length) history.push(str);
+        history.reset();
+        break;
+      }
+
+      // catch a TAB and implement autocomplete
+      if (character == 9) { // TAB
+        res = autocomplete(str);
+
+        if (str == res[0]) {
+          res = autocomplete('');
+        } else {
+          prevComplete = res.length;
+        }
+
+        if (res.length == 0) {
+          process.stdout.write('\t');
+          continue;
+        }
+
+        var item = res[cycle++] || res[cycle = 0, cycle++];
+
+        if (item) {
+          process.stdout.write('\r\u001b[K' + ask + item);
+          str = item;
+          insert = item.length;
+        }
+      }
+
+      if (character == 127 || (process.platform == 'win32' && character == 8)) { //backspace
+        if (!insert) continue;
+        str = str.slice(0, insert-1) + str.slice(insert);
+        insert--;
+        process.stdout.write('\u001b[2D');
+      } else {
+        if ((character < 32 ) || (character > 126))
+            continue;
+        str = str.slice(0, insert) + String.fromCharCode(character) + str.slice(insert);
+        insert++;
+      };
+
+      promptPrint(masked, ask, echo, str, insert);
+
+    }
+
+    process.stdout.write('\n')
+
+    process.stdin.setRawMode && process.stdin.setRawMode(wasRaw);
+
+    return str || value || '';
+  };
+
+
+  function promptPrint(masked, ask, echo, str, insert) {
+    if (masked) {
+        process.stdout.write('\u001b[2K\u001b[0G' + ask + Array(str.length+1).join(echo));
+    } else {
+      process.stdout.write('\u001b[s');
+      if (insert == str.length) {
+          process.stdout.write('\u001b[2K\u001b[0G'+ ask + str);
+      } else {
+        if (ask) {
+          process.stdout.write('\u001b[2K\u001b[0G'+ ask + str);
+        } else {
+          process.stdout.write('\u001b[2K\u001b[0G'+ str + '\u001b[' + (str.length - insert) + 'D');
+        }
+      }
+
+      // Reposition the cursor to the right of the insertion point
+      var askLength = stripAnsi(ask).length;
+      process.stdout.write(`\u001b[${askLength+1+(echo==''? 0:insert)}G`);
+    }
+  }
+};
+
+module.exports = create;

+ 70 - 0
TileManor/scripts/electron/node_modules/prompt-sync/package.json

@@ -0,0 +1,70 @@
+{
+  "_from": "prompt-sync",
+  "_id": "prompt-sync@4.2.0",
+  "_inBundle": false,
+  "_integrity": "sha512-BuEzzc5zptP5LsgV5MZETjDaKSWfchl5U9Luiu8SKp7iZWD5tZalOxvNcZRwv+d2phNFr8xlbxmFNcRKfJOzJw==",
+  "_location": "/prompt-sync",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "tag",
+    "registry": true,
+    "raw": "prompt-sync",
+    "name": "prompt-sync",
+    "escapedName": "prompt-sync",
+    "rawSpec": "",
+    "saveSpec": null,
+    "fetchSpec": "latest"
+  },
+  "_requiredBy": [
+    "#USER",
+    "/"
+  ],
+  "_resolved": "https://registry.npmjs.org/prompt-sync/-/prompt-sync-4.2.0.tgz",
+  "_shasum": "0198f73c5b70e3b03e4b9033a50540a7c9a1d7f4",
+  "_spec": "prompt-sync",
+  "_where": "/Users/xulianxin/Documents/develop/game/TileMatch/TileManor.Lv/TileManor/scripts/electron",
+  "bugs": {
+    "url": "https://github.com/heapwolf/prompt-sync/issues"
+  },
+  "bundleDependencies": false,
+  "contributors": [
+    {
+      "name": "Paolo Fragomeni",
+      "email": "paolo@async.ly"
+    },
+    {
+      "name": "David Mark Clements",
+      "email": "david.clements@nearform.com"
+    }
+  ],
+  "dependencies": {
+    "strip-ansi": "^5.0.0"
+  },
+  "deprecated": false,
+  "description": "a synchronous prompt for node.js",
+  "devDependencies": {
+    "prompt-sync-history": "^1.0.1"
+  },
+  "homepage": "https://github.com/heapwolf/prompt-sync#readme",
+  "keywords": [
+    "prompt",
+    "sync",
+    "blocking",
+    "readline",
+    "input",
+    "getline",
+    "repl",
+    "history"
+  ],
+  "license": "MIT",
+  "main": "index.js",
+  "name": "prompt-sync",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/heapwolf/prompt-sync.git"
+  },
+  "scripts": {
+    "test": "node test"
+  },
+  "version": "4.2.0"
+}

+ 38 - 0
TileManor/scripts/electron/node_modules/prompt-sync/test.js

@@ -0,0 +1,38 @@
+//basic:
+console.log(require('./')()('tell me something about yourself: '))
+
+// ANSI escape codes colored text test
+require('./')()('\u001B[31mcolored text: \u001B[39m');
+
+var prompt = require('./')({
+  history: require('prompt-sync-history')(),
+  autocomplete: complete(['hello1234', 'he', 'hello', 'hello12', 'hello123456']),
+  sigint: false
+});
+
+var value = 'frank';
+var name = prompt('enter name: ', value);
+console.log('enter echo * password');
+var pw = prompt({echo: '*'});
+var pwb = prompt('enter hidden password (or don\'t): ', {echo: '', value: '*pwb default*'})
+var pwc = prompt.hide('enter another hidden password: ')
+var autocompleteTest = prompt('custom autocomplete: ', {
+  autocomplete: complete(['bye1234', 'by', 'bye12', 'bye123456'])
+});
+
+prompt.history.save();
+
+console.log('\nName: %s\nPassword *: %s\nHidden password: %s\nAnother Hidden password: %s', name, pw, pwb, pwc);
+console.log('autocomplete2: ', autocompleteTest);
+
+function complete(commands) {
+  return function (str) {
+    var i;
+    var ret = [];
+    for (i=0; i< commands.length; i++) {
+      if (commands[i].indexOf(str) == 0)
+        ret.push(commands[i]);
+    }
+    return ret;
+  };
+};

+ 15 - 0
TileManor/scripts/electron/node_modules/strip-ansi/index.d.ts

@@ -0,0 +1,15 @@
+/**
+Strip [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) from a string.
+
+@example
+```
+import stripAnsi from 'strip-ansi';
+
+stripAnsi('\u001B[4mUnicorn\u001B[0m');
+//=> 'Unicorn'
+
+stripAnsi('\u001B]8;;https://github.com\u0007Click\u001B]8;;\u0007');
+//=> 'Click'
+```
+*/
+export default function stripAnsi(string: string): string;

+ 7 - 0
TileManor/scripts/electron/node_modules/strip-ansi/index.js

@@ -0,0 +1,7 @@
+'use strict';
+const ansiRegex = require('ansi-regex');
+
+const stripAnsi = string => typeof string === 'string' ? string.replace(ansiRegex(), '') : string;
+
+module.exports = stripAnsi;
+module.exports.default = stripAnsi;

+ 9 - 0
TileManor/scripts/electron/node_modules/strip-ansi/license

@@ -0,0 +1,9 @@
+MIT License
+
+Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 86 - 0
TileManor/scripts/electron/node_modules/strip-ansi/package.json

@@ -0,0 +1,86 @@
+{
+  "_from": "strip-ansi@^5.0.0",
+  "_id": "strip-ansi@5.2.0",
+  "_inBundle": false,
+  "_integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+  "_location": "/strip-ansi",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "range",
+    "registry": true,
+    "raw": "strip-ansi@^5.0.0",
+    "name": "strip-ansi",
+    "escapedName": "strip-ansi",
+    "rawSpec": "^5.0.0",
+    "saveSpec": null,
+    "fetchSpec": "^5.0.0"
+  },
+  "_requiredBy": [
+    "/prompt-sync"
+  ],
+  "_resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+  "_shasum": "8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae",
+  "_spec": "strip-ansi@^5.0.0",
+  "_where": "/Users/xulianxin/Documents/develop/game/TileMatch/TileManor.Lv/TileManor/scripts/electron/node_modules/prompt-sync",
+  "author": {
+    "name": "Sindre Sorhus",
+    "email": "sindresorhus@gmail.com",
+    "url": "sindresorhus.com"
+  },
+  "bugs": {
+    "url": "https://github.com/chalk/strip-ansi/issues"
+  },
+  "bundleDependencies": false,
+  "dependencies": {
+    "ansi-regex": "^4.1.0"
+  },
+  "deprecated": false,
+  "description": "Strip ANSI escape codes from a string",
+  "devDependencies": {
+    "ava": "^1.3.1",
+    "tsd-check": "^0.5.0",
+    "xo": "^0.24.0"
+  },
+  "engines": {
+    "node": ">=6"
+  },
+  "files": [
+    "index.js",
+    "index.d.ts"
+  ],
+  "homepage": "https://github.com/chalk/strip-ansi#readme",
+  "keywords": [
+    "strip",
+    "trim",
+    "remove",
+    "ansi",
+    "styles",
+    "color",
+    "colour",
+    "colors",
+    "terminal",
+    "console",
+    "string",
+    "tty",
+    "escape",
+    "formatting",
+    "rgb",
+    "256",
+    "shell",
+    "xterm",
+    "log",
+    "logging",
+    "command-line",
+    "text"
+  ],
+  "license": "MIT",
+  "name": "strip-ansi",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/chalk/strip-ansi.git"
+  },
+  "scripts": {
+    "test": "xo && ava && tsd-check"
+  },
+  "version": "5.2.0"
+}

+ 61 - 0
TileManor/scripts/electron/node_modules/strip-ansi/readme.md

@@ -0,0 +1,61 @@
+# strip-ansi [![Build Status](https://travis-ci.org/chalk/strip-ansi.svg?branch=master)](https://travis-ci.org/chalk/strip-ansi)
+
+> Strip [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) from a string
+
+---
+
+<div align="center">
+	<b>
+		<a href="https://tidelift.com/subscription/pkg/npm-strip-ansi?utm_source=npm-strip-ansi&utm_medium=referral&utm_campaign=readme">Get professional support for 'strip-ansi' with a Tidelift subscription</a>
+	</b>
+	<br>
+	<sub>
+		Tidelift helps make open source sustainable for maintainers while giving companies<br>assurances about security, maintenance, and licensing for their dependencies.
+	</sub>
+</div>
+
+---
+
+## Install
+
+```
+$ npm install strip-ansi
+```
+
+
+## Usage
+
+```js
+const stripAnsi = require('strip-ansi');
+
+stripAnsi('\u001B[4mUnicorn\u001B[0m');
+//=> 'Unicorn'
+
+stripAnsi('\u001B]8;;https://github.com\u0007Click\u001B]8;;\u0007');
+//=> 'Click'
+```
+
+
+## Security
+
+To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure.
+
+
+## Related
+
+- [strip-ansi-cli](https://github.com/chalk/strip-ansi-cli) - CLI for this module
+- [strip-ansi-stream](https://github.com/chalk/strip-ansi-stream) - Streaming version of this module
+- [has-ansi](https://github.com/chalk/has-ansi) - Check if a string has ANSI escape codes
+- [ansi-regex](https://github.com/chalk/ansi-regex) - Regular expression for matching ANSI escape codes
+- [chalk](https://github.com/chalk/chalk) - Terminal string styling done right
+
+
+## Maintainers
+
+- [Sindre Sorhus](https://github.com/sindresorhus)
+- [Josh Junon](https://github.com/qix-)
+
+
+## License
+
+MIT

+ 26 - 0
TileManor/scripts/electron/package-lock.json

@@ -81,6 +81,11 @@
         "@types/node": "*"
       }
     },
+    "ansi-regex": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+      "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
+    },
     "boolean": {
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz",
@@ -190,6 +195,11 @@
         "extract-zip": "^2.0.1"
       }
     },
+    "electron-prompt": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/electron-prompt/-/electron-prompt-1.7.0.tgz",
+      "integrity": "sha512-IfqJYEgcRO6NuyPROo8AtdkAiZ6N9I1lQEf4dJAkPuhV5YgOHdmLqZJf6OXumZJfzrjpzCM5jHeYOrhGdgbnEA=="
+    },
     "end-of-stream": {
       "version": "1.4.4",
       "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
@@ -474,6 +484,14 @@
       "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
       "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="
     },
+    "prompt-sync": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/prompt-sync/-/prompt-sync-4.2.0.tgz",
+      "integrity": "sha512-BuEzzc5zptP5LsgV5MZETjDaKSWfchl5U9Luiu8SKp7iZWD5tZalOxvNcZRwv+d2phNFr8xlbxmFNcRKfJOzJw==",
+      "requires": {
+        "strip-ansi": "^5.0.0"
+      }
+    },
     "pump": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
@@ -541,6 +559,14 @@
       "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
       "optional": true
     },
+    "strip-ansi": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+      "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+      "requires": {
+        "ansi-regex": "^4.1.0"
+      }
+    },
     "sumchecker": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz",

+ 3 - 1
TileManor/scripts/electron/package.json

@@ -7,6 +7,8 @@
   },
   "dependencies": {
     "csv-parse": "^5.5.3",
-    "electron": "28.1.4"
+    "electron": "28.1.4",
+    "electron-prompt": "^1.7.0",
+    "prompt-sync": "^4.2.0"
   }
 }

+ 2 - 2
TileManor/scripts/electron/preload.js

@@ -5,13 +5,13 @@ contextBridge.exposeInMainWorld(
   {
     send: (channel, data) => {
       // whitelist channels
-      let validChannels = ['parse-csv'];
+      let validChannels = ['parse-csv', 'get-file-list', 'open-newlv-prompt', 'open-newinst-prompt'];
       if (validChannels.includes(channel)) {
         ipcRenderer.send(channel, data);
       }
     },
     receive: (channel, func) => {
-      let validChannels = ['csv-data'];
+      let validChannels = ['csv-data', 'file-list-data', 'file-list-error', 'prompt-newlv-reply', 'prompt-newinst-reply'];
       if (validChannels.includes(channel)) {
         // Deliberately strip event as it includes `sender` 
         ipcRenderer.on(channel, (event, ...args) => func(...args));

+ 11 - 0
TileManor/scripts/electron/template.html

@@ -0,0 +1,11 @@
+<!-- level.html -->
+<div style="display: flex; flex-direction: row; height: 100vh; width: 100%;">
+    <div style="overflow-y: scroll; height: 100%; width: 30%;">
+        <ul id="template-list" style="height: 100%;">
+        </ul>
+    </div>
+    <div id="template-details" style="overflow-y: scroll; height: 100%; width: 60%;">
+        
+    </div>
+
+</div>