Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
5e5cbda
Update install.js
jzy-chitong56 Feb 11, 2026
ddb2390
Set minimum window size and enhance message handling
jzy-chitong56 Feb 11, 2026
e10f2f1
Add installation progress tracking to app component
jzy-chitong56 Feb 11, 2026
a9663c7
Update install.js
jzy-chitong56 Feb 14, 2026
5a5cad7
Add files via upload
jzy-chitong56 Feb 21, 2026
5961992
Update main.ts
jzy-chitong56 Feb 21, 2026
f93b9bb
Add files via upload
jzy-chitong56 Feb 21, 2026
de8800b
Update main.ts
jzy-chitong56 Mar 1, 2026
b72d157
Update main.ts
jzy-chitong56 Mar 1, 2026
81f3d07
Update home.component.html
jzy-chitong56 Mar 1, 2026
dcb43c1
Update home.component.scss
jzy-chitong56 Mar 1, 2026
d20ea6a
Update home.component.ts
jzy-chitong56 Mar 1, 2026
12d310c
Update app.component.ts
jzy-chitong56 Mar 1, 2026
02c7431
Update home.component.scss
jzy-chitong56 Mar 1, 2026
87e7aba
Update home.component.html
jzy-chitong56 Mar 1, 2026
95fbf23
Update home.component.scss
jzy-chitong56 Mar 1, 2026
f4ff9e9
Update package-lock.json
jzy-chitong56 Mar 1, 2026
f4e55ae
Update home.component.html
jzy-chitong56 Mar 1, 2026
8cc4e42
Update home.component.html
jzy-chitong56 Mar 1, 2026
d60e896
Update de.json
jzy-chitong56 Mar 1, 2026
973bcd0
Update en.json
jzy-chitong56 Mar 1, 2026
d10a7b5
Update es.json
jzy-chitong56 Mar 1, 2026
a863b2a
Update fr.json
jzy-chitong56 Mar 1, 2026
c1020be
Update no.json
jzy-chitong56 Mar 1, 2026
c9363d8
Update pt.json
jzy-chitong56 Mar 1, 2026
13b170d
Update ro.json
jzy-chitong56 Mar 1, 2026
db3aa90
Update ru.json
jzy-chitong56 Mar 1, 2026
772f992
Update sv.json
jzy-chitong56 Mar 1, 2026
ab2a989
Update zh.json
jzy-chitong56 Mar 1, 2026
76cc7ca
Update package.json
jzy-chitong56 Mar 1, 2026
8fcff9a
Update home.component.scss
jzy-chitong56 Mar 1, 2026
8c17412
Update home.component.html
jzy-chitong56 Mar 1, 2026
8028d8c
Update home.component.scss
jzy-chitong56 Mar 1, 2026
c961b03
Update home.component.scss
jzy-chitong56 Mar 1, 2026
4296123
Update home.component.scss
jzy-chitong56 Mar 1, 2026
507a991
Update home.component.scss
jzy-chitong56 Mar 1, 2026
dacda73
Add files via upload
jzy-chitong56 Mar 3, 2026
3722960
Add files via upload
jzy-chitong56 Mar 3, 2026
5330ed4
Add files via upload
jzy-chitong56 Mar 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 22 additions & 18 deletions Electron/AMAI-release/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -71,8 +70,6 @@ const installOnDirectory = async () => {
return
}



