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 @@

-
1.33+
-
Optimal:2.0.4
+
2.0.3+
+
{{ 'PAGES.HOME.REF_BEST_GAME_VERSION' | translate }}

-
1.24+
-
Optimal:1.24-1.28
+
1.24e+
+
{{ 'PAGES.HOME.TFT_BEST_GAME_VERSION' | translate }}

-
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}}",