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
boksbc 2026-05-11 22:34:44 +02:00
parent 1189f6000c
commit dc7e888ee3
4 changed files with 50 additions and 14 deletions

View File

@ -5,6 +5,7 @@ const HUD_URL = process.env.FOX_HUD_URL || 'https://fox.pxo.at/hud';
let hudWindow = null;
let hudLoaded = false;
let pendingShow = false;
let hudActive = false;
function createHud() {
const { width, height } = screen.getPrimaryDisplay().workAreaSize;
@ -16,7 +17,8 @@ function createHud() {
y: 0,
frame: false,
transparent: true,
alwaysOnTop: true,
backgroundColor: '#00000000',
alwaysOnTop: false,
show: false,
skipTaskbar: true,
hasShadow: false,
@ -29,14 +31,14 @@ function createHud() {
},
});
hudWindow.setAlwaysOnTop(true, 'screen-saver');
hudWindow.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true });
// Unsichtbar und click-through bis aktiv
hudWindow.setIgnoreMouseEvents(true);
hudWindow.webContents.once('did-finish-load', () => {
hudLoaded = true;
if (pendingShow) {
pendingShow = false;
hudWindow.webContents.send('hud-show');
_activateHud();
}
});
@ -46,28 +48,47 @@ function createHud() {
hudWindow = null;
hudLoaded = false;
pendingShow = false;
hudActive = false;
});
return hudWindow;
}
function _activateHud() {
hudActive = true;
hudWindow.setIgnoreMouseEvents(false);
hudWindow.setAlwaysOnTop(true, 'screen-saver');
hudWindow.webContents.send('hud-show');
}
function showHud() {
if (!hudWindow) createHud();
hudWindow.show();
hudWindow.focus();
if (hudLoaded) {
hudWindow.webContents.send('hud-show');
_activateHud();
} else {
pendingShow = true;
}
}
function hideHud() {
if (!hudWindow) return;
if (!hudWindow || !hudActive) return;
hudActive = false;
hudWindow.setAlwaysOnTop(false);
hudWindow.setIgnoreMouseEvents(true);
hudWindow.webContents.send('hud-hide');
setTimeout(() => {
if (hudWindow) hudWindow.hide();
}, 600);
}
module.exports = { createHud, showHud, hideHud };
function toggleHud() {
if (hudActive) {
hideHud();
} else {
showHud();
}
}
module.exports = { createHud, showHud, hideHud, toggleHud };

View File

@ -1,6 +1,6 @@
const { app, ipcMain, globalShortcut, nativeTheme } = require('electron');
const { createTray } = require('./tray');
const { createHud, showHud, hideHud } = require('./hud');
const { createHud, showHud, hideHud, toggleHud } = require('./hud');
const { startWakeword, stopWakeword } = require('./wakeword');
const { executeMacCommand } = require('./mac-agent');
const axios = require('axios');
@ -19,12 +19,13 @@ app.whenReady().then(() => {
createHud();
createTray({
onShow: showHud,
onToggle: toggleHud,
onHide: hideHud,
onQuit: () => app.quit(),
});
globalShortcut.register('CommandOrControl+Shift+F', showHud);
// Cmd+Shift+F togglet HUD
globalShortcut.register('CommandOrControl+Shift+F', toggleHud);
startWakeword({ onWake: showHud });

View File

@ -1,5 +1,10 @@
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', {
sendMessage: (text) => ipcRenderer.send('send-message', text),
hideHud: () => ipcRenderer.send('hide-hud'),

View File

@ -3,22 +3,31 @@ const path = require('path');
let tray = null;
function createTray({ onShow, onHide, onQuit }) {
function createTray({ onToggle, onHide, onQuit }) {
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);
tray = new Tray(icon);
tray.setToolTip('Fox');
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 },
{ type: 'separator' },
{ label: 'Beenden', click: onQuit },
]));
tray.on('click', onShow);
tray.on('click', onToggle);
return tray;
}