fix: click-through HUD, tray icon fallback, toggle shortcut, ESC handler
- hud.js: setIgnoreMouseEvents when inactive, alwaysOnTop only when active, backgroundColor #00000000, no visibleOnAllWorkspaces, toggleHud export - tray.js: onToggle callback, isEmpty() fallback icon, click toggles - preload.js: ESC listener at capture phase — works without HUD page changes - main.js: Cmd+Shift+F toggles, import toggleHud, pass onToggle to tray Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>main
parent
1189f6000c
commit
dc7e888ee3
|
|
@ -5,6 +5,7 @@ const HUD_URL = process.env.FOX_HUD_URL || 'https://fox.pxo.at/hud';
|
||||||
let hudWindow = null;
|
let hudWindow = null;
|
||||||
let hudLoaded = false;
|
let hudLoaded = false;
|
||||||
let pendingShow = false;
|
let pendingShow = false;
|
||||||
|
let hudActive = false;
|
||||||
|
|
||||||
function createHud() {
|
function createHud() {
|
||||||
const { width, height } = screen.getPrimaryDisplay().workAreaSize;
|
const { width, height } = screen.getPrimaryDisplay().workAreaSize;
|
||||||
|
|
@ -16,7 +17,8 @@ function createHud() {
|
||||||
y: 0,
|
y: 0,
|
||||||
frame: false,
|
frame: false,
|
||||||
transparent: true,
|
transparent: true,
|
||||||
alwaysOnTop: true,
|
backgroundColor: '#00000000',
|
||||||
|
alwaysOnTop: false,
|
||||||
show: false,
|
show: false,
|
||||||
skipTaskbar: true,
|
skipTaskbar: true,
|
||||||
hasShadow: false,
|
hasShadow: false,
|
||||||
|
|
@ -29,14 +31,14 @@ function createHud() {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
hudWindow.setAlwaysOnTop(true, 'screen-saver');
|
// Unsichtbar und click-through bis aktiv
|
||||||
hudWindow.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true });
|
hudWindow.setIgnoreMouseEvents(true);
|
||||||
|
|
||||||
hudWindow.webContents.once('did-finish-load', () => {
|
hudWindow.webContents.once('did-finish-load', () => {
|
||||||
hudLoaded = true;
|
hudLoaded = true;
|
||||||
if (pendingShow) {
|
if (pendingShow) {
|
||||||
pendingShow = false;
|
pendingShow = false;
|
||||||
hudWindow.webContents.send('hud-show');
|
_activateHud();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -46,28 +48,47 @@ function createHud() {
|
||||||
hudWindow = null;
|
hudWindow = null;
|
||||||
hudLoaded = false;
|
hudLoaded = false;
|
||||||
pendingShow = false;
|
pendingShow = false;
|
||||||
|
hudActive = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
return hudWindow;
|
return hudWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _activateHud() {
|
||||||
|
hudActive = true;
|
||||||
|
hudWindow.setIgnoreMouseEvents(false);
|
||||||
|
hudWindow.setAlwaysOnTop(true, 'screen-saver');
|
||||||
|
hudWindow.webContents.send('hud-show');
|
||||||
|
}
|
||||||
|
|
||||||
function showHud() {
|
function showHud() {
|
||||||
if (!hudWindow) createHud();
|
if (!hudWindow) createHud();
|
||||||
hudWindow.show();
|
hudWindow.show();
|
||||||
hudWindow.focus();
|
hudWindow.focus();
|
||||||
if (hudLoaded) {
|
if (hudLoaded) {
|
||||||
hudWindow.webContents.send('hud-show');
|
_activateHud();
|
||||||
} else {
|
} else {
|
||||||
pendingShow = true;
|
pendingShow = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideHud() {
|
function hideHud() {
|
||||||
if (!hudWindow) return;
|
if (!hudWindow || !hudActive) return;
|
||||||
|
hudActive = false;
|
||||||
|
hudWindow.setAlwaysOnTop(false);
|
||||||
|
hudWindow.setIgnoreMouseEvents(true);
|
||||||
hudWindow.webContents.send('hud-hide');
|
hudWindow.webContents.send('hud-hide');
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (hudWindow) hudWindow.hide();
|
if (hudWindow) hudWindow.hide();
|
||||||
}, 600);
|
}, 600);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { createHud, showHud, hideHud };
|
function toggleHud() {
|
||||||
|
if (hudActive) {
|
||||||
|
hideHud();
|
||||||
|
} else {
|
||||||
|
showHud();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { createHud, showHud, hideHud, toggleHud };
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
const { app, ipcMain, globalShortcut, nativeTheme } = require('electron');
|
const { app, ipcMain, globalShortcut, nativeTheme } = require('electron');
|
||||||
const { createTray } = require('./tray');
|
const { createTray } = require('./tray');
|
||||||
const { createHud, showHud, hideHud } = require('./hud');
|
const { createHud, showHud, hideHud, toggleHud } = require('./hud');
|
||||||
const { startWakeword, stopWakeword } = require('./wakeword');
|
const { startWakeword, stopWakeword } = require('./wakeword');
|
||||||
const { executeMacCommand } = require('./mac-agent');
|
const { executeMacCommand } = require('./mac-agent');
|
||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
|
|
@ -19,12 +19,13 @@ app.whenReady().then(() => {
|
||||||
createHud();
|
createHud();
|
||||||
|
|
||||||
createTray({
|
createTray({
|
||||||
onShow: showHud,
|
onToggle: toggleHud,
|
||||||
onHide: hideHud,
|
onHide: hideHud,
|
||||||
onQuit: () => app.quit(),
|
onQuit: () => app.quit(),
|
||||||
});
|
});
|
||||||
|
|
||||||
globalShortcut.register('CommandOrControl+Shift+F', showHud);
|
// Cmd+Shift+F togglet HUD
|
||||||
|
globalShortcut.register('CommandOrControl+Shift+F', toggleHud);
|
||||||
|
|
||||||
startWakeword({ onWake: showHud });
|
startWakeword({ onWake: showHud });
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
const { contextBridge, ipcRenderer } = require('electron');
|
const { contextBridge, ipcRenderer } = require('electron');
|
||||||
|
|
||||||
|
// ESC schließt HUD — funktioniert unabhängig vom HUD-JavaScript
|
||||||
|
window.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Escape') ipcRenderer.send('hide-hud');
|
||||||
|
}, true);
|
||||||
|
|
||||||
contextBridge.exposeInMainWorld('foxBridge', {
|
contextBridge.exposeInMainWorld('foxBridge', {
|
||||||
sendMessage: (text) => ipcRenderer.send('send-message', text),
|
sendMessage: (text) => ipcRenderer.send('send-message', text),
|
||||||
hideHud: () => ipcRenderer.send('hide-hud'),
|
hideHud: () => ipcRenderer.send('hide-hud'),
|
||||||
|
|
|
||||||
|
|
@ -3,22 +3,31 @@ const path = require('path');
|
||||||
|
|
||||||
let tray = null;
|
let tray = null;
|
||||||
|
|
||||||
function createTray({ onShow, onHide, onQuit }) {
|
function createTray({ onToggle, onHide, onQuit }) {
|
||||||
const iconPath = path.join(__dirname, '../assets/fox-tray.png');
|
const iconPath = path.join(__dirname, '../assets/fox-tray.png');
|
||||||
const icon = nativeImage.createFromPath(iconPath);
|
let icon = nativeImage.createFromPath(iconPath);
|
||||||
|
|
||||||
|
if (icon.isEmpty()) {
|
||||||
|
console.warn('[Tray] Icon nicht gefunden:', iconPath, '— Fallback auf leeres Icon');
|
||||||
|
// Minimales 1x1 schwarzes Template-PNG als Fallback
|
||||||
|
icon = nativeImage.createFromDataURL(
|
||||||
|
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=='
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
icon.setTemplateImage(true);
|
icon.setTemplateImage(true);
|
||||||
|
|
||||||
tray = new Tray(icon);
|
tray = new Tray(icon);
|
||||||
tray.setToolTip('Fox');
|
tray.setToolTip('Fox');
|
||||||
|
|
||||||
tray.setContextMenu(Menu.buildFromTemplate([
|
tray.setContextMenu(Menu.buildFromTemplate([
|
||||||
{ label: 'Fox öffnen', accelerator: 'Cmd+Shift+F', click: onShow },
|
{ label: 'Fox öffnen/schließen', accelerator: 'Cmd+Shift+F', click: onToggle },
|
||||||
{ label: 'Fox verstecken', click: onHide },
|
{ label: 'Fox verstecken', click: onHide },
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
{ label: 'Beenden', click: onQuit },
|
{ label: 'Beenden', click: onQuit },
|
||||||
]));
|
]));
|
||||||
|
|
||||||
tray.on('click', onShow);
|
tray.on('click', onToggle);
|
||||||
|
|
||||||
return tray;
|
return tray;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue