diff --git a/Electron/AMAI-release/install.js b/Electron/AMAI-release/install.js index 95bd469c7..3c9790a6a 100644 --- a/Electron/AMAI-release/install.js +++ b/Electron/AMAI-release/install.js @@ -3,7 +3,8 @@ const path = require("path"); const { takeHeapSnapshot } = require("process"); const spawnSync = require("child_process").spawnSync; const arrayOfFiles = []; - +let totalFiles = 0; +let currentFileIndex = 0; /** uncomment to debbug */ // const ls = spawnSync( @@ -33,16 +34,14 @@ const installOnDirectory = async () => { const args = process.argv.slice(2); const response = args[0]; const commander = args[1]; - const ver = args[2] - const language = args[3] - const installCommander = commander == 1 - const vsAICommander = commander == 2 - let bj = 'Blizzard.j' - if (vsAICommander) { bj = 'vsai\\Blizzard.j'} - + const ver = args[2]; + const language = args[3]; + const installCommander = commander == 1; + const vsAICommander = commander == 2; + let bj = 'Blizzard.j'; + if (vsAICommander) {bj = 'vsai\\Blizzard.j'} const commonAIPath = `Scripts\\${ver}\\common.ai` const blizzardPath =`Scripts\\${ver}\\Blizzard.j` - process.send(`#### Installing AMAI for ${ver} Commander ${commander > 0 ? bj : 'None'} forcing ai language to ${language || 'default'} ####`); // TODO: change to receive array of maps @@ -71,8 +70,6 @@ const installOnDirectory = async () => { return } - - if (language !== '-') { setLanguage(commonAIPath, language); if (installCommander) { @@ -85,8 +82,9 @@ const installOnDirectory = async () => { } } - if(arrayOfFiles) { + totalFiles = arrayOfFiles.length; + //process.send({ type: 'progress', current: currentFileIndex, total: totalFiles }); for (const file of arrayOfFiles) { /** uncomment to debbug */ // process.send(`path.extname(file): ${path.extname(file)}`); @@ -94,6 +92,15 @@ const installOnDirectory = async () => { const ext = path.extname(file).toLowerCase(); if(ext.indexOf(`w3m`) >= 0 || ext.indexOf(`w3x`) >= 0) { + currentFileIndex++; + // Send complete progress data including both current and total + if (process.send) { + process.send({ + type: 'progress', + current: currentFileIndex, + total: totalFiles, + }); + } process.send(`#### Installing ${ver} into file: ${file} ####`); } else { process.send(`skip file: ${file}`); @@ -128,7 +135,7 @@ const installOnDirectory = async () => { process.send(mpqEditor.error.message) : process.send(`Resize map hashtable size ${file}`); - const f1AddToMPQ = spawnSync( + const f1AddToMPQ = spawnSync( `MPQEditor.exe`, [ 'a', @@ -178,16 +185,15 @@ const installOnDirectory = async () => { f1AddVSAIToMPQ.error ? process.send(f1AddVSAIToMPQ.error.message) : process.send(`Installing VS Vanilla AI Scripts ${file}`); - } - const f2AddToMPQ = spawnSync( + const f2AddToMPQ = spawnSync( `MPQEditor.exe`, [ 'a', file, `Scripts\\${ver}\\${bj}`, - `Scripts\\Blizzard.j`, + `Scripts\\Blizzard.j` ], { encoding : `utf8` } ); @@ -206,7 +212,6 @@ const installOnDirectory = async () => { f2AddToMPQ.error ? process.send(f2AddToMPQ.error.message) : process.send(installCommander ? `Installing commander ${file}` : `Installing VS Vanilla AI commander ${file}`); - } const f3AddToMPQ = spawnSync( @@ -239,7 +244,6 @@ const installOnDirectory = async () => { } } - function setLanguage(file, language) { let data = fs.readFileSync(file, 'utf8'); const searchFor = /string language = "([^"]*)"/; diff --git a/Electron/app/main.ts b/Electron/app/main.ts index 6d045bc0f..6c945bccc 100644 --- a/Electron/app/main.ts +++ b/Electron/app/main.ts @@ -9,6 +9,7 @@ const cp = require('child_process'); let win: BrowserWindow = null; let translations : { [key: string]: string } = {}; let currentLanguage: string = "English"; +let defaultPath: string | null = null; const args = process.argv.slice(1), serve = args.some(val => val === '--serve'); @@ -25,13 +26,14 @@ const isDev = () => { const createWindow = (): BrowserWindow => { const size = screen.getPrimaryDisplay().workAreaSize; - // Create the browser window. win = new BrowserWindow({ x: 0, y: 0, width: size.width, height: size.height, + minWidth: 1280, + minHeight: 940, webPreferences: { devTools: true, nodeIntegration: true, @@ -76,16 +78,65 @@ const createWindow = (): BrowserWindow => { const execInstall = async (signal, commander: number = 1, isMap: boolean = false, ver: string = "REFORGED", forceLang: boolean) => { const controller = new AbortController(); - const response = dialog.showOpenDialogSync(win, { - // TODO: add i18n here - title : isMap ? translations["PAGES.ELECTRON.OPEN_MAP"] || '': translations["PAGES.ELECTRON.OPEN_DIR"] || '', - // TODO: Change to let multiples selections when is map - properties: isMap ? ['openFile'] : ['openDirectory'], - // TODO: add i18n here - filters: isMap ? [ + let response; + try { + const settingsPath = path.join(app.getPath('userData'), 'settings.json'); + if (fs.existsSync(settingsPath)) { + const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); + defaultPath = settings.defaultPath || null; + console.log('get default Path :',defaultPath); + } + } catch (err) { + console.error('Failed to load default path:', err); + } + // Handle folder mode (isMap = false) + if (!isMap) { + // If default path exists, use it directly + if (defaultPath) { + response = [defaultPath]; + } else { + // Show dialog and save selected path as default + response = dialog.showOpenDialogSync(win, { + title: translations["PAGES.ELECTRON.OPEN_DIR"] || '', + properties: ['openDirectory'], + }); + + // Save the selected path as default if not canceled + if (response && response.length > 0) { + defaultPath = response[0]; + console.log('set default Path :',defaultPath); + // Save to settings.json directly in main process + const settingsPath = path.join(app.getPath('userData'), 'settings.json'); + const settings = { + defaultPath: defaultPath + }; + fs.writeFileSync(settingsPath, JSON.stringify(settings)); + } + } + } else { + // Handle map mode (isMap = true) + const documentsPath = app.getPath('documents'); + response = dialog.showOpenDialogSync(win, { + title: translations["PAGES.ELECTRON.OPEN_MAP"] || '', + properties: ['openFile'], + filters: [ { name: translations["PAGES.ELECTRON.MAPFILE"] || '', extensions: ['w3x', 'w3m'] }, - ] : null, - }); + ], + // Use default path if available, otherwise open "documents" + defaultPath: defaultPath || documentsPath, + }); + // 选择文件后自动将文件所在目录设为默认路径 + if (response && response.length > 0) { + const filePath = response[0]; + const folderPath = path.dirname(filePath); + defaultPath = folderPath; + const settingsPath = path.join(app.getPath('userData'), 'settings.json'); + const settings = { defaultPath: folderPath }; + fs.writeFileSync(settingsPath, JSON.stringify(settings)); + console.log('Default path updated to:', folderPath); + } + console.log('default Path :',defaultPath); + } let child; @@ -156,10 +207,16 @@ const execInstall = async (signal, commander: number = 1, isMap: boolean = false } ); - // send messages to modal on front child.on('message', (message) => { - win.webContents.send('on-install-message', message); + if (typeof message === 'object' && message.type === 'progress') { + // Send progress updates via dedicated channel + console.log('progress:', message); + win.webContents.send('on-install-progress', message); + } else { + // Send regular messages via standard channel + win.webContents.send('on-install-message', message); + } }); // close modal on process finishes @@ -171,6 +228,37 @@ const execInstall = async (signal, commander: number = 1, isMap: boolean = false } } + +const setupFileOperations = () => { + ipcMain?.handle('file-operations', async (_, { operation, payload }) => { + switch(operation) { + case 'load-default-path': + const settingsPath = path.join(app.getPath('userData'), 'settings.json'); + if (fs.existsSync(settingsPath)) { + return JSON.parse(fs.readFileSync(settingsPath, 'utf8')).defaultPath; + } + return null; + + case 'select-folder': + const result = dialog.showOpenDialogSync(win, { + title: translations["PAGES.ELECTRON.OPEN_DIR"] || '', + defaultPath: payload?.defaultPath, + properties: ['openDirectory'], + }); + return result && result.length > 0 ? result[0] : null; + + case 'save-default-path': { + const settingsPath = path.join(app.getPath('userData'), 'settings.json'); + fs.writeFileSync(settingsPath, JSON.stringify({ defaultPath: payload })); + return true; + } + + default: + throw new Error(`unknow: ${operation}`); + } + }); +} + const installProcess = () => { let signal = {}; @@ -267,4 +355,5 @@ const installTrans = () => { init(); installTrans(); +setupFileOperations(); installProcess(); diff --git a/Electron/src/app/app.component.ts b/Electron/src/app/app.component.ts index 289b43199..e070e6752 100644 --- a/Electron/src/app/app.component.ts +++ b/Electron/src/app/app.component.ts @@ -14,6 +14,9 @@ export class AppComponent implements AfterViewChecked { public active = false; public couldClose = false; public messages = []; + public currentFile = 0; + public totalFiles = 0; + public installingText = ''; @ViewChild('logareawrapper') private readonly logContainer: ElementRef; @@ -44,16 +47,29 @@ export class AppComponent implements AfterViewChecked { }) this.cdr.detectChanges(); }); - if (electronService.isElectron) { this.menuService.createMenu(); + this.electronService.ipcRenderer.on('on-install-progress', (_, args: { current: number, total: number }) => { + // console.log('totalFiles-in:', args.total, 'currentFile-in:', args.current); + if ( args.total > 0 && this.totalFiles < args.total) { + this.totalFiles = args.total; + } + if (this.currentFile < this.totalFiles) { + this.currentFile++; + this.title = '(' + this.currentFile + '/' + this.totalFiles + ') ' + this.installingText; + } + // console.log('totalFiles-out:', this.totalFiles, 'currentFile-out:', this.currentFile); + this.cdr.detectChanges(); + }); + // TODO: add 'push notification'/'notification' this.electronService.ipcRenderer.on('on-install-init', (_, args: InstallModel) => { console.log('args-install-init', args) this.translate.get(t_('PAGES.APP.INSTALLING'), {path: args.response}).subscribe((res: string) => { - this.title = res + this.title = '(0/X) ' + res; + this.installingText = res; }); this.active = true; this.couldClose = false; @@ -77,16 +93,19 @@ export class AppComponent implements AfterViewChecked { console.log('args-install-empty', args); this.active = false; this.couldClose = true; + this.totalFiles = 0; + this.currentFile = 0; this.cdr.detectChanges(); }); // TODO: add 'push notification'/'notification' this.electronService.ipcRenderer.on('on-install-exit', (_, args) => { this.translate.get(t_('PAGES.APP.INSTALL_DONE')).subscribe((res: string) => { - this.title = res; + this.title = '(' + this.currentFile + '/' + this.totalFiles + ')' + ' ' + res; }); this.couldClose = true; - + this.totalFiles = 0; + this.currentFile = 0; this .menuService .changeEnabledMenuState(true); @@ -104,7 +123,8 @@ export class AppComponent implements AfterViewChecked { this.electronService.ipcRenderer.on('on-install-error', (_, args) => { console.log('args-install-error', args); this.couldClose = true; - + this.totalFiles = 0; + this.currentFile = 0; this .menuService .changeEnabledMenuState(true); diff --git a/Electron/src/app/home/home.component.html b/Electron/src/app/home/home.component.html index 0711661b8..ff0419972 100644 --- a/Electron/src/app/home/home.component.html +++ b/Electron/src/app/home/home.component.html @@ -7,20 +7,20 @@

