Compare commits
3 Commits
bb51771dc7
...
2c7b609295
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c7b609295 | ||
|
|
1008a55ec3 | ||
|
|
c8fdb8b1e7 |
71
dist/main/system_handlers.js
vendored
71
dist/main/system_handlers.js
vendored
@@ -132,12 +132,25 @@ function registerSystemHandlers() {
|
|||||||
headers['x-account-id'] = account_id;
|
headers['x-account-id'] = account_id;
|
||||||
try {
|
try {
|
||||||
const response = await (0, axios_1.default)({ method: 'get', url: image_url, responseType: 'stream', headers });
|
const response = await (0, axios_1.default)({ method: 'get', url: image_url, responseType: 'stream', headers });
|
||||||
|
const content_type = (response.headers['content-type'] ?? '');
|
||||||
|
if (!content_type.startsWith('image/')) {
|
||||||
|
response.data.destroy();
|
||||||
|
return { success: false, error: `URL did not return an image (Content-Type: ${content_type})` };
|
||||||
|
}
|
||||||
const writer = fs.createWriteStream(dest_path);
|
const writer = fs.createWriteStream(dest_path);
|
||||||
response.data.pipe(writer);
|
response.data.pipe(writer);
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
writer.on('finish', resolve);
|
writer.on('finish', resolve);
|
||||||
writer.on('error', reject);
|
writer.on('error', reject);
|
||||||
});
|
});
|
||||||
|
const file_size = fs.statSync(dest_path).size;
|
||||||
|
if (file_size === 0) {
|
||||||
|
try {
|
||||||
|
fs.unlinkSync(dest_path);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
return { success: false, error: 'Wallpaper download incomplete (0 bytes)' };
|
||||||
|
}
|
||||||
return { success: true, path: dest_path };
|
return { success: true, path: dest_path };
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
@@ -185,6 +198,12 @@ function registerSystemHandlers() {
|
|||||||
return { success: false, error: result.error };
|
return { success: false, error: result.error };
|
||||||
primary_path = result.path;
|
primary_path = result.path;
|
||||||
}
|
}
|
||||||
|
if (!primary_path && url_external && display === 'external') {
|
||||||
|
const ext_result = await download_wallpaper_image(url_external, 'wallpaper_external');
|
||||||
|
if (!ext_result.success || !ext_result.path)
|
||||||
|
return { success: false, error: ext_result.error };
|
||||||
|
return await apply_mac_wallpaper(ext_result.path, 'external');
|
||||||
|
}
|
||||||
if (!primary_path)
|
if (!primary_path)
|
||||||
return { success: false, error: 'No image source provided' };
|
return { success: false, error: 'No image source provided' };
|
||||||
if (url_external) {
|
if (url_external) {
|
||||||
@@ -193,26 +212,50 @@ function registerSystemHandlers() {
|
|||||||
if (!primary_result.success)
|
if (!primary_result.success)
|
||||||
return primary_result;
|
return primary_result;
|
||||||
const ext_result = await download_wallpaper_image(url_external, 'wallpaper_external');
|
const ext_result = await download_wallpaper_image(url_external, 'wallpaper_external');
|
||||||
const ext_path = ext_result.success && ext_result.path ? ext_result.path : primary_path;
|
if (!ext_result.success || !ext_result.path)
|
||||||
return await apply_mac_wallpaper(ext_path, 'external');
|
return { success: false, error: ext_result.error };
|
||||||
|
return await apply_mac_wallpaper(ext_result.path, 'external');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return await apply_mac_wallpaper(primary_path, display);
|
return await apply_mac_wallpaper(primary_path, display);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (os.platform() === 'linux') {
|
if (os.platform() === 'linux') {
|
||||||
let img_path = imagePath ? (0, file_utils_1.expandPath)(imagePath) : null;
|
// Dev test mode: never touch the desktop on Linux. Running gsettings during
|
||||||
if (!img_path && url) {
|
// development would reset the dev workstation monitors on every test cycle.
|
||||||
const result = await download_wallpaper_image(url, 'wallpaper_primary');
|
// Return what would have run so the Svelte side can show a debug popup.
|
||||||
if (!result.success || !result.path)
|
const would_run = [];
|
||||||
return { success: false, error: result.error };
|
const cache_dir = wallpaper_cache_dir;
|
||||||
img_path = result.path;
|
if (!imagePath && !url && !url_external) {
|
||||||
}
|
|
||||||
if (!img_path)
|
|
||||||
return { success: false, error: 'No image source provided' };
|
return { success: false, error: 'No image source provided' };
|
||||||
if (!fs.existsSync(img_path))
|
}
|
||||||
return { success: false, error: 'Image file not found' };
|
if (imagePath) {
|
||||||
return await runExec(`gsettings set org.gnome.desktop.background picture-uri "file://${img_path}"`);
|
const clean = (0, file_utils_1.expandPath)(imagePath);
|
||||||
|
if (!fs.existsSync(clean))
|
||||||
|
return { success: false, error: 'Image file not found' };
|
||||||
|
}
|
||||||
|
if (url)
|
||||||
|
would_run.push(`download: ${url}\n → ${path.join(cache_dir, 'wallpaper_primary.jpg')}`);
|
||||||
|
if (url_external)
|
||||||
|
would_run.push(`download: ${url_external}\n → ${path.join(cache_dir, 'wallpaper_external.jpg')}`);
|
||||||
|
if (imagePath) {
|
||||||
|
would_run.push(`gsettings set org.gnome.desktop.background picture-uri "file://${(0, file_utils_1.expandPath)(imagePath)}"`);
|
||||||
|
}
|
||||||
|
else if (url) {
|
||||||
|
would_run.push(`gsettings set org.gnome.desktop.background picture-uri "file://${path.join(cache_dir, 'wallpaper_primary.jpg')}"`);
|
||||||
|
}
|
||||||
|
if (url_external) {
|
||||||
|
would_run.push(`(external display: gsettings has no per-display wallpaper support)`);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
linux_test_mode: true,
|
||||||
|
platform: 'linux',
|
||||||
|
display,
|
||||||
|
url: url ?? null,
|
||||||
|
url_external: url_external ?? null,
|
||||||
|
would_run
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return { success: false, error: 'Platform not supported' };
|
return { success: false, error: 'Platform not supported' };
|
||||||
});
|
});
|
||||||
@@ -241,7 +284,7 @@ function registerSystemHandlers() {
|
|||||||
}
|
}
|
||||||
if (!cmd)
|
if (!cmd)
|
||||||
return { success: false, error: 'Action not supported' };
|
return { success: false, error: 'Action not supported' };
|
||||||
// NOTE: These commands often require root.
|
// NOTE: These commands often require root.
|
||||||
// For a kiosk, you might configure sudoers to allow this specific command without password.
|
// For a kiosk, you might configure sudoers to allow this specific command without password.
|
||||||
return await runExec(cmd);
|
return await runExec(cmd);
|
||||||
});
|
});
|
||||||
|
|||||||
2
dist/main/system_handlers.js.map
vendored
2
dist/main/system_handlers.js.map
vendored
File diff suppressed because one or more lines are too long
@@ -23,7 +23,7 @@ const runExec = (cmd: string): Promise<{ success: boolean; stdout?: string; stde
|
|||||||
let recordingProcess: any = null;
|
let recordingProcess: any = null;
|
||||||
|
|
||||||
export function registerSystemHandlers() {
|
export function registerSystemHandlers() {
|
||||||
|
|
||||||
// 1. Window Control
|
// 1. Window Control
|
||||||
ipcMain.handle('native:window-control', async (event, { action, value }) => {
|
ipcMain.handle('native:window-control', async (event, { action, value }) => {
|
||||||
const win = BrowserWindow.fromWebContents(event.sender);
|
const win = BrowserWindow.fromWebContents(event.sender);
|
||||||
@@ -35,7 +35,7 @@ export function registerSystemHandlers() {
|
|||||||
case 'minimize': win.minimize(); break;
|
case 'minimize': win.minimize(); break;
|
||||||
case 'restore': win.restore(); break;
|
case 'restore': win.restore(); break;
|
||||||
case 'close': win.close(); break;
|
case 'close': win.close(); break;
|
||||||
case 'devtools':
|
case 'devtools':
|
||||||
if (value) win.webContents.openDevTools();
|
if (value) win.webContents.openDevTools();
|
||||||
else win.webContents.closeDevTools();
|
else win.webContents.closeDevTools();
|
||||||
break;
|
break;
|
||||||
@@ -96,12 +96,23 @@ export function registerSystemHandlers() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios({ method: 'get', url: image_url, responseType: 'stream', headers });
|
const response = await axios({ method: 'get', url: image_url, responseType: 'stream', headers });
|
||||||
|
const content_type = (response.headers['content-type'] ?? '') as string;
|
||||||
|
if (!content_type.startsWith('image/')) {
|
||||||
|
response.data.destroy();
|
||||||
|
return { success: false, error: `URL did not return an image (Content-Type: ${content_type})` };
|
||||||
|
}
|
||||||
|
|
||||||
const writer = fs.createWriteStream(dest_path);
|
const writer = fs.createWriteStream(dest_path);
|
||||||
response.data.pipe(writer);
|
response.data.pipe(writer);
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
writer.on('finish', resolve);
|
writer.on('finish', resolve);
|
||||||
writer.on('error', reject);
|
writer.on('error', reject);
|
||||||
});
|
});
|
||||||
|
const file_size = fs.statSync(dest_path).size;
|
||||||
|
if (file_size === 0) {
|
||||||
|
try { fs.unlinkSync(dest_path); } catch {}
|
||||||
|
return { success: false, error: 'Wallpaper download incomplete (0 bytes)' };
|
||||||
|
}
|
||||||
return { success: true, path: dest_path };
|
return { success: true, path: dest_path };
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
return { success: false, error: `Wallpaper download failed: ${e.message}` };
|
return { success: false, error: `Wallpaper download failed: ${e.message}` };
|
||||||
@@ -144,6 +155,12 @@ export function registerSystemHandlers() {
|
|||||||
primary_path = result.path;
|
primary_path = result.path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!primary_path && url_external && display === 'external') {
|
||||||
|
const ext_result = await download_wallpaper_image(url_external, 'wallpaper_external');
|
||||||
|
if (!ext_result.success || !ext_result.path) return { success: false, error: ext_result.error };
|
||||||
|
return await apply_mac_wallpaper(ext_result.path, 'external');
|
||||||
|
}
|
||||||
|
|
||||||
if (!primary_path) return { success: false, error: 'No image source provided' };
|
if (!primary_path) return { success: false, error: 'No image source provided' };
|
||||||
|
|
||||||
if (url_external) {
|
if (url_external) {
|
||||||
@@ -152,26 +169,50 @@ export function registerSystemHandlers() {
|
|||||||
if (!primary_result.success) return primary_result;
|
if (!primary_result.success) return primary_result;
|
||||||
|
|
||||||
const ext_result = await download_wallpaper_image(url_external, 'wallpaper_external');
|
const ext_result = await download_wallpaper_image(url_external, 'wallpaper_external');
|
||||||
const ext_path = ext_result.success && ext_result.path ? ext_result.path : primary_path;
|
if (!ext_result.success || !ext_result.path) return { success: false, error: ext_result.error };
|
||||||
return await apply_mac_wallpaper(ext_path, 'external');
|
return await apply_mac_wallpaper(ext_result.path, 'external');
|
||||||
} else {
|
} else {
|
||||||
return await apply_mac_wallpaper(primary_path, display);
|
return await apply_mac_wallpaper(primary_path, display);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (os.platform() === 'linux') {
|
if (os.platform() === 'linux') {
|
||||||
let img_path: string | null = imagePath ? expandPath(imagePath) : null;
|
// Dev test mode: never touch the desktop on Linux. Running gsettings during
|
||||||
|
// development would reset the dev workstation monitors on every test cycle.
|
||||||
|
// Return what would have run so the Svelte side can show a debug popup.
|
||||||
|
const would_run: string[] = [];
|
||||||
|
const cache_dir = wallpaper_cache_dir;
|
||||||
|
|
||||||
if (!img_path && url) {
|
if (!imagePath && !url && !url_external) {
|
||||||
const result = await download_wallpaper_image(url, 'wallpaper_primary');
|
return { success: false, error: 'No image source provided' };
|
||||||
if (!result.success || !result.path) return { success: false, error: result.error };
|
|
||||||
img_path = result.path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!img_path) return { success: false, error: 'No image source provided' };
|
if (imagePath) {
|
||||||
if (!fs.existsSync(img_path)) return { success: false, error: 'Image file not found' };
|
const clean = expandPath(imagePath);
|
||||||
|
if (!fs.existsSync(clean)) return { success: false, error: 'Image file not found' };
|
||||||
|
}
|
||||||
|
|
||||||
return await runExec(`gsettings set org.gnome.desktop.background picture-uri "file://${img_path}"`);
|
if (url) would_run.push(`download: ${url}\n → ${path.join(cache_dir, 'wallpaper_primary.jpg')}`);
|
||||||
|
if (url_external) would_run.push(`download: ${url_external}\n → ${path.join(cache_dir, 'wallpaper_external.jpg')}`);
|
||||||
|
|
||||||
|
if (imagePath) {
|
||||||
|
would_run.push(`gsettings set org.gnome.desktop.background picture-uri "file://${expandPath(imagePath)}"`);
|
||||||
|
} else if (url) {
|
||||||
|
would_run.push(`gsettings set org.gnome.desktop.background picture-uri "file://${path.join(cache_dir, 'wallpaper_primary.jpg')}"`);
|
||||||
|
}
|
||||||
|
if (url_external) {
|
||||||
|
would_run.push(`(external display: gsettings has no per-display wallpaper support)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
linux_test_mode: true,
|
||||||
|
platform: 'linux',
|
||||||
|
display,
|
||||||
|
url: url ?? null,
|
||||||
|
url_external: url_external ?? null,
|
||||||
|
would_run
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return { success: false, error: 'Platform not supported' };
|
return { success: false, error: 'Platform not supported' };
|
||||||
@@ -195,8 +236,8 @@ export function registerSystemHandlers() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!cmd) return { success: false, error: 'Action not supported' };
|
if (!cmd) return { success: false, error: 'Action not supported' };
|
||||||
|
|
||||||
// NOTE: These commands often require root.
|
// NOTE: These commands often require root.
|
||||||
// For a kiosk, you might configure sudoers to allow this specific command without password.
|
// For a kiosk, you might configure sudoers to allow this specific command without password.
|
||||||
return await runExec(cmd);
|
return await runExec(cmd);
|
||||||
});
|
});
|
||||||
@@ -216,7 +257,7 @@ export function registerSystemHandlers() {
|
|||||||
return await runExec(`firefox "${url}"`);
|
return await runExec(`firefox "${url}"`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default system handler
|
// Default system handler
|
||||||
await shell.openExternal(url);
|
await shell.openExternal(url);
|
||||||
return { success: true };
|
return { success: true };
|
||||||
@@ -225,30 +266,30 @@ export function registerSystemHandlers() {
|
|||||||
// 5. Manage Recording (Aperture Wrapper)
|
// 5. Manage Recording (Aperture Wrapper)
|
||||||
ipcMain.handle('native:manage-recording', async (event, { action, options }) => {
|
ipcMain.handle('native:manage-recording', async (event, { action, options }) => {
|
||||||
if (os.platform() !== 'darwin') return { success: false, error: 'Recording only supported on macOS' };
|
if (os.platform() !== 'darwin') return { success: false, error: 'Recording only supported on macOS' };
|
||||||
|
|
||||||
// Path to bundled aperture binary
|
// Path to bundled aperture binary
|
||||||
// In dev: ./resources/bin/aperture
|
// In dev: ./resources/bin/aperture
|
||||||
// In prod: process.resourcesPath/bin/aperture
|
// In prod: process.resourcesPath/bin/aperture
|
||||||
const binPath = app.isPackaged
|
const binPath = app.isPackaged
|
||||||
? path.join(process.resourcesPath, 'bin', 'aperture')
|
? path.join(process.resourcesPath, 'bin', 'aperture')
|
||||||
: path.join(__dirname, '../../resources/bin/aperture'); // Adjust based on structure
|
: path.join(__dirname, '../../resources/bin/aperture'); // Adjust based on structure
|
||||||
|
|
||||||
if (action === 'start') {
|
if (action === 'start') {
|
||||||
if (recordingProcess) return { success: false, error: 'Recording already in progress' };
|
if (recordingProcess) return { success: false, error: 'Recording already in progress' };
|
||||||
|
|
||||||
const { fps = 30, audioDeviceId, output } = options || {};
|
const { fps = 30, audioDeviceId, output } = options || {};
|
||||||
const cleanOutput = expandPath(output || '~/tmp/recording.mp4');
|
const cleanOutput = expandPath(output || '~/tmp/recording.mp4');
|
||||||
|
|
||||||
const args = ['run', '--fps', fps, '--output', cleanOutput];
|
const args = ['run', '--fps', fps, '--output', cleanOutput];
|
||||||
if (audioDeviceId) args.push('--audio-device-id', audioDeviceId);
|
if (audioDeviceId) args.push('--audio-device-id', audioDeviceId);
|
||||||
|
|
||||||
// Spawn process
|
// Spawn process
|
||||||
// Note: aperture is a CLI tool. We might need 'aperture' node package or the binary.
|
// Note: aperture is a CLI tool. We might need 'aperture' node package or the binary.
|
||||||
// Assuming binary usage here.
|
// Assuming binary usage here.
|
||||||
try {
|
try {
|
||||||
console.log(`Starting recording: ${binPath} ${args.join(' ')}`);
|
console.log(`Starting recording: ${binPath} ${args.join(' ')}`);
|
||||||
recordingProcess = spawn(binPath, args);
|
recordingProcess = spawn(binPath, args);
|
||||||
|
|
||||||
recordingProcess.on('error', (err: any) => {
|
recordingProcess.on('error', (err: any) => {
|
||||||
console.error('Recording error:', err);
|
console.error('Recording error:', err);
|
||||||
recordingProcess = null;
|
recordingProcess = null;
|
||||||
@@ -266,7 +307,7 @@ export function registerSystemHandlers() {
|
|||||||
|
|
||||||
} else if (action === 'stop') {
|
} else if (action === 'stop') {
|
||||||
if (!recordingProcess) return { success: false, error: 'No recording in progress' };
|
if (!recordingProcess) return { success: false, error: 'No recording in progress' };
|
||||||
|
|
||||||
recordingProcess.kill('SIGINT'); // Send interrupt to stop cleanly
|
recordingProcess.kill('SIGINT'); // Send interrupt to stop cleanly
|
||||||
recordingProcess = null;
|
recordingProcess = null;
|
||||||
return { success: true };
|
return { success: true };
|
||||||
@@ -280,13 +321,13 @@ export function registerSystemHandlers() {
|
|||||||
// 6. Set Display Layout (Displayplacer)
|
// 6. Set Display Layout (Displayplacer)
|
||||||
ipcMain.handle('native:set-display-layout', async (event, { mode, configStr }) => {
|
ipcMain.handle('native:set-display-layout', async (event, { mode, configStr }) => {
|
||||||
if (os.platform() !== 'darwin') return { success: false, error: 'Display control only supported on macOS' };
|
if (os.platform() !== 'darwin') return { success: false, error: 'Display control only supported on macOS' };
|
||||||
|
|
||||||
const binPath = app.isPackaged
|
const binPath = app.isPackaged
|
||||||
? path.join(process.resourcesPath, 'bin', 'displayplacer')
|
? path.join(process.resourcesPath, 'bin', 'displayplacer')
|
||||||
: path.join(__dirname, '../../resources/bin/displayplacer');
|
: path.join(__dirname, '../../resources/bin/displayplacer');
|
||||||
|
|
||||||
let cmd = '';
|
let cmd = '';
|
||||||
|
|
||||||
if (mode === 'mirror') {
|
if (mode === 'mirror') {
|
||||||
// This usually requires querying current IDs, which is complex.
|
// This usually requires querying current IDs, which is complex.
|
||||||
// If configStr is provided (output of 'displayplacer list'), use it.
|
// If configStr is provided (output of 'displayplacer list'), use it.
|
||||||
@@ -304,7 +345,7 @@ export function registerSystemHandlers() {
|
|||||||
if (cmd) {
|
if (cmd) {
|
||||||
return await runExec(cmd);
|
return await runExec(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { success: false, error: 'Invalid mode or missing config' };
|
return { success: false, error: 'Invalid mode or missing config' };
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -324,10 +365,10 @@ export function registerSystemHandlers() {
|
|||||||
url: url,
|
url: url,
|
||||||
responseType: 'stream'
|
responseType: 'stream'
|
||||||
});
|
});
|
||||||
|
|
||||||
const writer = fs.createWriteStream(destPath);
|
const writer = fs.createWriteStream(destPath);
|
||||||
response.data.pipe(writer);
|
response.data.pipe(writer);
|
||||||
|
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
writer.on('finish', () => resolve(true));
|
writer.on('finish', () => resolve(true));
|
||||||
writer.on('error', reject);
|
writer.on('error', reject);
|
||||||
@@ -351,9 +392,9 @@ export function registerSystemHandlers() {
|
|||||||
// Real implementation depends on OS and packaging format.
|
// Real implementation depends on OS and packaging format.
|
||||||
// macOS: Mount DMG, copy .app to /Applications? Or Unzip .app?
|
// macOS: Mount DMG, copy .app to /Applications? Or Unzip .app?
|
||||||
// Linux: chmod +x AppImage and move?
|
// Linux: chmod +x AppImage and move?
|
||||||
|
|
||||||
console.log(`Ready to install update from: ${updateFile}`);
|
console.log(`Ready to install update from: ${updateFile}`);
|
||||||
|
|
||||||
// For now, just return success so the UI knows we "downloaded" it.
|
// For now, just return success so the UI knows we "downloaded" it.
|
||||||
return { success: true, message: 'Update downloaded/located. Installation logic requires packaging specifics.', downloadedPath: updateFile };
|
return { success: true, message: 'Update downloaded/located. Installation logic requires packaging specifics.', downloadedPath: updateFile };
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user