-
@@ -44,6 +55,7 @@ import useEventHandler from '../Screens/useEventHandler'
import { useDevStore } from '~/store/dev'
import { initialPanZoom } from './panzoomFns'
+
// Connect w/ Electron
const interactions = useInteractionStore()
@@ -104,6 +116,7 @@ onMounted(async () => {
// Init Panzoom globally
// We use the 'canvas' option to enable interactions on the parent DOM Node as well
// Docs: https://github.com/timmywil/panzoom#canvas
+
$root.$panzoom = Panzoom(innerPanArea.value, {
canvas: true, // Allows parent to control child
cursor: 'grab',
@@ -216,6 +229,8 @@ function enableEventListeners() {
}
}
+
+
/**
* Handles wheel events (i.e. mousewheel)
* @param DOMElement DOM element that Panzoom is on
@@ -323,6 +338,7 @@ function fitToScreen() {
position: relative; // Important
display: inline-block;
overflow: visible;
+ will-change: auto;
}
#parent {
@@ -341,7 +357,6 @@ function fitToScreen() {
}
.dev-visual-debugger {
-
&:before,
&:after {
position: absolute;
@@ -367,7 +382,6 @@ function fitToScreen() {
// Nested debugger
.dev-visual-debugger {
-
// Vertical line
&:before {
position: absolute;
diff --git a/app/electron/main.ts b/app/electron/main.ts
index a2701a24..9dbd7ad6 100644
--- a/app/electron/main.ts
+++ b/app/electron/main.ts
@@ -7,8 +7,11 @@ import path from 'path'
import { init as initIpcHandlers } from './ipcHandlers'
import mainWindowInit from './mainWindow'
import { getPackageJson } from './util'
- ; import { RuntimeConfig } from './config'
-(async () => {
+import { RuntimeConfig } from './config'
+import startPerfMonitoring from './usage-monitoring'
+import { installBrowsers } from './cross-browser/playwright-browser-manager'
+import enableCrossBrowserScreenshots from './cross-browser/screenshots/api'
+;(async () => {
const version = await getPackageJson().then((data) => data.version)
// Set the version
@@ -30,19 +33,19 @@ import { getPackageJson } from './util'
// Check if asar is enabled
const runtimeConfig = RuntimeConfig.getInstance()
- if (!runtimeConfig.dev.appFilesPath || !runtimeConfig.packaged.appFilesPath) {
+ if (
+ !runtimeConfig.dev.appFilesPath ||
+ !runtimeConfig.packaged.appFilesPath
+ ) {
throw new Error('RuntimeConfig.packaged.appFilesPath is not set')
}
webPreferences.preload = isDev
- ? path.join(
- runtimeConfig.dev.appFilesPath,
- 'extraResources/inject.js'
- )
+ ? path.join(runtimeConfig.dev.appFilesPath, 'extraResources/inject.js')
: path.join(
- runtimeConfig.packaged.appFilesPath,
- 'dist-electron/extraResources/inject.js'
- )
+ runtimeConfig.packaged.appFilesPath,
+ 'dist-electron/extraResources/inject.js'
+ )
// webPreferences.nodeIntegration = false // Disable Node.js integration inside
// webPreferences.webSecurity = false // Disable web security
@@ -72,5 +75,14 @@ import { getPackageJson } from './util'
initIpcHandlers()
// Load here all startup windows
- mainWindowInit()
+ const mainWindow = await mainWindowInit()
+
+ // Start monitoring CPU/Memory usage
+ startPerfMonitoring()
+
+ // Check for browser installations
+ installBrowsers().then(() => {
+ // Screenshot worker
+ enableCrossBrowserScreenshots()
+ })
})()
diff --git a/app/electron/mainWindow.ts b/app/electron/mainWindow.ts
index 9fa50938..6062553b 100644
--- a/app/electron/mainWindow.ts
+++ b/app/electron/mainWindow.ts
@@ -11,18 +11,15 @@ import windowPosition from './windowPosition'
// import browserInstaller from './browser-installer'
import { setMenu } from './menu'
import { init as initUpdates } from './updates'
-import browserInstaller from './browser-installer'
-import { installBrowsers } from './cross-browser/playwright-browser-manager'
import log from 'electron-log'
import isDev from 'electron-is-dev'
-import enableCrossBrowserScreenshots from './cross-browser/screenshots/api'
import { RuntimeConfig } from './config'
const INDEX_PATH = path.join(__dirname, '..', 'renderer', 'index.html')
const DEV_SERVER_URL = process.env.DEV_SERVER_URL // eslint-disable-line prefer-destructuring
-export default function init() {
+export default async function init(): Promise {
// Get saved window position state
windowPosition.onAppLoad()
const initialWindowPos = windowPosition.getState()
@@ -43,7 +40,7 @@ export default function init() {
titleBarStyle: 'hiddenInset', // Hide the bar
})
- winHandler.onCreated(async (browserWindow: BrowserWindow) => {
+ return await winHandler.onCreated(async (browserWindow: BrowserWindow) => {
// Initialize the runtime config
// Exposes file paths and other dynamic properties
const appConfig = RuntimeConfig.getInstance()
@@ -77,11 +74,7 @@ export default function init() {
initUpdates(browserWindow)
})
- // Check for browser installations
- await installBrowsers()
-
- // Screenshot worker test
- enableCrossBrowserScreenshots()
+
// Reload when requested by the renderer process
ipcMain.handle('reload-window', () => {
@@ -100,5 +93,8 @@ export default function init() {
browserWindow.on('closed', () => {
browserWindow = null
})
+
+ // Return the window instance
+ return browserWindow
})
}
diff --git a/app/electron/usage-monitoring.ts b/app/electron/usage-monitoring.ts
new file mode 100644
index 00000000..16506efe
--- /dev/null
+++ b/app/electron/usage-monitoring.ts
@@ -0,0 +1,82 @@
+import si from 'systeminformation'
+import { app, BrowserWindow } from 'electron'
+import { RuntimeConfig } from './config'
+
+let cpuInterval: string | number | NodeJS.Timeout | undefined
+let memoryInterval: string | number | NodeJS.Timeout | undefined
+
+const cpuThreshold = 85 // 85% CPU usage threshold
+const memoryThreshold = 90 // 90% memory usage threshold
+
+let currentWindow: BrowserWindow | null = null
+
+const emitToRenderer = (channel: string, ...args: any[]) => {
+ if (currentWindow) {
+ currentWindow.webContents.send(channel, ...args)
+ } else {
+ throw new Error('No focused window found')
+ }
+}
+
+export default function startMonitoring() {
+ const appConfig = RuntimeConfig.getInstance()
+ currentWindow = appConfig.window
+
+ monitorCPUUsage()
+ monitorMemoryUsage()
+
+ // Register before-quit event listener
+ app.on('before-quit', () => {
+ // Clear the monitoring intervals
+ clearInterval(cpuInterval)
+ clearInterval(memoryInterval)
+ })
+}
+
+function monitorCPUUsage() {
+ cpuInterval = setInterval(async () => {
+ try {
+ const cpuData = await si.currentLoad()
+ const cpuUsagePercent = cpuData.currentLoad
+
+ emitToRenderer('perf-cpu', Math.round(cpuUsagePercent)) // Send CPU usage to renderer process
+
+ if (cpuUsagePercent >= cpuThreshold) {
+ const warningMessage = `High memory usage: ${cpuUsagePercent.toFixed(
+ 2
+ )}%`
+ console.warn(warningMessage)
+ emitToRenderer('perf-warning', warningMessage)
+ } else {
+ console.info('CPU usage:', cpuUsagePercent.toFixed(2), '%')
+ }
+ } catch (err) {
+ console.debug('Error retrieving CPU usage:', err)
+ }
+ }, 1000)
+}
+
+function monitorMemoryUsage() {
+ memoryInterval = setInterval(async () => {
+ try {
+ const memData = await si.mem()
+ const totalMemory = memData.total
+ const usedMemory = memData.active
+ const memoryUsagePercent = (usedMemory / totalMemory) * 100
+
+ emitToRenderer('perf-memory', Math.round(memoryUsagePercent)) // Send memory usage to renderer process
+
+ if (memoryUsagePercent >= memoryThreshold) {
+ const warningMessage = `High memory usage: ${memoryUsagePercent.toFixed(
+ 2
+ )}%`
+ console.warn(warningMessage)
+ emitToRenderer('perf-warning', warningMessage)
+ } else {
+ console.info('Memory usage:', memoryUsagePercent.toFixed(2), '%')
+ }
+ } catch (err) {
+ console.error('Error retrieving memory usage:', err)
+ }
+ }, 1000)
+}
diff --git a/app/mixins/rightClickMenu.ts b/app/mixins/rightClickMenu.ts
index 9bc58959..b3d6a982 100644
--- a/app/mixins/rightClickMenu.ts
+++ b/app/mixins/rightClickMenu.ts
@@ -46,7 +46,7 @@ export default function (artboard: Artboard) {
label: artboard.isVisible ? 'Hide' : 'Show',
click() {
const store = useArtboardsStore()
- store.changeArtboardVisibility(artboard)
+ store.changeArtboardVisibility(artboard.id)
},
})
)
diff --git a/app/package.json b/app/package.json
index 13422a66..5a07b7d6 100644
--- a/app/package.json
+++ b/app/package.json
@@ -40,6 +40,7 @@
"electron-store": "^8.1.0",
"electron-updater": "5.3.0",
"html-to-image": "^1.11.11",
+ "systeminformation": "^5.18.3",
"uuid": "8.3.2",
"vue": "^3.2.47",
"vuetify": "^3.0.6"
diff --git a/app/store/artboards.ts b/app/store/artboards.ts
index 8cd53dfd..081ef120 100644
--- a/app/store/artboards.ts
+++ b/app/store/artboards.ts
@@ -87,12 +87,15 @@ export const useArtboardsStore = defineStore('artboards', {
? (artboard.isInViewport = isVisible)
: console.warn('No artboard found')
},
- changeArtboardVisibility(artboard: Artboard) {
- // 1. Get the artboard.id
- const id = artboard.id
- const index = this.list.findIndex((obj) => obj.id === id)
- // 2. Change the visibility of just that artboard's is property
- this.list[index].isVisible = !artboard.isVisible
+ changeArtboardVisibility(id: Artboard['id']) {
+ const match = this.list.find((obj) => obj.id === id)
+
+ if (!match) {
+ console.error('Artboard not found')
+ return
+ }
+
+ match.isVisible = !match.isVisible
},
updateArtboardAtIndex(artboard: Partial) {
// 1. Get the artboard.id
diff --git a/yarn.lock b/yarn.lock
index 17ce493c..705d68f7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2841,6 +2841,7 @@ __metadata:
sass: 1.60.0
sortablejs: ^1.15.0
sortablejs-vue3: ^1.2.9
+ systeminformation: ^5.18.3
tailwindcss: ^3.3.2
typescript: ^5.0.0
uuid: 8.3.2
@@ -20303,6 +20304,16 @@ __metadata:
languageName: node
linkType: hard
+"systeminformation@npm:^5.18.3":
+ version: 5.18.3
+ resolution: "systeminformation@npm:5.18.3"
+ bin:
+ systeminformation: lib/cli.js
+ checksum: dac4d0b92c102d4f5766692ea275c54bf234012a989e3383e43511ada7193957843e5ad391df3dfea89d3579ae0d9f9e7ca1e555b48f30c173374dcb8f645ce3
+ conditions: (os=darwin | os=linux | os=win32 | os=freebsd | os=openbsd | os=netbsd | os=sunos | os=android)
+ languageName: node
+ linkType: hard
+
"table@npm:^6.0.9":
version: 6.8.1
resolution: "table@npm:6.8.1"