REFSelected REF - 1.33+ - Optimal:2.0.4 + 2.0.3+ + {{ 'PAGES.HOME.REF_BEST_GAME_VERSION' | translate }}
TFTSelected TFT - 1.24+ - Optimal:1.24-1.28 + 1.24e+ + {{ 'PAGES.HOME.TFT_BEST_GAME_VERSION' | translate }}
ROCSelected ROC - 1.24 - 1.31 - Optimal:1.24-1.28 + 1.24e+ ~ 1.31 + {{ 'PAGES.HOME.ROC_BEST_GAME_VERSION' | translate }}
@@ -50,4 +50,14 @@

{{ 'PAGES.HOME.FORCELANG' | translate }} - +
+ + {{ 'PAGES.HOME.DEFAULT_PATH' | translate }} + + +
+ \ No newline at end of file diff --git a/Electron/src/app/home/home.component.scss b/Electron/src/app/home/home.component.scss index 1529c2215..605cc393e 100644 --- a/Electron/src/app/home/home.component.scss +++ b/Electron/src/app/home/home.component.scss @@ -1,223 +1,265 @@ :host { - :root { - --switch-width: 300px; /* 最大宽度 */ - } + :root { + --switch-width: 300px; /* 最大宽度 */ + } - .center-container { - text-align: center; - justify-content: center; - align-items: center; - margin: auto; - max-width: 100%; - } + .center-container { + text-align: center; + justify-content: center; + align-items: center; + margin: auto; + max-width: 100%; + } - .container { - color: white; - } + .container { + color: white; + } - .headtitle-text { - text-align: center; - font-size: 2rem; - font-weight: bold; - } + .headtitle-text { + text-align: center; + font-size: 2rem; + font-weight: bold; + } - .subtitle-text { - text-align: center; - font-size: 2rem; - font-weight: bold; - margin-top: 4rem; - } + .subtitle-text { + text-align: center; + font-size: 2rem; + font-weight: bold; + margin-top: 4rem; + } - .ContainerLattice { - display: flex; - text-align: center; - justify-content: space-around; - align-items: center; - max-width: inherit; /* 继承父元素(.center-container)的最大宽度 */ - width: 100%; - gap: 10px; - margin-top: 2rem; - } + .ContainerLattice { + display: flex; + text-align: center; + justify-content: space-around; + align-items: center; + max-width: inherit; /* 继承父元素(.center-container)的最大宽度 */ + width: 100%; + gap: 10px; + margin-top: 2rem; + } - .AdvancedSettings, - .MoreSettings { - background-color: black; - } + .AdvancedSettings, + .MoreSettings { + background-color: black; + } - .MoreSettings { - margin-top: 1rem; - } + .MoreSettings { + margin-top: 1rem; + } - .imageContainerROC, - .imageContainerTFT, - .imageContainerREF { - display: block; - align-items: center; - justify-content: center; - max-width: 512px; - width: 50%; - height: auto; - background-color: black; - min-width: 200px; + .imageContainerROC, + .imageContainerTFT, + .imageContainerREF { + display: block; + align-items: center; + justify-content: center; + max-width: 512px; + width: 50%; + height: auto; + background-color: black; + min-width: 200px; + } + + .imageContainerROC span, + .imageContainerTFT span, + .imageContainerREF span { + background-color: black; + font-size: 2rem; + } + + .reccomendations { + font-size: 14pt; + display: block; + color: gold; + } + + /* 当屏幕宽度小于等于800px时,减小字体大小 */ + @media (max-width: 800px) { + .headtitle-text, .subtitle-text { + font-size: 1rem; } .imageContainerROC span, .imageContainerTFT span, .imageContainerREF span { - background-color: black; - font-size: 2rem; + font-size: 1rem; } .reccomendations { - font-size: 14pt; - display: block; - color: gold; + font-size: 10pt; } + } - /* 当屏幕宽度小于等于800px时,减小字体大小 */ - @media (max-width: 800px) { - .headtitle-text, .subtitle-text { - font-size: 1rem; - } - - .imageContainerROC span, - .imageContainerTFT span, - .imageContainerREF span { - font-size: 1rem; - } - - .reccomendations { - font-size: 10pt; - } + @media (max-width: 1600px) { + .ContainerLattice img { + width: 100%; } + } - @media (max-width: 1600px) { - .ContainerLattice img { - width: 100%; - } - } + .commander-group { + display: flex; + justify-content: center; + text-align: start; + align-items: center; + position: relative; + cursor: pointer; + width: 30%; + font-size: 1rem; + transform: scale(1.5); + min-width: 200px; + } - .commander-group { - display: flex; - justify-content: center; - text-align: start; - align-items: center; - position: relative; - cursor: pointer; - width: 30%; - font-size: 1rem; - transform: scale(1.5); - min-width: 200px; - } + .checkboxbj { + display: flex; + justify-content: center; + text-align: start; + align-items: center; + position: relative; + cursor: pointer; + width: 30%; + font-size: 1rem; + transform: scale(1.5); + min-width: 200px; + padding: 4px 10px; + border-radius: 12px; + background-color: black; + color: #FFFFFF; + } - .checkboxbj { - display: flex; - justify-content: center; - text-align: start; - align-items: center; - position: relative; - cursor: pointer; - width: 30%; - font-size: 1rem; - transform: scale(1.5); - min-width: 200px; - padding: 4px 10px; - border-radius: 12px; - background-color: black; - color: #FFFFFF; - } + .radio-btn { + display: inline-flex; /* 使用 flex 布局来更好地控制内部元素 */ + align-items: center; /* 垂直居中文本 */ + justify-content: center; /* 水平居中文本 */ + padding: 4px 10px; /* 内边距,根据需要调整 */ + border-radius: 12px; /* 圆角 */ + font-weight: bold; + background-color: #AAAAAA; /* 未选中时背景色 */ + cursor: pointer; /* 鼠标悬停时显示指针 */ + transition: background-color 0.3s, font-size 0.3s; /* 背景色变化过渡效果和字体大小变化过渡效果 */ + width: 220px; /* 固定宽度 */ + white-space: nowrap; /* 防止文本换行 */ + overflow: hidden; /* 隐藏超出部分 */ + text-overflow: ellipsis; /* 显示省略号 */ + box-sizing: border-box; /* 包含padding和border */ + font-size: calc(100% - 2px); /* 初始字体大小 */ + max-width: 100%; /* 最大宽度 */ + } - .radio-btn { - display: inline-flex; /* 使用 flex 布局来更好地控制内部元素 */ - align-items: center; /* 垂直居中文本 */ - justify-content: center; /* 水平居中文本 */ - padding: 4px 10px; /* 内边距,根据需要调整 */ - border-radius: 12px; /* 圆角 */ - font-weight: bold; - background-color: #AAAAAA; /* 未选中时背景色 */ - cursor: pointer; /* 鼠标悬停时显示指针 */ - transition: background-color 0.3s, font-size 0.3s; /* 背景色变化过渡效果和字体大小变化过渡效果 */ - // width: 220px; /* 固定宽度 */ - white-space: nowrap; /* 防止文本换行 */ - overflow: visible; /* 隐藏超出部分 */ - text-overflow: ellipsis; /* 显示省略号 */ - box-sizing: border-box; /* 包含padding和border */ - font-size: calc(100% - 2px); /* 初始字体大小 */ - max-width: 100%; /* 最大宽度 */ - } + /* 鼠标悬停样式 */ + .radio-btn:hover { + color: #FFFFFF; /* 悬停时文字颜色 */ + background-color: #47A4F9; /* 悬停时背景色 */ + } - /* 鼠标悬停样式 */ - .radio-btn:hover { - color: #FFFFFF; /* 悬停时文字颜色 */ - background-color: #47A4F9; /* 悬停时背景色 */ - } + input[type="radio"]:checked + .radio-btn { + background-color: rgb(33, 4, 251); /* 选中时背景色 */ + color: #FFFFFF; /* 选中时文字颜色 */ + } - input[type="radio"]:checked + .radio-btn { - background-color: rgb(33, 4, 251); /* 选中时背景色 */ - color: #FFFFFF; /* 选中时文字颜色 */ - } + .switch { + display: flex; + justify-content: center; + align-items: center; + text-align: center; + position: relative; + max-width: var(--switch-width); + width: 36%; + height: 32px; + border: 3px solid #5c5c5c; /* 添加边框 */ + box-sizing: border-box; /* 包含边框和内填充在内的总宽度 */ + border-radius: 26px; + min-width: 400px; + } - .switch { - display: flex; - justify-content: center; - align-items: center; - text-align: center; - position: relative; - max-width: var(--switch-width); - width: 36%; - height: 32px; - border: 3px solid #5c5c5c; /* 添加边框 */ - box-sizing: border-box; /* 包含边框和内填充在内的总宽度 */ - border-radius: 26px; - min-width: 400px; - } + .slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgb(33, 4, 251); + border-radius: 26px; + -webkit-transition: .4s; + transition: .4s; - .slider { - position: absolute; - cursor: pointer; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgb(33, 4, 251); - border-radius: 26px; - -webkit-transition: .4s; - transition: .4s; - - color: #ffffff; - font-size: 1rem; - font-weight: bold; - display: flex; - align-items: center; - justify-content: space-around; - } + color: #ffffff; + font-size: 1rem; + font-weight: bold; + display: flex; + align-items: center; + justify-content: space-around; + } - .slider:before { - position: absolute; - display: flex; /* 将其转换为弹性布局容器 */ - align-items: center; - justify-content: center; - content: ""; - white-space: nowrap; - left: 1px; - height: 100%; - width: 50%; - background-color: #ffffff; - border-radius: 26px; - -webkit-transition: .4s; - transition: .4s; - } + .slider:before { + position: absolute; + display: flex; /* 将其转换为弹性布局容器 */ + align-items: center; + justify-content: center; + content: ""; + white-space: nowrap; + left: 1px; + height: 100%; + width: 50%; + background-color: #ffffff; + border-radius: 26px; + -webkit-transition: .4s; + transition: .4s; + } - input:checked + .slider { - background-color: #36b61a; - } + input:checked + .slider { + background-color: #36b61a; + } + + input:checked + .slider:before { + content: ""; + -webkit-transform: translateX(100%); + -ms-transform: translateX(100%); + transform: translateX(100%); + margin-left: -1px; + } - input:checked + .slider:before { - content: ""; - -webkit-transform: translateX(100%); - -ms-transform: translateX(100%); - transform: translateX(100%); - margin-left: -1px; + .default-folder-container { + width: 100%; + margin: 1.5rem 0; + padding: 0.2rem; + background-color: #000000; + border-radius: 0; + color: white; + cursor: pointer; + text-align: center; + transition: background-color 0.2s ease; + display: none; + + &:has(.underlined-path) { + display: block; } + + &:hover { + background-color: #47A4F9; + } + + .default-folder-text { + display: block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } + + .underlined-path { + text-decoration: underline; + text-decoration-skip-ink: none; + text-underline-position: under; + text-decoration-thickness: 1px; + text-decoration-color: currentColor; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 100%; + display: inline-block; + vertical-align: middle; + } } \ No newline at end of file diff --git a/Electron/src/app/home/home.component.ts b/Electron/src/app/home/home.component.ts index f57e5b9e6..769d80dbe 100644 --- a/Electron/src/app/home/home.component.ts +++ b/Electron/src/app/home/home.component.ts @@ -1,6 +1,7 @@ import { Component, OnInit, ViewChild, ElementRef, HostListener, Injectable } from '@angular/core'; import { Router } from '@angular/router'; import { ElectronService } from '../core/services/electron/electron.service'; +import { TranslateService, LangChangeEvent } from "@codeandweb/ngx-translate"; @Injectable({ providedIn: 'root' @@ -12,6 +13,75 @@ import { ElectronService } from '../core/services/electron/electron.service'; styleUrls: ['./home.component.scss'] }) export class HomeComponent implements OnInit { + defaultPath: string | null = null; + defaultPathText: string = ''; + + constructor(private electronService: ElectronService, private translate: TranslateService) {} + + ngOnInit(): void { + console.log('HomeComponent INIT'); + this.loadDefaultPath(); + } + + loadDefaultPath(): void { + if (this.electronService.isElectron) { + this.electronService.ipcRenderer.invoke('file-operations', { + operation: 'load-default-path' + }).then((path: string | null) => { + console.log('Loaded default path:', path); + this.defaultPath = path; + if (path) { + this.defaultPathText = this.formatPath(path); + this.defaultPath = path; + } else { + this.defaultPathText = ''; + this.defaultPath = null; + } + }).catch(error => { + console.error('Error loading default path:', error); + this.defaultPathText = this.translate.instant('PAGES.HOME.CAN_NOT_GET_DEFAULT_PATH'); + }); + } + } + + private formatPath(path: string, maxLength = 30): string { + if (path.length <= maxLength) return path; + + const parts = path.split(/[\\/]/); + if (parts.length <= 2) return path; + + const firstPart = parts[0]; + const lastPart = parts[parts.length - 1]; + const middleLength = maxLength - (firstPart.length + lastPart.length + 5); + + if (middleLength > 0) { + return `${firstPart}/.../${lastPart}`; + } + return `${firstPart}/...${lastPart}`; + } + async selectDefaultFolder(event: Event): Promise { + event.stopPropagation(); + try { + if (this.electronService.isElectron) { + console.log('try select folder'); + const result = await this.electronService.ipcRenderer.invoke('file-operations', { + operation: 'select-folder', + payload: this.defaultPath + }); + if (result && result.length > 0) { + this.defaultPath = result[0]; + this.defaultPathText = this.formatPath(result[0]); + await this.electronService.ipcRenderer.invoke('file-operations', { + operation: 'save-default-path', + payload: result[0] + }); + console.log('selected folder:', result[0]); + } + } + } catch (error) { + console.error('select folder fail:', error); + } + } Images_ROC_Shown: boolean = false; Images_TFT_Shown: boolean = false; @@ -29,10 +99,6 @@ export class HomeComponent implements OnInit { forcelang: boolean = false; installEvent: string = 'install' - ngOnInit(): void { - console.log('HomeComponent INIT'); - } - @HostListener('mouseenter', ['$event', '$event.target.dataset.action']) onMouseEnter(event: MouseEvent, action: string) { if (this.isInteractive) { @@ -56,7 +122,6 @@ export class HomeComponent implements OnInit { }; } - @HostListener('mouseout', ['$event', '$event.target.dataset.action']) onMouseLeave(event: MouseEvent, action: string) { if (this.isInteractive) { @@ -79,8 +144,6 @@ export class HomeComponent implements OnInit { } }; } - - @HostListener('click', ['$event', '$event.target.dataset.action']) onClick(event: MouseEvent, action: string) { @@ -89,6 +152,9 @@ export class HomeComponent implements OnInit { case 'Roc': if (!this.ROCInstall) { this.message = `install${this.modeState}${this.bjState}-ROC`; + this.Images_ROC_Shown = true; + this.Images_TFT_Shown = false; + this.Images_REF_Shown = false; this.TFTInstall = false; this.REFInstall = false; this.ROCInstall = !this.ROCInstall; @@ -99,6 +165,9 @@ export class HomeComponent implements OnInit { case 'Tft': if (!this.TFTInstall) { this.message = `install${this.modeState}${this.bjState}-TFT`; + this.Images_ROC_Shown = false; + this.Images_TFT_Shown = true; + this.Images_REF_Shown = false; this.ROCInstall = false; this.REFInstall = false; this.TFTInstall = !this.TFTInstall; @@ -109,6 +178,9 @@ export class HomeComponent implements OnInit { case 'Ref': if (!this.REFInstall) { this.message = `install${this.modeState}${this.bjState}`; + this.Images_ROC_Shown = false; + this.Images_TFT_Shown = false; + this.Images_REF_Shown = true; this.ROCInstall = false; this.TFTInstall = false; this.REFInstall = !this.REFInstall; @@ -135,20 +207,20 @@ export class HomeComponent implements OnInit { console.log('mode',this.modeState,this.Mode_State); break; case 'BJoptionOn': - this.bjState = ''; - this.BJ_State = 1; - console.log('BJ',this.bjState); - break; + this.bjState = ''; + this.BJ_State = 1; + console.log('BJ',this.bjState); + break; case 'BJoptionVsAI': - this.bjState = '-vai'; - this.BJ_State = 2; - console.log('BJ',this.bjState); - break; + this.bjState = '-vai'; + this.BJ_State = 2; + console.log('BJ',this.bjState); + break; case 'BJoptionOff': - this.bjState = '-noc'; - this.BJ_State = 0; - console.log('BJ',this.bjState); - break; + this.bjState = '-noc'; + this.BJ_State = 0; + console.log('BJ',this.bjState); + break; case 'Optimise': this.optimize = !this.optimize; if (this.optimize) { @@ -160,13 +232,8 @@ export class HomeComponent implements OnInit { if (this.forcelang) { this.optimize = false; } - break; + break; } }; } - - constructor( - private router: Router, - private electronService: ElectronService, - ) { } -} +} \ No newline at end of file diff --git a/Electron/src/assets/i18n/de.json b/Electron/src/assets/i18n/de.json index 173d3d31f..d34a6400c 100644 --- a/Electron/src/assets/i18n/de.json +++ b/Electron/src/assets/i18n/de.json @@ -10,7 +10,9 @@ "INCLUDE_COMMANDER_VSAI": "Gegen Vanilla KI", "INCLUDE_COMMANDER_OFF": "Kein Kommandant", "OPTIMISE": "Optimierte Skripte verwenden", - "FORCELANG": "AI Chat Sprache überschreiben" + "FORCELANG": "AI Chat Sprache überschreiben", + "CAN_NOT_GET_DEFAULT_PATH":"Kann den Standardspeicherort nicht abrufen", + "DEFAULT_PATH":"Standardpfad: " }, "APP": { "INSTALLING": "Installation in {{path}}", diff --git a/Electron/src/assets/i18n/en.json b/Electron/src/assets/i18n/en.json index 2a176ceb0..c2b4ff105 100644 --- a/Electron/src/assets/i18n/en.json +++ b/Electron/src/assets/i18n/en.json @@ -10,7 +10,9 @@ "INCLUDE_COMMANDER_VSAI": "Vs Vanilla AI", "INCLUDE_COMMANDER_OFF": "No Commander", "OPTIMISE": "Use Optimised Scripts", - "FORCELANG": "Override AI Chat Language" + "FORCELANG": "Override AI Chat Language", + "CAN_NOT_GET_DEFAULT_PATH":"Can not get default path", + "DEFAULT_PATH":"Default path: " }, "APP": { "INSTALLING": "Installing into {{path}}", diff --git a/Electron/src/assets/i18n/es.json b/Electron/src/assets/i18n/es.json index ffdf3bdc8..5948f236e 100644 --- a/Electron/src/assets/i18n/es.json +++ b/Electron/src/assets/i18n/es.json @@ -10,7 +10,9 @@ "INCLUDE_COMMANDER_VSAI": "Contra IA Vanilla", "INCLUDE_COMMANDER_OFF": "Sin Comandante", "OPTIMISE": "Usar scripts optimizados", - "FORCELANG": "Cubrir el lenguaje de chat de Ia" + "FORCELANG": "Cubrir el lenguaje de chat de Ia", + "CAN_NOT_GET_DEFAULT_PATH":"No se puede obtener la ruta predeterminada", + "DEFAULT_PATH":"Ruta predeterminada: " }, "APP": { "INSTALLING": "Instalando en {{path}}", diff --git a/Electron/src/assets/i18n/fr.json b/Electron/src/assets/i18n/fr.json index 341b472f4..309bd5b57 100644 --- a/Electron/src/assets/i18n/fr.json +++ b/Electron/src/assets/i18n/fr.json @@ -10,7 +10,9 @@ "INCLUDE_COMMANDER_VSAI": "Contre IA Vanilla", "INCLUDE_COMMANDER_OFF": "Pas de Commandant", "OPTIMISE": "Utiliser des scripts optimisés", - "FORCELANG": "Couvrir le langage de chat ai" + "FORCELANG": "Couvrir le langage de chat ai", + "CAN_NOT_GET_DEFAULT_PATH":"Ne peut pas obtenir le chemin par défaut", + "DEFAULT_PATH":"Chemin par défaut: " }, "APP": { "INSTALLING": "Installation dans {{path}}", diff --git a/Electron/src/assets/i18n/no.json b/Electron/src/assets/i18n/no.json index 370a2f190..e83d9c71a 100644 --- a/Electron/src/assets/i18n/no.json +++ b/Electron/src/assets/i18n/no.json @@ -10,7 +10,9 @@ "INCLUDE_COMMANDER_VSAI": "Mot Vanilla AI", "INCLUDE_COMMANDER_OFF": "Ingen Kommandant", "OPTIMISE": "Bruk optimaliserte skript", - "FORCELANG": "Overskriv AI-samtalespråk" + "FORCELANG": "Overskriv AI-samtalespråk", + "CAN_NOT_GET_DEFAULT_PATH":"Kan ikke få standardstien", + "DEFAULT_PATH":"Standardstiendeveis: " }, "APP": { "INSTALLING": "Installerer i {{path}}", diff --git a/Electron/src/assets/i18n/pt.json b/Electron/src/assets/i18n/pt.json index acdbf0c2a..a5e85cfea 100644 --- a/Electron/src/assets/i18n/pt.json +++ b/Electron/src/assets/i18n/pt.json @@ -10,7 +10,9 @@ "INCLUDE_COMMANDER_VSAI": "Contra IA Vanilla", "INCLUDE_COMMANDER_OFF": "Sem Comandante", "OPTIMISE": "Usar scripts otimizados", - "FORCELANG": "Substituir a linguagem de conversação AI" + "FORCELANG": "Substituir a linguagem de conversação AI", + "CAN_NOT_GET_DEFAULT_PATH":"Can not get caminho padrão", + "DEFAULT_PATH":"caminho padrão: " }, "APP": { "INSTALLING": "Instalando em {{path}}", diff --git a/Electron/src/assets/i18n/ro.json b/Electron/src/assets/i18n/ro.json index 0330126b4..272ca4dc2 100644 --- a/Electron/src/assets/i18n/ro.json +++ b/Electron/src/assets/i18n/ro.json @@ -10,7 +10,9 @@ "INCLUDE_COMMANDER_VSAI": "Împotriva AI Vanilla", "INCLUDE_COMMANDER_OFF": "Fără comandant", "OPTIMISE": "Utilizați scripturi optimizate", - "FORCELANG": "Suprascrie limbajul de chat AI" + "FORCELANG": "Suprascrie limbajul de chat AI", + "CAN_NOT_GET_DEFAULT_PATH":"Nu se poate obține calea implicită", + "DEFAULT_PATH":"Căi implicită: " }, "APP": { "INSTALLING": "Instalare în curs la {{path}}", diff --git a/Electron/src/assets/i18n/ru.json b/Electron/src/assets/i18n/ru.json index 86b1c6cc3..e4fe6a06f 100644 --- a/Electron/src/assets/i18n/ru.json +++ b/Electron/src/assets/i18n/ru.json @@ -10,7 +10,9 @@ "INCLUDE_COMMANDER_VSAI": "Против Vanilla ИИ", "INCLUDE_COMMANDER_OFF": "Без командира", "OPTIMISE": "Использовать оптимизированные скрипты", - "FORCELANG": "Скачать язык разговора" + "FORCELANG": "Скачать язык разговора", + "CAN_NOT_GET_DEFAULT_PATH":"Не удается получить путь по умолчанию", + "DEFAULT_PATH":"Nути по умолчанию: " }, "APP": { "INSTALLING": "Установка в {{path}}", diff --git a/Electron/src/assets/i18n/sv.json b/Electron/src/assets/i18n/sv.json index 084909b1e..843a4e772 100644 --- a/Electron/src/assets/i18n/sv.json +++ b/Electron/src/assets/i18n/sv.json @@ -10,7 +10,9 @@ "INCLUDE_COMMANDER_VSAI": "Mot Vanilla AI", "INCLUDE_COMMANDER_OFF": "Ingen befälhavare", "OPTIMISE": "Använd optimerade skript", - "FORCELANG": "Skriv över AI-chattspråk" + "FORCELANG": "Skriv över AI-chattspråk", + "CAN_NOT_GET_DEFAULT_PATH":"Kan inte få standardvägen", + "DEFAULT_PATH":"Standardväg: " }, "APP": { "INSTALLING": "Installerar i {{path}}", diff --git a/Electron/src/assets/i18n/zh.json b/Electron/src/assets/i18n/zh.json index 6107cf9fd..ee38b9edb 100644 --- a/Electron/src/assets/i18n/zh.json +++ b/Electron/src/assets/i18n/zh.json @@ -7,10 +7,12 @@ "INSTALL_ON_FOLDER": "按文件夹安装", "INSTALL_ON_MAP": "按地图安装", "INCLUDE_COMMANDER": "安装控制台", - "INCLUDE_COMMANDER_VSAI": "AMAI VS 暴雪AI", + "INCLUDE_COMMANDER_VSAI": "AMAI VS 暴雪AI 控制台", "INCLUDE_COMMANDER_OFF": "不安装控制台", "OPTIMISE": "使用优化脚本", - "FORCELANG": "覆盖AI聊天语言" + "FORCELANG": "覆盖AI聊天语言", + "CAN_NOT_GET_DEFAULT_PATH":"找不到默认路径", + "DEFAULT_PATH":"默认路径:" }, "APP": { "INSTALLING": "正在安装 {{path}}",