Fix: Electron Screen-Picker nutzt jetzt temp-Datei statt data: URL
- data:text/html URLs werden in Electron BrowserWindows blockiert - Picker-HTML wird jetzt als temp-Datei geschrieben und via loadFile geladen - IPC-Kommunikation statt page-title-updated für die Source-Auswahl - nodeIntegration nur im Picker-Fenster aktiviert (nicht im Hauptfenster) - Temp-Datei wird nach Auswahl/Abbruch automatisch gelöscht Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
5f756a5404
commit
2a81bc216b
1 changed files with 58 additions and 29 deletions
|
|
@ -1,5 +1,7 @@
|
||||||
const { app, BrowserWindow, session, shell, desktopCapturer, autoUpdater, dialog, ipcMain, Notification } = require('electron');
|
const { app, BrowserWindow, session, shell, desktopCapturer, autoUpdater, dialog, ipcMain, Notification } = require('electron');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
const os = require('os');
|
||||||
const { setupAdBlocker } = require('./ad-blocker');
|
const { setupAdBlocker } = require('./ad-blocker');
|
||||||
|
|
||||||
// Handle Squirrel events (Windows installer)
|
// Handle Squirrel events (Windows installer)
|
||||||
|
|
@ -93,6 +95,9 @@ function createWindow() {
|
||||||
setupAdBlocker(session.defaultSession);
|
setupAdBlocker(session.defaultSession);
|
||||||
|
|
||||||
// Enable screen capture (getDisplayMedia) in Electron — always show picker
|
// Enable screen capture (getDisplayMedia) in Electron — always show picker
|
||||||
|
// IPC channel for picker result
|
||||||
|
const PICKER_CHANNEL = 'screen-picker-result';
|
||||||
|
|
||||||
session.defaultSession.setDisplayMediaRequestHandler(async (_request, callback) => {
|
session.defaultSession.setDisplayMediaRequestHandler(async (_request, callback) => {
|
||||||
const sources = await desktopCapturer.getSources({ types: ['screen', 'window'], thumbnailSize: { width: 320, height: 180 } });
|
const sources = await desktopCapturer.getSources({ types: ['screen', 'window'], thumbnailSize: { width: 320, height: 180 } });
|
||||||
if (sources.length === 0) {
|
if (sources.length === 0) {
|
||||||
|
|
@ -100,28 +105,14 @@ function createWindow() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build a picker window so the user always chooses
|
|
||||||
const picker = new BrowserWindow({
|
|
||||||
width: 680,
|
|
||||||
height: 520,
|
|
||||||
parent: mainWindow,
|
|
||||||
modal: true,
|
|
||||||
resizable: false,
|
|
||||||
minimizable: false,
|
|
||||||
maximizable: false,
|
|
||||||
title: 'Bildschirm / Fenster ausw\u00e4hlen',
|
|
||||||
backgroundColor: '#1a1b1e',
|
|
||||||
autoHideMenuBar: true,
|
|
||||||
webPreferences: { contextIsolation: true, nodeIntegration: false },
|
|
||||||
});
|
|
||||||
|
|
||||||
const sourceData = sources.map(s => ({
|
const sourceData = sources.map(s => ({
|
||||||
id: s.id,
|
id: s.id,
|
||||||
name: s.name,
|
name: s.name,
|
||||||
thumbnail: s.thumbnail.toDataURL(),
|
thumbnail: s.thumbnail.toDataURL(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const html = `<!DOCTYPE html>
|
// Write picker HTML to a temp file (data: URLs are blocked in Electron)
|
||||||
|
const pickerHtml = `<!DOCTYPE html>
|
||||||
<html><head><meta charset="utf-8"><style>
|
<html><head><meta charset="utf-8"><style>
|
||||||
*{margin:0;padding:0;box-sizing:border-box}
|
*{margin:0;padding:0;box-sizing:border-box}
|
||||||
body{background:#1a1b1e;color:#e0e0e0;font-family:system-ui,sans-serif;padding:16px;overflow-y:auto}
|
body{background:#1a1b1e;color:#e0e0e0;font-family:system-ui,sans-serif;padding:16px;overflow-y:auto}
|
||||||
|
|
@ -135,32 +126,63 @@ h2{font-size:16px;margin-bottom:12px;color:#ccc}
|
||||||
.cancel-btn{background:#3a3a4e;color:#e0e0e0;border:none;padding:8px 24px;border-radius:6px;cursor:pointer;font-size:14px}
|
.cancel-btn{background:#3a3a4e;color:#e0e0e0;border:none;padding:8px 24px;border-radius:6px;cursor:pointer;font-size:14px}
|
||||||
.cancel-btn:hover{background:#4a4a5e}
|
.cancel-btn:hover{background:#4a4a5e}
|
||||||
</style></head><body>
|
</style></head><body>
|
||||||
<h2>Bildschirm oder Fenster ausw\u00e4hlen</h2>
|
<h2>Bildschirm oder Fenster w\\u00e4hlen</h2>
|
||||||
<div class="grid" id="grid"></div>
|
<div class="grid" id="grid"></div>
|
||||||
<div class="cancel-row"><button class="cancel-btn" onclick="choose(null)">Abbrechen</button></div>
|
<div class="cancel-row"><button class="cancel-btn" id="cancelBtn">Abbrechen</button></div>
|
||||||
<script>
|
<script>
|
||||||
const sources = ${JSON.stringify(sourceData)};
|
const sources = ${JSON.stringify(sourceData)};
|
||||||
const grid = document.getElementById('grid');
|
const grid = document.getElementById('grid');
|
||||||
sources.forEach(s => {
|
sources.forEach(s => {
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
div.className = 'item';
|
div.className = 'item';
|
||||||
div.innerHTML = '<img src="' + s.thumbnail + '"><div class="label">' + s.name.replace(/</g,'<') + '</div>';
|
const img = document.createElement('img');
|
||||||
div.onclick = () => choose(s.id);
|
img.src = s.thumbnail;
|
||||||
|
div.appendChild(img);
|
||||||
|
const label = document.createElement('div');
|
||||||
|
label.className = 'label';
|
||||||
|
label.textContent = s.name;
|
||||||
|
div.appendChild(label);
|
||||||
|
div.addEventListener('click', () => {
|
||||||
|
require('electron').ipcRenderer.send('${PICKER_CHANNEL}', s.id);
|
||||||
|
});
|
||||||
grid.appendChild(div);
|
grid.appendChild(div);
|
||||||
});
|
});
|
||||||
function choose(id) {
|
document.getElementById('cancelBtn').addEventListener('click', () => {
|
||||||
document.title = 'PICK:' + (id || '');
|
require('electron').ipcRenderer.send('${PICKER_CHANNEL}', null);
|
||||||
}
|
});
|
||||||
</script></body></html>`;
|
</script></body></html>`;
|
||||||
|
|
||||||
picker.loadURL('data:text/html;charset=utf-8,' + encodeURIComponent(html));
|
const tmpFile = path.join(os.tmpdir(), 'gaming-hub-screen-picker.html');
|
||||||
|
fs.writeFileSync(tmpFile, pickerHtml, 'utf-8');
|
||||||
|
|
||||||
|
const picker = new BrowserWindow({
|
||||||
|
width: 680,
|
||||||
|
height: 520,
|
||||||
|
parent: mainWindow,
|
||||||
|
modal: true,
|
||||||
|
resizable: false,
|
||||||
|
minimizable: false,
|
||||||
|
maximizable: false,
|
||||||
|
title: 'Bildschirm / Fenster w\u00e4hlen',
|
||||||
|
backgroundColor: '#1a1b1e',
|
||||||
|
autoHideMenuBar: true,
|
||||||
|
webPreferences: {
|
||||||
|
contextIsolation: false,
|
||||||
|
nodeIntegration: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
picker.loadFile(tmpFile);
|
||||||
|
|
||||||
let resolved = false;
|
let resolved = false;
|
||||||
picker.webContents.on('page-title-updated', (_ev, title) => {
|
|
||||||
if (!title.startsWith('PICK:')) return;
|
const onPickerResult = (_event, selectedId) => {
|
||||||
|
if (resolved) return;
|
||||||
resolved = true;
|
resolved = true;
|
||||||
const selectedId = title.slice(5);
|
ipcMain.removeListener(PICKER_CHANNEL, onPickerResult);
|
||||||
picker.close();
|
picker.close();
|
||||||
|
try { fs.unlinkSync(tmpFile); } catch {}
|
||||||
|
|
||||||
if (!selectedId) {
|
if (!selectedId) {
|
||||||
callback({});
|
callback({});
|
||||||
return;
|
return;
|
||||||
|
|
@ -171,10 +193,17 @@ function choose(id) {
|
||||||
} else {
|
} else {
|
||||||
callback({});
|
callback({});
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
|
ipcMain.on(PICKER_CHANNEL, onPickerResult);
|
||||||
|
|
||||||
picker.on('closed', () => {
|
picker.on('closed', () => {
|
||||||
if (!resolved) callback({});
|
try { fs.unlinkSync(tmpFile); } catch {}
|
||||||
|
if (!resolved) {
|
||||||
|
resolved = true;
|
||||||
|
ipcMain.removeListener(PICKER_CHANNEL, onPickerResult);
|
||||||
|
callback({});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue