1- const { app, BrowserWindow, globalShortcut , screen, ipcMain } = require ( 'electron' ) ;
1+ const { app, BrowserWindow, ipcMain , Menu , screen, desktopCapturer } = require ( 'electron' ) ;
22const path = require ( 'path' ) ;
33const fs = require ( 'fs' ) ;
44const os = require ( 'os' ) ;
55const si = require ( 'systeminformation' ) ;
6+ const { uIOhook, UiohookKey } = require ( 'uiohook-napi' ) ;
67
78let mainWindow ;
89let config = { autoLaunch : false , shortcut : 'CommandOrControl+G' } ;
910let updateInterval ;
11+ let isCtrlPressed = false ;
12+ let isRecording = false ;
13+ let interactRect = null ;
1014
1115async function collectStats ( ) {
1216 const stats = { } ;
1317
14- // CPU Stats
15- const cpuTemp = await si . cpuTemperature ( ) ;
16- stats . cpuTemp = cpuTemp . main !== null ? cpuTemp . main . toFixed ( 1 ) : 'N/A' ;
18+ try {
19+ // CPU Stats
20+ const cpuTemp = await si . cpuTemperature ( ) ;
21+ stats . cpuTemp = cpuTemp . main !== null && cpuTemp . main !== undefined ? cpuTemp . main . toFixed ( 1 ) : '--' ;
1722
18- const currentLoad = await si . currentLoad ( ) ;
19- stats . cpuUsage = currentLoad . currentLoad . toFixed ( 1 ) ;
23+ const currentLoad = await si . currentLoad ( ) ;
24+ stats . cpuUsage = currentLoad . currentLoad ? currentLoad . currentLoad . toFixed ( 1 ) : '--' ;
2025
21- const cpu = await si . cpu ( ) ;
22- stats . cpuFreq = cpu . speed ? ( cpu . speed * 1000 ) . toFixed ( 0 ) : 'N/A ' ;
26+ const cpu = await si . cpu ( ) ;
27+ stats . cpuFreq = cpu . speed ? ( cpu . speed * 1000 ) . toFixed ( 0 ) : '-- ' ;
2328
24- stats . cpuVoltage = 'N/A' ; // systeminformation doesn't provide voltage directly
29+ stats . cpuCores = os . cpus ( ) . length ; // Zastąpienie voltage liczbą rdzeni (zawsze osiągalne)
2530
26- // For fans, systeminformation has si.chassis() but limited, fallback to sensors if needed
27- stats . cpuFan = 'N/A ';
31+ const chassis = await si . chassis ( ) ;
32+ stats . cpuFan = chassis . fans && chassis . fans . length > 0 ? chassis . fans [ 0 ] . speed : '-- ';
2833
29- // GPU Stats
30- const graphics = await si . graphics ( ) ;
31- if ( graphics . controllers && graphics . controllers . length > 0 ) {
32- const gpu = graphics . controllers [ 0 ] ;
33- stats . gpuTemp = gpu . temperatureGpu ? gpu . temperatureGpu . toFixed ( 1 ) : 'N/A ' ;
34- stats . gpuUsage = gpu . utilizationGpu ? gpu . utilizationGpu . toFixed ( 1 ) : 'N/A ' ;
35- stats . gpuFan = gpu . fanPercent ? gpu . fanPercent . toFixed ( 1 ) : 'N/A ' ;
36- stats . gpuMem = gpu . memoryUsed ? `${ gpu . memoryUsed } /${ gpu . memoryTotal } ` : 'N/A ' ;
37- stats . gpuPower = gpu . powerDraw ? gpu . powerDraw . toFixed ( 1 ) : 'N/A ' ;
38- } else {
39- stats . gpuTemp = 'N/A ' ;
40- stats . gpuUsage = 'N/A ' ;
41- stats . gpuFan = 'N/A ' ;
42- stats . gpuMem = 'N/A ' ;
43- stats . gpuPower = 'N/A ' ;
44- }
34+ // GPU Stats (zbierane, ale nie wyświetlane w top-right)
35+ const graphics = await si . graphics ( ) ;
36+ if ( graphics . controllers && graphics . controllers . length > 0 ) {
37+ const gpu = graphics . controllers [ 0 ] ;
38+ stats . gpuTemp = gpu . temperatureGpu !== undefined && gpu . temperatureGpu !== null ? gpu . temperatureGpu . toFixed ( 1 ) : '-- ' ;
39+ stats . gpuUsage = gpu . utilizationGpu !== undefined && gpu . utilizationGpu !== null ? gpu . utilizationGpu . toFixed ( 1 ) : '-- ' ;
40+ stats . gpuFan = gpu . fanPercent !== undefined && gpu . fanPercent !== null ? gpu . fanPercent . toFixed ( 1 ) : '-- ' ;
41+ stats . gpuMem = gpu . memoryUsed && gpu . memoryTotal ? `${ gpu . memoryUsed } /${ gpu . memoryTotal } ` : '-- ' ;
42+ stats . gpuPower = gpu . powerDraw !== undefined && gpu . powerDraw !== null ? gpu . powerDraw . toFixed ( 1 ) : '-- ' ;
43+ } else {
44+ stats . gpuTemp = '-- ' ;
45+ stats . gpuUsage = '-- ' ;
46+ stats . gpuFan = '-- ' ;
47+ stats . gpuMem = '-- ' ;
48+ stats . gpuPower = '-- ' ;
49+ }
4550
46- // RAM Usage
47- const mem = await si . mem ( ) ;
48- stats . ramUsage = `${ ( mem . used / 1024 / 1024 / 1024 ) . toFixed ( 1 ) } / ${ ( mem . total / 1024 / 1024 / 1024 ) . toFixed ( 1 ) } GB` ;
49-
50- // Disk Usage
51- const fsSize = await si . fsSize ( ) ;
52- const root = fsSize . find ( d => d . mount === '/' ) ;
53- stats . diskUsage = root ? `${ ( root . used / 1024 / 1024 / 1024 ) . toFixed ( 1 ) } / ${ ( root . size / 1024 / 1024 / 1024 ) . toFixed ( 1 ) } GB` : 'N/A' ;
54-
55- // Battery
56- const battery = await si . battery ( ) ;
57- stats . batteryLevel = battery . hasBattery ? battery . percent : 'N/A' ;
58-
59- // Uptime
60- const time = si . time ( ) ;
61- stats . uptime = time . uptimeFormatted || `${ Math . floor ( os . uptime ( ) / 3600 ) } h ${ Math . floor ( ( os . uptime ( ) % 3600 ) / 60 ) } m` ;
62-
63- // Network
64- const netStats = await si . networkStats ( ) ;
65- if ( netStats . length > 0 ) {
66- const net = netStats [ 0 ] ;
67- stats . netDownload = ( net . rx_sec / 1024 / 1024 * 8 ) . toFixed ( 2 ) + ' Mbps' ;
68- stats . netUpload = ( net . tx_sec / 1024 / 1024 * 8 ) . toFixed ( 2 ) + ' Mbps' ;
69- } else {
70- stats . netDownload = 'N/A' ;
71- stats . netUpload = 'N/A' ;
72- }
51+ // RAM Usage
52+ const mem = await si . mem ( ) ;
53+ stats . ramUsage = mem . used && mem . total ? `${ ( mem . used / 1024 / 1024 / 1024 ) . toFixed ( 1 ) } / ${ ( mem . total / 1024 / 1024 / 1024 ) . toFixed ( 1 ) } GB` : '--' ;
54+
55+ // Disk Usage
56+ const fsSize = await si . fsSize ( ) ;
57+ const root = fsSize . find ( d => d . mount === '/' || d . mount === 'C:\\' ) ;
58+ stats . diskUsage = root ? `${ ( root . used / 1024 / 1024 / 1024 ) . toFixed ( 1 ) } / ${ ( root . size / 1024 / 1024 / 1024 ) . toFixed ( 1 ) } GB` : '--' ;
59+
60+ // Battery
61+ const battery = await si . battery ( ) ;
62+ stats . batteryLevel = battery . hasBattery && battery . percent !== null ? battery . percent : '--' ;
7363
74- // FPS placeholder
75- stats . fps = 'N/A' ;
64+ // Uptime
65+ const time = await si . time ( ) ;
66+ stats . uptime = time . uptime ? `${ Math . floor ( time . uptime / 3600 ) } h ${ Math . floor ( ( time . uptime % 3600 ) / 60 ) } m` : '--' ;
7667
77- // Load Avg
78- stats . loadAvg = os . loadavg ( ) . map ( l => l . toFixed ( 2 ) ) . join ( ' ' ) ;
68+ // Network
69+ const defaultIface = await si . networkInterfaceDefault ( ) ;
70+ const netStats = await si . networkStats ( defaultIface ) ;
71+ if ( netStats . length > 0 && netStats [ 0 ] . rx_sec !== null && netStats [ 0 ] . tx_sec !== null ) {
72+ stats . netDownload = ( netStats [ 0 ] . rx_sec / 1024 / 1024 * 8 ) . toFixed ( 2 ) ;
73+ stats . netUpload = ( netStats [ 0 ] . tx_sec / 1024 / 1024 * 8 ) . toFixed ( 2 ) ;
74+ } else {
75+ stats . netDownload = '--' ;
76+ stats . netUpload = '--' ;
77+ }
78+
79+ stats . fps = '--' ; // Zostawiamy, ale w renderer.js dodamy counter
80+
81+ stats . loadAvg = os . loadavg ( ) . map ( l => l . toFixed ( 2 ) ) . join ( ' ' ) ;
82+ } catch ( error ) {
83+ console . error ( 'Error collecting stats:' , error ) ;
84+ // Fallback defaults
85+ stats . cpuTemp = '--' ;
86+ stats . cpuUsage = '--' ;
87+ stats . cpuFreq = '--' ;
88+ stats . cpuCores = os . cpus ( ) . length ;
89+ stats . cpuFan = '--' ;
90+ stats . gpuTemp = '--' ;
91+ stats . gpuUsage = '--' ;
92+ stats . gpuFan = '--' ;
93+ stats . gpuMem = '--' ;
94+ stats . gpuPower = '--' ;
95+ stats . ramUsage = '--' ;
96+ stats . diskUsage = '--' ;
97+ stats . batteryLevel = '--' ;
98+ stats . uptime = '--' ;
99+ stats . netDownload = '--' ;
100+ stats . netUpload = '--' ;
101+ stats . fps = '--' ;
102+ stats . loadAvg = '--' ;
103+ }
79104
80105 return stats ;
81106}
@@ -94,6 +119,18 @@ function stopUpdating() {
94119 if ( updateInterval ) clearInterval ( updateInterval ) ;
95120}
96121
122+ function toggleWindow ( ) {
123+ if ( isRecording ) {
124+ mainWindow . webContents . send ( 'stop-recording' ) ;
125+ isRecording = false ;
126+ mainWindow . show ( ) ;
127+ } else if ( mainWindow . isVisible ( ) ) {
128+ mainWindow . hide ( ) ;
129+ } else {
130+ mainWindow . show ( ) ;
131+ }
132+ }
133+
97134function createWindow ( ) {
98135 const { width, height } = screen . getPrimaryDisplay ( ) . workAreaSize ;
99136
@@ -122,6 +159,10 @@ function createWindow() {
122159
123160 mainWindow . on ( 'show' , startUpdating ) ;
124161 mainWindow . on ( 'hide' , stopUpdating ) ;
162+
163+ mainWindow . webContents . on ( 'did-finish-load' , ( ) => {
164+ mainWindow . webContents . send ( 'get-rects' ) ;
165+ } ) ;
125166}
126167
127168app . whenReady ( ) . then ( ( ) => {
@@ -132,25 +173,49 @@ app.whenReady().then(() => {
132173
133174 createWindow ( ) ;
134175
135- const ret = globalShortcut . register ( config . shortcut , ( ) => {
136- if ( mainWindow . isVisible ( ) ) {
137- mainWindow . hide ( ) ;
138- } else {
139- mainWindow . show ( ) ;
176+ // Użyj uiohook-napi do globalnego nasłuchu klawiszy na Waylandzie
177+ uIOhook . start ( ) ;
178+
179+ uIOhook . on ( 'keydown' , ( e ) => {
180+ if ( e . keycode === UiohookKey . Ctrl ) isCtrlPressed = true ;
181+ if ( isCtrlPressed && e . keycode === UiohookKey . G ) {
182+ console . log ( 'Ctrl + G detected' ) ;
183+ toggleWindow ( ) ;
140184 }
141185 } ) ;
142186
143- if ( ! ret ) {
144- console . log ( 'Shortcut registration failed' ) ;
145- }
187+ uIOhook . on ( 'keyup' , ( e ) => {
188+ if ( e . keycode === UiohookKey . Ctrl ) isCtrlPressed = false ;
189+ } ) ;
146190
147- if ( config . autoLaunch ) {
148- mainWindow . show ( ) ;
149- }
191+ uIOhook . on ( 'mousemove' , ( e ) => {
192+ if ( mainWindow && mainWindow . isVisible ( ) && interactRect ) {
193+ const display = screen . getPrimaryDisplay ( ) ;
194+ const left = display . workArea . width - interactRect . right - interactRect . width ;
195+ const right = display . workArea . width - interactRect . right ;
196+ const top = interactRect . top ;
197+ const bottom = interactRect . top + interactRect . height ;
198+ if ( e . x > left && e . x < right && e . y > top && e . y < bottom ) {
199+ mainWindow . setIgnoreMouseEvents ( false ) ;
200+ } else {
201+ mainWindow . setIgnoreMouseEvents ( true ) ;
202+ }
203+ }
204+ } ) ;
205+
206+ // Opcjonalne menu dla alternatywy
207+ const menu = Menu . buildFromTemplate ( [
208+ {
209+ label : 'Toggle Overlay' ,
210+ accelerator : 'Ctrl+G' ,
211+ click : toggleWindow
212+ }
213+ ] ) ;
214+ Menu . setApplicationMenu ( menu ) ;
150215} ) ;
151216
152217app . on ( 'will-quit' , ( ) => {
153- globalShortcut . unregisterAll ( ) ;
218+ uIOhook . stop ( ) ;
154219} ) ;
155220
156221app . on ( 'window-all-closed' , ( ) => {
@@ -164,3 +229,29 @@ app.on('activate', () => {
164229 createWindow ( ) ;
165230 }
166231} ) ;
232+
233+ ipcMain . on ( 'rects' , ( event , rects ) => {
234+ interactRect = rects . topRight ;
235+ } ) ;
236+
237+ ipcMain . on ( 'take-screenshot' , async ( ) => {
238+ mainWindow . hide ( ) ;
239+ await new Promise ( resolve => setTimeout ( resolve , 100 ) ) ; // Opóźnienie, aby upewnić się, że okno jest ukryte
240+ try {
241+ const sources = await desktopCapturer . getSources ( {
242+ types : [ 'screen' ] ,
243+ thumbnailSize : screen . getPrimaryDisplay ( ) . workAreaSize
244+ } ) ;
245+ const image = sources [ 0 ] . thumbnail . toPNG ( ) ;
246+ const filePath = path . join ( os . homedir ( ) , 'Desktop' , `screenshot_${ Date . now ( ) } .png` ) ;
247+ fs . writeFileSync ( filePath , image ) ;
248+ } catch ( err ) {
249+ console . error ( 'Error taking screenshot:' , err ) ;
250+ }
251+ mainWindow . show ( ) ;
252+ } ) ;
253+
254+ ipcMain . on ( 'start-recording' , ( ) => {
255+ mainWindow . hide ( ) ;
256+ isRecording = true ;
257+ } ) ;
0 commit comments