diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 0000000..eddd889
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,22 @@
+module.exports = {
+ "env": {
+ "browser": true,
+ "es2021": true
+ },
+ "extends": "eslint:recommended",
+ "parserOptions": {
+ "ecmaVersion": 13,
+ "sourceType": "module"
+ },
+ "globals": {
+ "__dirname": true,
+ "converse": true,
+ "exports": true,
+ "module": true,
+ "process": true,
+ "require": true,
+ },
+ "rules": {
+ "prefer-const": "error",
+ }
+};
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c5e75dc
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+node_modules
+.DS_*
+dist/
+.idea/
diff --git a/.nvmrc b/.nvmrc
new file mode 100644
index 0000000..3d0176d
--- /dev/null
+++ b/.nvmrc
@@ -0,0 +1 @@
+v16.13.1
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..dc644bd
--- /dev/null
+++ b/index.html
@@ -0,0 +1,23 @@
+
+
+
+
+ RS Desktop
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/main.js b/main.js
new file mode 100644
index 0000000..bbd2b80
--- /dev/null
+++ b/main.js
@@ -0,0 +1,161 @@
+import { app, BrowserWindow, ipcMain, shell, powerMonitor } from 'electron';
+import path from 'path';
+import { fileURLToPath } from 'url';
+import keytar from 'keytar';
+
+import trayService from './modules/tray-service.js';
+import menuService from './modules/menu-service.js';
+import settingsService from './modules/settings-service.js';
+import setFindBar from "find-bar" // search bar
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+
+export let mainWindow;
+let zoom = 1;
+
+const isMac = process.platform === 'darwin';
+const isWin = process.platform === 'win32';
+
+function initApp() {
+ if (!app.requestSingleInstanceLock()) {
+ app.quit();
+ }
+
+ createWindow();
+
+ if (isWin) {
+ app.setAppUserModelId("com.rscommunity.rs-desktop");
+ }
+}
+
+function createWindow() {
+ const getSavedWindowBounds = () => {
+ const winBounds = settingsService.get('winBounds') || { width: 800, height: 600, x: undefined, y: undefined }; winBounds.width = Math.max(winBounds.width, 200);
+ winBounds.height = Math.max(winBounds.height, 200);
+ return winBounds;
+ };
+
+ const mainWindowOptions = {
+ zoomToPageWidth: true,
+ show: false,
+ autoHideMenuBar: settingsService.get('hideMenubar') || false,
+ webPreferences: {
+ nodeIntegration: true,
+ preload: path.join(__dirname, 'preload.cjs'), // this is a commonjs file, because electron doesnt support natively loading esm in preload files, which is retarded, but i cant change that.. so we will just cope :p
+ },
+ icon: path.join(__dirname, 'resources', 'images', 'logo-48x48.png'),
+ ...getSavedWindowBounds(),
+ };
+
+ mainWindow = new BrowserWindow(mainWindowOptions);
+
+ trayService.initTray(mainWindow);
+ menuService.createMenu(mainWindow);
+
+ mainWindow.on('close', (e) => {
+ settingsService.set('isMaximized', mainWindow.isMaximized());
+ if (mainWindow.isFullScreen()) {
+ mainWindow.setFullScreen(false);
+ }
+ if (!app.isQuitting && settingsService.get('minimizeOnClose')) {
+ e.preventDefault();
+ mainWindow.hide();
+ }
+ return false;
+ });
+
+ mainWindow.on('resized', () => {
+ settingsService.set('winBounds', mainWindow.getBounds());
+ });
+ mainWindow.on('moved', () => {
+ settingsService.set('winBounds', mainWindow.getBounds());
+ });
+
+ if (isMac) {
+ powerMonitor.on('shutdown', () => {
+ app.isQuitting = true;
+ app.quit();
+ });
+ }
+
+ ipcMain.on('app-quit', () => {
+ app.isQuitting = true;
+ app.quit();
+ });
+
+ ipcMain.on('increaseZoom', () => { // custom zoom for the client
+ if (zoom >= 2) return;
+ zoom += 0.1;
+ mainWindow.webContents.setZoomFactor(zoom);
+ });
+
+ ipcMain.on('decreaseZoom', () => {
+ if (zoom <= 0.5) return;
+ zoom -= 0.1;
+ mainWindow.webContents.setZoomFactor(zoom);
+ });
+
+ mainWindow.webContents.setZoomFactor(1);
+
+ mainWindow.on('closed', () => {
+ mainWindow = null;
+ });
+
+ mainWindow.webContents.setWindowOpenHandler((details) => {
+ shell.openExternal(details.url).catch(console.log);
+ return { action: 'deny' };
+ });
+
+ settingsService.webContents = mainWindow.webContents;
+
+ ipcMain.handle('settings', (e, method, ...args) => {
+ return settingsService[method].apply(settingsService, args);
+ });
+
+ ipcMain.handle('trayService', (e, method, ...args) => {
+ return trayService[method].apply(trayService, args);
+ });
+
+ ipcMain.handle('keytar', (e, method, ...args) => {
+ return keytar[method].apply(keytar, args);
+ });
+
+ mainWindow.on('ready-to-show', () => {
+ if (settingsService.get('isMaximized')) {
+ mainWindow.maximize();
+ } else {
+ mainWindow.show();
+ }
+ });
+
+ mainWindow.loadFile('index.html').catch((err) => {
+ console.log(err);
+ app.isQuitting = true;
+ app.quit();
+ });
+}
+
+app.on('ready', initApp);
+
+app.on('browser-window-created', (_, win) => {
+ setFindBar(win); // search bar
+ });
+
+app.on('window-all-closed', () => {
+ app.quit();
+});
+
+app.on('activate', () => {
+ if (mainWindow === null) {
+ createWindow();
+ } else {
+ mainWindow.show();
+ }
+});
+
+app.on('second-instance', () => {
+ mainWindow.show();
+});
+
+app.commandLine.appendSwitch('autoplay-policy', 'no-user-gesture-required');