if (language !== '-') {
setLanguage(commonAIPath, language);
if (installCommander) {
Expand All @@ -85,15 +82,25 @@ 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)}`);

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}`);
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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` }
);
Expand All @@ -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(
Expand Down Expand Up @@ -239,7 +244,6 @@ const installOnDirectory = async () => {
}
}


function setLanguage(file, language) {
let data = fs.readFileSync(file, 'utf8');
const searchFor = /string language = "([^"]*)"/;
Expand Down
173 changes: 149 additions & 24 deletions Electron/app/main.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
import {app, BrowserWindow, dialog, Menu, screen } from 'electron';
import { app, BrowserWindow, dialog, Menu, screen } from 'electron';
import * as path from 'path';
import * as fs from 'fs';
import * as remote from '@electron/remote/main';
import { InstallModel } from '../commons/models';
const ipcMain = require('electron').ipcMain;
const cp = require('child_process');

type Settings = {
TFT_PATH?: string | null;
ROC_PATH?: string | null;
REFORGED_PATH?: string | null;
[key: string]: any;
};

let win: BrowserWindow = null;
let translations : { [key: string]: string } = {};
let translations: { [key: string]: string } = {};
let currentLanguage: string = "English";
const documentsPath = app.getPath('documents');
const args = process.argv.slice(1),
serve = args.some(val => val === '--serve');

Expand All @@ -23,15 +31,15 @@ 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,
Expand Down Expand Up @@ -74,18 +82,59 @@ const createWindow = (): BrowserWindow => {
return win;
}

const execInstall = async (signal, commander: number = 1, isMap: boolean = false, ver: string = "REFORGED", forceLang: boolean) => {
const getversionpath = (pathver: string, settings: Settings): string => {
win.webContents.send('on-install-console', `get version path : ${JSON.stringify(settings)}, settings path : ${settings[`${pathver}_PATH`]}`);
if (pathver == "REFORGED") {
return settings.REFORGED_PATH || '';
} else if (pathver == "TFT") {
return settings.TFT_PATH || '';
} else if (pathver == "ROC") {
return settings.ROC_PATH || '';
}
return '';
}

const execInstall = async (signal, commander: number = 1, isMap: boolean = false, ver: string = "REFORGED", forceLang: boolean, pathver: string = "REFORGED") => {
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 ? [
{ name: translations["PAGES.ELECTRON.MAPFILE"] || '', extensions: ['w3x', 'w3m'] },
] : null,
});
let response;
const settingsPath = path.join(app.getPath('userData'), 'settings.json');
let settings: Settings = {};
let usepath = null;
if (fs.existsSync(settingsPath)) {
settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
usepath = getversionpath(pathver, settings);
win.webContents.send('on-install-console', `${pathver} default path : ${usepath}`);
}
if (usepath !== null && usepath !== undefined && usepath !== '') {
if (isMap) {
response = dialog.showOpenDialogSync(win, {
title: translations["PAGES.ELECTRON.OPEN_MAP"] || '',
properties: ['openFile'] ,
filters: [
{ name: translations["PAGES.ELECTRON.MAPFILE"] || '', extensions: ['w3x', 'w3m'] },
],
defaultPath: usepath,
});
if (response && (response?.length > 0)) {
usepath = null; // wait updata path , maybe selected other path
}
} else {
response = [usepath];
}
} else {
win.webContents.send('on-install-console', 'Choose path');
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 ? [
{ name: translations["PAGES.ELECTRON.MAPFILE"] || '', extensions: ['w3x', 'w3m'] },
] : null,
defaultPath: documentsPath,
});
}

let child;

Expand All @@ -95,7 +144,7 @@ const execInstall = async (signal, commander: number = 1, isMap: boolean = false
let currentExecDir = `./AMAI-release/`,
currentScriptDir = './AMAI-release/';

if(!isDev()) {
if (!isDev()) {
currentExecDir = `./AMAI/`;
currentScriptDir = path.join(
__dirname,
Expand All @@ -116,11 +165,23 @@ const execInstall = async (signal, commander: number = 1, isMap: boolean = false
// win.webContents.send('on-install-message', 'currentExecDir: ' + currentExecDir);
// win.webContents.send('on-install-message', `install js path: ../${currentExecDir}install.js`);

if(!response || (response?.length === 0)) {
if (!response || (response?.length === 0)) {
win.webContents.send('on-install-empty');
return;
}

if (usepath === null) {
if (!isMap) {
usepath = response[0];
} else {
usepath = path.dirname(response[0]);
}
const finalPath = usepath ? path.resolve(usepath) : null;
settings[`${pathver}_PATH`] = finalPath;
fs.writeFileSync(settingsPath, JSON.stringify(settings));
win.webContents.send('on-install-console', `Default path updated to: ${finalPath}`);
win.webContents.send('path-updated', { pathver: pathver, path: finalPath });
}
// open modal on front
win.webContents.send('on-install-init', <InstallModel>{
response: response[0],
Expand All @@ -132,14 +193,13 @@ const execInstall = async (signal, commander: number = 1, isMap: boolean = false
// MPQEditor and AddToMPQ only work when files and folders are in same directory
try {
process.chdir(currentScriptDir);
} catch(err) {
} catch (err) {
console.log('error:', err.message);

/** uncomment to debbug */
// win.webContents.send('on-install-message', 'Error: ' + err.message);
}


// init install proccess
try {
child = cp.fork(
Expand All @@ -156,26 +216,89 @@ 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
child.on('exit', () => {
win.webContents.send('on-install-exit');
});
} catch(err) {
} catch (err) {
win.webContents.send('on-install-error', err.message);
}
}

const GetDefaultPath = () => {
ipcMain?.handle('load-path', async (_) => {
const settingsPath = path.join(app.getPath('userData'), 'settings.json');
let settings: Settings = {};
if (fs.existsSync(settingsPath)) {
settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
win.webContents.send('on-install-console',`Loaded paths : REFORGED : ${settings.REFORGED_PATH} , TFT : ${settings.TFT_PATH} , ROC : ${settings.ROC_PATH}`);
return { REFORGED_PATH: settings.REFORGED_PATH || null, TFT_PATH: settings.TFT_PATH || null, ROC_PATH: settings.ROC_PATH || null };
}
win.webContents.send('on-install-console', `Loading path file failed , using defaults`);
return { REFORGED_PATH: null, TFT_PATH: null, ROC_PATH: null };
});
}

const SetDefaultPath = () => {
ipcMain?.on('set-path-and-install', async (_event, toFolder: boolean, commander: number, optimize: boolean, forceLang: boolean, pathver: string = "REFORGED") => {
const settingsPath = path.join(app.getPath('userData'), 'settings.json');
let settings: Settings = {};
let usepath = documentsPath;
let result;
let signal = {};
win.webContents.send('on-install-console', `Selecting path and install , version : ${pathver}`);
if (toFolder) {
if (fs.existsSync(settingsPath)) {
win.webContents.send('on-install-console', `Get default path`);
settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
usepath = getversionpath(pathver, settings);
if (!usepath || usepath === '' || usepath === null || !fs.existsSync(usepath)) {
usepath = documentsPath;
}
}
result = dialog.showOpenDialogSync(win, {
title: translations["PAGES.ELECTRON.OPEN_DIR"] || '',
properties: ['openDirectory'],
defaultPath: usepath
});
if (result && (result?.length > 0)) {
usepath = result[0] ? path.resolve(result[0]) : documentsPath;
settings[`${pathver}_PATH`] = usepath;
win.webContents.send('on-install-console', `Set path : ${usepath}`);
try {
fs.writeFileSync(settingsPath, JSON.stringify(settings));
win.webContents.send('path-updated', { pathver: pathver, path: usepath });
execInstall(signal, commander, !toFolder, optimize ? `OPT${pathver}` : pathver, forceLang, pathver);
} catch (err) {
win.webContents.send('on-install-console', `Set path failed: ${err.message}`);
}
} else {
win.webContents.send('on-install-console', `Path selection was cancelled`);
}
} else {
execInstall(signal, commander, !toFolder, optimize ? `OPT${pathver}` : pathver, forceLang, pathver);
}
});
}

const installProcess = () => {
let signal = {};

ipcMain?.on('install', async (_event, ver: string, toFolder: boolean, commander: number, optimize: boolean, forceLang : boolean) => {
execInstall(signal, commander, !toFolder, optimize ? `OPT${ver}` : ver, forceLang);
ipcMain?.on('install', async (_event, ver: string, toFolder: boolean, commander: number, optimize: boolean, forceLang: boolean) => {
const pathver = ver;
execInstall(signal, commander, !toFolder, optimize ? `OPT${ver}` : ver, forceLang, pathver);
});

// TODO: stop process with signal
Expand Down Expand Up @@ -268,3 +391,5 @@ const installTrans = () => {
init();
installTrans();
installProcess();
GetDefaultPath();
SetDefaultPath();
Loading