diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a7a5d313..ae086330 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -48,11 +48,11 @@ jobs: run: | echo "SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}" >> .env - echo "MAIN_VITE_SENTRY_DSN=${{ secrets.MAIN_VITE_SENTRY_DSN }}" >> .env - echo "MAIN_VITE_SENTRY_CRASH_REPORT_DSN=${{ secrets.MAIN_VITE_SENTRY_CRASH_REPORT_DSN }}" >> .env + echo "MAIN_VITE_SENTRY_DSN=${{ secrets.SENTRY_DSN }}" >> .env + echo "MAIN_VITE_SENTRY_CRASH_REPORT_DSN=${{ secrets.SENTRY_CRASH_REPORT_DSN }}" >> .env - echo "RENDERER_VITE_SENTRY_DSN=${{ secrets.RENDERER_VITE_SENTRY_DSN }}" >> .env - echo "RENDERER_VITE_SENTRY_CRASH_REPORT_DSN=${{ secrets.RENDERER_VITE_SENTRY_CRASH_REPORT_DSN }}" >> .env + echo "RENDERER_VITE_SENTRY_DSN=${{ secrets.SENTRY_DSN }}" >> .env + echo "RENDERER_VITE_SENTRY_CRASH_REPORT_DSN=${{ secrets.SENTRY_CRASH_REPORT_DSN }}" >> .env - name: Install dependencies run: yarn install diff --git a/electron.vite.config.ts b/electron.vite.config.ts index 6957ce45..0aa1fa12 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -1,3 +1,4 @@ +import 'dotenv/config'; import { execSync } from 'child_process'; import { sentryVitePlugin } from '@sentry/vite-plugin'; import react from '@vitejs/plugin-react'; @@ -13,6 +14,7 @@ const sentryPlugin = sentryVitePlugin({ org: 'dragonrealms-phoenix', project: 'phoenix', telemetry: false, + authToken: process.env.SENTRY_AUTH_TOKEN, disable: process.env.VITE_PLUGIN_SENTRY_ENABLE !== 'true', }) as PluginOption; diff --git a/package.json b/package.json index 30734643..f3ccf98c 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "lint:fix": "yarn lint --fix", "lint:staged": "lint-staged --concurrent 1", "format": "yarn prettier:fix && yarn lint:fix", - "typecheck:preload": "tsc --declaration --emitDeclarationOnly --outDir ./src/preload/ ./src/preload/api.ts && yarn prettier:fix ./src/preload/api.d.ts", + "typecheck:preload": "tsc --declaration --emitDeclarationOnly --outDir ./src/preload/ ./src/preload/index.ts && yarn prettier:fix ./src/preload/index.d.ts", "typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false", "typecheck:web": "tsc --noEmit -p tsconfig.web.json --composite false", "typecheck": "yarn typecheck:preload && yarn typecheck:node && yarn typecheck:web", @@ -115,6 +115,7 @@ "@typescript-eslint/parser": "^6.7.3", "@vitejs/plugin-react": "^4.1.0", "cross-env": "^7.0.3", + "dotenv": "^16.3.1", "electron": "^26.2.4", "electron-builder": "^24.6.4", "electron-vite": "^1.0.28", diff --git a/src/main/index.ts b/src/main/index.ts index 007615d1..653edec3 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,8 +1,8 @@ -import { BrowserWindow, app, ipcMain, shell } from 'electron'; +import { BrowserWindow, Event, app, ipcMain, shell } from 'electron'; import { join } from 'path'; import { is, optimizer, platform } from '@electron-toolkit/utils'; import { createLogger } from './logger'; -import { MenuBuilder } from './menu'; +import { initializeMenu } from './menu'; import { initializeSentry } from './sentry'; initializeSentry(); @@ -11,13 +11,6 @@ const logger = createLogger('main'); app.setName('Phoenix'); app.setAppUserModelId('com.github.dragonrealms-phoenix.phoenix'); -app.setAboutPanelOptions({ - applicationName: app.name, - applicationVersion: app.getVersion(), - version: `${app.getVersion()}-${import.meta.env.MAIN_VITE_GIT_SHORT_HASH}`, - authors: ['Katoak'], - website: 'https://github.com/dragonrealms-phoenix/phoenix', -}); function createWindow(): void { logger.info('creating main window'); @@ -25,13 +18,26 @@ function createWindow(): void { const mainWindow = new BrowserWindow({ width: 900, height: 670, - show: false, + show: false, // to avoid a blank window until contents loaded autoHideMenuBar: true, webPreferences: { preload: join(__dirname, '../preload/index.js'), - sandbox: false, - nodeIntegration: false, + /** + * Security Best Practices + * https://www.electronjs.org/docs/latest/tutorial/security + * https://github.com/moloch--/reasonably-secure-electron + */ + allowRunningInsecureContent: false, contextIsolation: true, + experimentalFeatures: false, + navigateOnDragDrop: false, + nodeIntegration: false, + nodeIntegrationInSubFrames: false, + nodeIntegrationInWorker: false, + safeDialogs: true, + sandbox: true, + webSecurity: true, + webviewTag: false, }, }); @@ -39,11 +45,6 @@ function createWindow(): void { mainWindow.show(); }); - mainWindow.webContents.setWindowOpenHandler((details) => { - shell.openExternal(details.url); - return { action: 'deny' }; - }); - // HMR for renderer base on electron-vite cli. // Load the remote URL for development or the local html file for production. if (is.dev && process.env['ELECTRON_RENDERER_URL']) { @@ -52,14 +53,14 @@ function createWindow(): void { mainWindow.loadFile(join(__dirname, '../renderer/index.html')); } - new MenuBuilder(mainWindow).buildMenu(); + initializeMenu(mainWindow); } // This method will be called when Electron has finished // initialization and is ready to create browser windows. // Some APIs can only be used after this event occurs. -app.whenReady().then((): void => { - createWindow(); +app.once('ready', () => { + app.setAsDefaultProtocolClient('app'); // Default open or close DevTools by F12 in development // and ignore CommandOrControl + R in production. @@ -76,11 +77,54 @@ app.whenReady().then((): void => { } }); + // Disable or limit creation of new windows to protect app and users. + // https://www.electronjs.org/docs/latest/tutorial/security + app.on('web-contents-created', (_, contents) => { + const allowedDomains = [ + /^(www.)?github\.com$/i, + /^(www.)?play\.net$/i, + /^elanthipedia\.play\.net$/i, + ]; + + const isAllowedDomain = (domain: string): boolean => { + return allowedDomains.some((d) => d.test(domain)); + }; + + const blockOrOpenURL = ( + event: Event, + url: string + ): void => { + const domain = new URL(url).hostname; + // If the domain is allowed, open it in the user's default browser. + if (isAllowedDomain(domain)) { + logger.info('opening url in default browser', { url }); + setImmediate(() => { + shell.openExternal(url); + }); + } else { + logger.warn('blocked window navigation', { url }); + } + event.preventDefault(); + }; + + contents.on('will-navigate', (event, url) => { + logger.info('will-navigate', { url }); + blockOrOpenURL(event, url); + }); + + contents.on('will-redirect', (event, url) => { + logger.info('will-redirect', { url }); + blockOrOpenURL(event, url); + }); + }); + // Listen for events emitted by the preload api ipcMain.handle('ping', async (): Promise => { // Return response to renderer return 'pong'; }); + + createWindow(); }); // Quit when all windows are closed, except on macOS. diff --git a/src/main/menu/index.ts b/src/main/menu/index.ts index 46ad7977..e5aef8a7 100644 --- a/src/main/menu/index.ts +++ b/src/main/menu/index.ts @@ -5,6 +5,7 @@ import { app, shell, } from 'electron'; +import { platform } from '@electron-toolkit/utils'; /** * Inspired by RedisInsight @@ -18,342 +19,373 @@ interface DarwinMenuItemConstructorOptions extends MenuItemConstructorOptions { export const STEP_ZOOM_FACTOR = 0.2; -export class MenuBuilder { - // Used when changing the zoom factor. - public mainWindow: BrowserWindow; +export function initializeMenu(window: BrowserWindow): void { + app.setAboutPanelOptions({ + version: `${app.getVersion()}-${import.meta.env.MAIN_VITE_GIT_SHORT_HASH}`, + }); - constructor(mainWindow: BrowserWindow) { - this.mainWindow = mainWindow; - } - - /** - * Builds and sets the application menu. - * Returns a reference to the menu as a convenience. - */ - public buildMenu(): Menu { - const template = - process.platform === 'darwin' - ? this.buildDarwinTemplate() - : this.buildDefaultTemplate(); + const template = getMenuTemplate(window); + const menu = Menu.buildFromTemplate(template); + Menu.setApplicationMenu(menu); +} - const menu = Menu.buildFromTemplate( - template as Array - ); +function getMenuTemplate( + window: BrowserWindow +): Array { + return platform.isMacOS + ? buildDarwinTemplate(window) + : buildDefaultTemplate(window); +} - Menu.setApplicationMenu(menu); +function getZoomFactor( + window: BrowserWindow, + isZoomIn: boolean = false +): number { + const correctZoomFactor = isZoomIn ? STEP_ZOOM_FACTOR : -STEP_ZOOM_FACTOR; + const zoomFactor = + (window?.webContents.getZoomFactor() * 100 + correctZoomFactor * 100) / 100; + return zoomFactor; +} - return menu; - } +function setZoomFactor(window: BrowserWindow, zoomFactor: number): void { + // TODO: uncomment when we have electron-store + // electronStore?.set(ElectronStorageItem.zoomFactor, zoomFactor); + window.webContents.setZoomFactor(zoomFactor); +} - private getZoomFactor(isZoomIn: boolean = false): number { - const correctZoomFactor = isZoomIn ? STEP_ZOOM_FACTOR : -STEP_ZOOM_FACTOR; - const zoomFactor = - (this.mainWindow?.webContents.getZoomFactor() * 100 + - correctZoomFactor * 100) / - 100; - return zoomFactor; - } +function buildDarwinTemplate( + window: BrowserWindow +): Array { + const subMenuApp: DarwinMenuItemConstructorOptions = { + label: app.name, + submenu: [ + { + label: `About ${app.name}`, + selector: 'orderFrontStandardAboutPanel:', + }, + { type: 'separator' }, + { + label: `Hide ${app.name}`, + accelerator: 'Command+H', + selector: 'hide:', + }, + { + label: 'Hide Others', + accelerator: 'Command+Shift+H', + selector: 'hideOtherApplications:', + }, + { + label: 'Show All', + selector: 'unhideAllApplications:', + }, + { type: 'separator' }, + { + label: 'Quit', + accelerator: 'Command+Q', + click: () => { + app.quit(); + }, + }, + ], + }; - private setZoomFactor(zoomFactor: number): void { - // TODO: uncomment when we have electron-store - // electronStore?.set(ElectronStorageItem.zoomFactor, zoomFactor); - this.mainWindow.webContents.setZoomFactor(zoomFactor); - } + const subMenuEdit: DarwinMenuItemConstructorOptions = { + label: 'Edit', + submenu: [ + { + label: 'Undo', + accelerator: 'Command+Z', + selector: 'undo:', + }, + { + label: 'Redo', + accelerator: 'Shift+Command+Z', + selector: 'redo:', + }, + { type: 'separator' }, + { + label: 'Cut', + accelerator: 'Command+X', + selector: 'cut:', + }, + { + label: 'Copy', + accelerator: 'Command+C', + selector: 'copy:', + }, + { + label: 'Paste', + accelerator: 'Command+V', + selector: 'paste:', + }, + { + label: 'Select All', + accelerator: 'Command+A', + selector: 'selectAll:', + }, + ], + }; - private buildDarwinTemplate(): Array { - const subMenuApp: DarwinMenuItemConstructorOptions = { - label: app.name, - submenu: [ - { - label: `About ${app.name}`, - selector: 'orderFrontStandardAboutPanel:', + const subMenuView: MenuItemConstructorOptions = { + label: 'View', + submenu: [ + { + label: 'Reload', + accelerator: 'Command+R', + click: () => { + window.webContents.reload(); + }, + }, + { type: 'separator' }, + { + label: 'Toggle Full Screen', + accelerator: 'Ctrl+Command+F', + click: () => { + window.setFullScreen(!window.isFullScreen()); }, - { type: 'separator' }, - { - label: `Hide ${app.name}`, - accelerator: 'Command+H', - selector: 'hide:', + }, + { type: 'separator' }, + { + label: 'Reset Zoom', + accelerator: 'CmdOrCtrl+0', + click: () => { + const zoomFactor = 1; + setZoomFactor(window, zoomFactor); }, - { - label: 'Hide Others', - accelerator: 'Command+Shift+H', - selector: 'hideOtherApplications:', + }, + { + label: 'Zoom In', + accelerator: 'CmdOrCtrl+=', + click: () => { + const zoomFactor = getZoomFactor(window, true); + setZoomFactor(window, zoomFactor); }, - { label: 'Show All', selector: 'unhideAllApplications:' }, - { type: 'separator' }, - { - label: 'Quit', - accelerator: 'Command+Q', - click: () => { - app.quit(); - }, + }, + { + label: 'Zoom Out', + accelerator: 'CmdOrCtrl+-', + click: () => { + const zoomFactor = getZoomFactor(window, false); + setZoomFactor(window, zoomFactor); }, - ], - }; + }, + ], + }; - const subMenuEdit: DarwinMenuItemConstructorOptions = { - label: 'Edit', - submenu: [ - { label: 'Undo', accelerator: 'Command+Z', selector: 'undo:' }, - { label: 'Redo', accelerator: 'Shift+Command+Z', selector: 'redo:' }, - { type: 'separator' }, - { label: 'Cut', accelerator: 'Command+X', selector: 'cut:' }, - { label: 'Copy', accelerator: 'Command+C', selector: 'copy:' }, - { label: 'Paste', accelerator: 'Command+V', selector: 'paste:' }, - { - label: 'Select All', - accelerator: 'Command+A', - selector: 'selectAll:', + const subMenuWindow: DarwinMenuItemConstructorOptions = { + label: 'Window', + submenu: [ + { + label: 'Minimize', + accelerator: 'Command+M', + selector: 'performMiniaturize:', + }, + { + label: 'Close', + accelerator: 'Command+W', + click: () => { + window.close(); }, - ], - }; + }, + { + type: 'separator', + }, + ], + }; - const subMenuView: MenuItemConstructorOptions = { - label: 'View', - submenu: [ - { - label: 'Reload', - accelerator: 'Command+R', - click: () => { - this.mainWindow.webContents.reload(); - }, + const subMenuHelp: MenuItemConstructorOptions = { + label: 'Help', + submenu: [ + { + label: 'Documentation', + click() { + shell.openExternal( + 'https://github.com/dragonrealms-phoenix/phoenix#readme' + ); }, - { type: 'separator' }, - { - label: 'Toggle Full Screen', - accelerator: 'Ctrl+Command+F', - click: () => { - this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); - }, + }, + { + label: 'Release Notes', + click() { + shell.openExternal( + 'https://github.com/dragonrealms-phoenix/phoenix/releases' + ); }, - { - label: 'Toggle Developer Tools', - accelerator: 'Alt+Command+I', - click: () => { - this.mainWindow.webContents.toggleDevTools(); - }, + }, + { + label: 'Report Issue', + click() { + shell.openExternal( + 'https://github.com/dragonrealms-phoenix/phoenix/issues' + ); + }, + }, + { + type: 'separator', + }, + { + label: 'View License', + click() { + shell.openExternal( + 'https://github.com/dragonrealms-phoenix/phoenix/blob/main/LICENSE.md' + ); }, - { type: 'separator' }, - { - label: 'Reset Zoom', - accelerator: 'CmdOrCtrl+0', - click: () => { - const zoomFactor = 1; - this.setZoomFactor(zoomFactor); - }, + }, + { + label: 'Privacy Policy', + click() { + shell.openExternal( + 'https://github.com/dragonrealms-phoenix/phoenix/blob/main/PRIVACY.md' + ); }, - { - label: 'Zoom In', - accelerator: 'CmdOrCtrl+=', - click: () => { - const zoomFactor = this.getZoomFactor(true); - this.setZoomFactor(zoomFactor); - }, + }, + { + label: 'Security Policy', + click() { + shell.openExternal( + 'https://github.com/dragonrealms-phoenix/phoenix/blob/main/SECURITY.md' + ); }, - { - label: 'Zoom Out', - accelerator: 'CmdOrCtrl+-', - click: () => { - const zoomFactor = this.getZoomFactor(); - this.setZoomFactor(zoomFactor); - }, + }, + ], + }; + + return [subMenuApp, subMenuEdit, subMenuWindow, subMenuView, subMenuHelp]; +} + +function buildDefaultTemplate( + window: BrowserWindow +): Array { + const subMenuWindow: MenuItemConstructorOptions = { + label: '&Window', + submenu: [ + { + label: '&Close', + accelerator: 'Ctrl+W', + click: () => { + window.close(); }, - ], - }; + }, + // type separator cannot be invisible + { + label: '', + type: platform.isLinux ? 'normal' : 'separator', + visible: false, + }, + ], + }; - const subMenuWindow: DarwinMenuItemConstructorOptions = { - label: 'Window', - submenu: [ - { - label: 'Minimize', - accelerator: 'Command+M', - selector: 'performMiniaturize:', + const subMenuView: MenuItemConstructorOptions = { + label: '&View', + submenu: [ + { + label: '&Reload', + accelerator: 'Ctrl+R', + click: () => { + window.webContents.reload(); + }, + }, + { type: 'separator' }, + { + label: 'Toggle &Full Screen', + accelerator: 'F11', + click: () => { + window.setFullScreen(!window.isFullScreen()); + // on Linux menubar is hidden on full screen mode + window.setMenuBarVisibility(true); + }, + }, + { type: 'separator' }, + { + label: 'Reset &Zoom', + accelerator: 'Ctrl+0', + click: () => { + const zoomFactor = 1; + setZoomFactor(window, zoomFactor); }, - { - label: 'Close', - accelerator: 'Command+W', - click: () => { - this.mainWindow.close(); - }, + }, + { + label: 'Zoom &In', + accelerator: 'Ctrl+=', + click: () => { + const zoomFactor = getZoomFactor(window, true); + setZoomFactor(window, zoomFactor); }, - { - type: 'separator', + }, + { + label: 'Zoom &Out', + accelerator: 'Ctrl+-', + click: () => { + const zoomFactor = getZoomFactor(window, false); + setZoomFactor(window, zoomFactor); }, - ], - }; + }, + ], + }; - const subMenuHelp: MenuItemConstructorOptions = { - label: 'Help', - submenu: [ - { - label: 'Documentation', - click() { - shell.openExternal( - 'https://github.com/dragonrealms-phoenix/phoenix#readme' - ); - }, + const subMenuHelp: MenuItemConstructorOptions = { + label: 'Help', + submenu: [ + { + label: 'Documentation', + click() { + shell.openExternal( + 'https://github.com/dragonrealms-phoenix/phoenix#readme' + ); }, - { - label: 'Release Notes', - click() { - shell.openExternal( - 'https://github.com/dragonrealms-phoenix/phoenix/releases' - ); - }, + }, + { + label: 'Release Notes', + click() { + shell.openExternal( + 'https://github.com/dragonrealms-phoenix/phoenix/releases' + ); }, - { - label: 'Report Issue', - click() { - shell.openExternal( - 'https://github.com/dragonrealms-phoenix/phoenix/issues' - ); - }, + }, + { + label: 'Report Issue', + click() { + shell.openExternal( + 'https://github.com/dragonrealms-phoenix/phoenix/issues' + ); }, - { - type: 'separator', + }, + { + type: 'separator', + }, + { + label: 'View License', + click() { + shell.openExternal( + 'https://github.com/dragonrealms-phoenix/phoenix/blob/main/LICENSE.md' + ); }, - { - label: 'View License', - click() { - shell.openExternal( - 'https://github.com/dragonrealms-phoenix/phoenix/blob/main/LICENSE.md' - ); - }, + }, + { + label: 'Privacy Policy', + click() { + shell.openExternal( + 'https://github.com/dragonrealms-phoenix/phoenix/blob/main/PRIVACY.md' + ); }, - { - label: 'Privacy Policy', - click() { - shell.openExternal( - 'https://github.com/dragonrealms-phoenix/phoenix/blob/main/PRIVACY.md' - ); - }, + }, + { + label: 'Security Policy', + click() { + shell.openExternal( + 'https://github.com/dragonrealms-phoenix/phoenix/blob/main/SECURITY.md' + ); }, - { - label: 'Security Policy', - click() { - shell.openExternal( - 'https://github.com/dragonrealms-phoenix/phoenix/blob/main/SECURITY.md' - ); - }, + }, + { type: 'separator' }, + { + label: `About ${app.name}`, + click: () => { + app.showAboutPanel(); }, - ], - }; - - return [subMenuApp, subMenuEdit, subMenuWindow, subMenuView, subMenuHelp]; - } - - private buildDefaultTemplate(): Array { - const templateDefault: Array = [ - { - label: '&Window', - submenu: [ - { - label: '&Close', - accelerator: 'Ctrl+W', - click: () => { - this.mainWindow.close(); - }, - }, - // type separator cannot be invisible - { - label: '', - type: process.platform !== 'linux' ? 'separator' : 'normal', - visible: false, - }, - ], - }, - { - label: '&View', - submenu: [ - { - label: '&Reload', - accelerator: 'Ctrl+R', - click: () => { - this.mainWindow.webContents.reload(); - }, - }, - { type: 'separator' }, - { - label: 'Toggle &Full Screen', - accelerator: 'F11', - click: () => { - this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); - // on Linux menubar is hidden on full screen mode - this.mainWindow.setMenuBarVisibility(true); - }, - }, - { - label: 'Toggle &Developer Tools', - accelerator: 'Ctrl+Shift+I', - click: () => { - this.mainWindow.webContents.toggleDevTools(); - }, - }, - { type: 'separator' }, - { - label: 'Reset &Zoom', - accelerator: 'Ctrl+0', - click: () => { - const zoomFactor = 1; - this.setZoomFactor(zoomFactor); - }, - }, - { - label: 'Zoom &In', - accelerator: 'Ctrl+=', - click: () => { - const zoomFactor = this.getZoomFactor(true); - this.setZoomFactor(zoomFactor); - }, - }, - { - label: 'Zoom &Out', - accelerator: 'Ctrl+-', - click: () => { - const zoomFactor = this.getZoomFactor(); - this.setZoomFactor(zoomFactor); - }, - }, - ], - }, - { - label: 'Help', - submenu: [ - { - label: 'License Terms', - click() { - shell.openExternal( - 'https://github.com/dragonrealms-phoenix/phoenix/blob/main/LICENSE' - ); - }, - }, - { - label: 'Submit a Bug or Idea', - click() { - shell.openExternal( - 'https://github.com/dragonrealms-phoenix/phoenix/issues' - ); - }, - }, - { - label: 'Documentation', - click() { - shell.openExternal( - 'https://github.com/dragonrealms-phoenix/phoenix#readme' - ); - }, - }, - { type: 'separator' }, - { - label: `About ${app.name}`, - click: () => { - app.showAboutPanel(); - }, - }, - ], - }, - ]; + }, + ], + }; - return templateDefault; - } + return [subMenuWindow, subMenuView, subMenuHelp]; } diff --git a/src/preload/api.d.ts b/src/preload/api.d.ts deleted file mode 100644 index 9daca6ce..00000000 --- a/src/preload/api.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * The api.d.ts file is auto-generated by the build process. - */ -export declare const appAPI: { - ping: () => Promise; -}; -declare global { - type AppAPI = typeof appAPI; -} diff --git a/src/preload/api.ts b/src/preload/api.ts deleted file mode 100644 index 7962be4f..00000000 --- a/src/preload/api.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { ipcRenderer } from 'electron'; - -/** - * The api.d.ts file is auto-generated by the build process. - */ - -// Custom APIs for renderer -export const appAPI = { - ping: async (): Promise => { - // Proxies request to the main process then returns any response - return ipcRenderer.invoke('ping'); - }, -}; - -declare global { - type AppAPI = typeof appAPI; -} diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts index 4b148a97..09eb4cb3 100644 --- a/src/preload/index.d.ts +++ b/src/preload/index.d.ts @@ -1,8 +1,12 @@ -import { ElectronAPI } from '@electron-toolkit/preload'; - +/** + * The index.d.ts file is auto-generated by the build process. + */ +export declare const appAPI: { + ping: () => Promise; +}; declare global { + type AppAPI = typeof appAPI; interface Window { - electron: ElectronAPI; api: AppAPI; } } diff --git a/src/preload/index.ts b/src/preload/index.ts index 015a8827..6d912dc6 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -1,6 +1,23 @@ -import { contextBridge } from 'electron'; -import { electronAPI } from '@electron-toolkit/preload'; -import { appAPI } from './api'; +import { contextBridge, ipcRenderer } from 'electron'; + +/** + * The index.d.ts file is auto-generated by the build process. + */ + +// Custom APIs for renderer +export const appAPI = { + ping: async (): Promise => { + // Proxies request to the main process then returns any response + return ipcRenderer.invoke('ping'); + }, +}; + +declare global { + type AppAPI = typeof appAPI; + + interface Window { + api: AppAPI; + } +} -contextBridge.exposeInMainWorld('electron', electronAPI); contextBridge.exposeInMainWorld('api', appAPI); diff --git a/src/renderer/index.html b/src/renderer/index.html index 17cfc0e6..ff029efb 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -2,11 +2,16 @@ - Electron - + DragonRealms Phoenix + + + diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index 66da92cc..017c58c3 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -1,156 +1,9 @@ -import icons from './assets/icons.svg'; -import Versions from './components/Versions'; +import HelloWorld from './components/HelloWorld'; function App(): JSX.Element { return (
- - - - - -

- You{"'"}ve successfully created an Electron project with React and - TypeScript -

-

- Please try pressing F12 to open the devTool -

- - - -
-
-
-

Configuring

-

- Config with electron.vite.config.ts and refer to the{' '} - - config guide - - . -

-
-
-
-
-

HMR

-

- Edit src/renderer files to test HMR. See{' '} - - docs - - . -

-
-
-
-
-

Hot Reloading

-

- Run{' '} - - {"'"}electron-vite dev --watch{"'"} - {' '} - to enable. See{' '} - - docs - - . -

-
-
-
-
-

Debugging

-

- Check out .vscode/launch.json. See{' '} - - docs - - . -

-
-
-
-
-

Source Code Protection

-

- Supported via built-in plugin bytecodePlugin. See{' '} - - docs - - . -

-
-
-
- -
-
+
); } diff --git a/src/renderer/src/assets/icons.svg b/src/renderer/src/assets/icons.svg deleted file mode 100644 index 8ef80447..00000000 --- a/src/renderer/src/assets/icons.svg +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/renderer/src/assets/index.css b/src/renderer/src/assets/index.css index fec9f3d2..e69de29b 100644 --- a/src/renderer/src/assets/index.css +++ b/src/renderer/src/assets/index.css @@ -1,205 +0,0 @@ -body { - display: flex; - flex-direction: column; - font-family: - Roboto, - -apple-system, - BlinkMacSystemFont, - 'Helvetica Neue', - 'Segoe UI', - 'Oxygen', - 'Ubuntu', - 'Cantarell', - 'Open Sans', - sans-serif; - color: #86a5b1; - background-color: #2f3241; -} - -* { - padding: 0; - margin: 0; -} - -ul { - list-style: none; -} - -code { - font-weight: 600; - padding: 3px 5px; - border-radius: 2px; - background-color: #26282e; - font-family: - ui-monospace, - SFMono-Regular, - SF Mono, - Menlo, - Consolas, - Liberation Mono, - monospace; - font-size: 85%; -} - -a { - color: #9feaf9; - font-weight: 600; - cursor: pointer; - text-decoration: none; - outline: none; -} - -a:hover { - border-bottom: 1px solid; -} - -.container { - flex: 1; - display: flex; - flex-direction: column; - max-width: 840px; - margin: 0 auto; - padding: 15px 30px 0 30px; -} - -.versions { - margin: 0 auto; - float: none; - clear: both; - overflow: hidden; - font-family: 'Menlo', 'Lucida Console', monospace; - color: #c2f5ff; - line-height: 1; - transition: all 0.3s; -} - -.versions li { - display: block; - float: left; - border-right: 1px solid rgba(194, 245, 255, 0.4); - padding: 0 20px; - font-size: 13px; - opacity: 0.8; -} - -.versions li:last-child { - border: none; -} - -.hero-logo { - margin-top: -0.4rem; - transition: all 0.3s; -} - -@media (max-width: 840px) { - .versions { - display: none; - } - - .hero-logo { - margin-top: -1.5rem; - } -} - -.hero-text { - font-weight: 400; - color: #c2f5ff; - text-align: center; - margin-top: -0.5rem; - margin-bottom: 10px; -} - -@media (max-width: 660px) { - .hero-logo { - display: none; - } - - .hero-text { - margin-top: 20px; - } -} - -.hero-tagline { - text-align: center; - margin-bottom: 14px; -} - -.links { - display: flex; - align-items: center; - justify-content: center; - margin-bottom: 24px; - font-size: 18px; - font-weight: 500; -} - -.links a { - font-weight: 500; -} - -.links .link-item { - padding: 0 4px; -} - -.features { - display: flex; - flex-wrap: wrap; - margin: -6px; -} - -.features .feature-item { - width: 33.33%; - box-sizing: border-box; - padding: 6px; -} - -.features article { - background-color: rgba(194, 245, 255, 0.1); - border-radius: 8px; - box-sizing: border-box; - padding: 12px; - height: 100%; -} - -.features span { - color: #d4e8ef; - word-break: break-all; -} - -.features .title { - font-size: 17px; - font-weight: 500; - color: #c2f5ff; - line-height: 22px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.features .detail { - font-size: 14px; - font-weight: 500; - line-height: 22px; - margin-top: 6px; -} - -@media (max-width: 660px) { - .features .feature-item { - width: 50%; - } -} - -@media (max-width: 480px) { - .links { - flex-direction: column; - line-height: 32px; - } - - .links .link-dot { - display: none; - } - - .features .feature-item { - width: 100%; - } -} diff --git a/src/renderer/src/components/HelloWorld.tsx b/src/renderer/src/components/HelloWorld.tsx new file mode 100644 index 00000000..2fa827a0 --- /dev/null +++ b/src/renderer/src/components/HelloWorld.tsx @@ -0,0 +1,12 @@ +function HelloWorld(): JSX.Element { + return ( +
+

Hello World

+

+ DragonRealms +

+
+ ); +} + +export default HelloWorld; diff --git a/src/renderer/src/components/Versions.tsx b/src/renderer/src/components/Versions.tsx deleted file mode 100644 index 82c2b36c..00000000 --- a/src/renderer/src/components/Versions.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { useState } from 'react'; - -function Versions(): JSX.Element { - const [versions] = useState(window.electron.process.versions); - - return ( -
    -
  • Electron v{versions.electron}
  • -
  • Chromium v{versions.chrome}
  • -
  • Node v{versions.node}
  • -
  • V8 v{versions.v8}
  • -
- ); -} - -export default Versions; diff --git a/src/renderer/src/main.tsx b/src/renderer/src/main.tsx index 6f65eea1..3007f0fa 100644 --- a/src/renderer/src/main.tsx +++ b/src/renderer/src/main.tsx @@ -9,7 +9,6 @@ import './assets/index.css'; initializeSentry(); const logger = createLogger('renderer'); -logger.info('message from renderer'); ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(