mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-04-10 04:19:25 -07:00
119 lines
3.5 KiB
TypeScript
119 lines
3.5 KiB
TypeScript
import { BrowserWindow, ipcMain } from 'electron';
|
|
import * as path from 'path';
|
|
import type { WindowGeometry } from '../../types.js';
|
|
import { IPC_CHANNELS } from '../../shared/ipc/contracts.js';
|
|
import {
|
|
buildStatsWindowLoadFileOptions,
|
|
buildStatsWindowOptions,
|
|
promoteStatsWindowLevel,
|
|
shouldHideStatsWindowForInput,
|
|
} from './stats-window-runtime.js';
|
|
|
|
let statsWindow: BrowserWindow | null = null;
|
|
let toggleRegistered = false;
|
|
|
|
export interface StatsWindowOptions {
|
|
/** Absolute path to stats/dist/ directory */
|
|
staticDir: string;
|
|
/** Absolute path to the compiled preload-stats.js */
|
|
preloadPath: string;
|
|
/** Resolve the active stats API base URL */
|
|
getApiBaseUrl?: () => string;
|
|
/** Resolve the active stats toggle key from config */
|
|
getToggleKey: () => string;
|
|
/** Resolve the tracked overlay/mpv bounds */
|
|
resolveBounds: () => WindowGeometry | null;
|
|
/** Notify the main process when the stats overlay becomes visible/hidden */
|
|
onVisibilityChanged?: (visible: boolean) => void;
|
|
}
|
|
|
|
function syncStatsWindowBounds(window: BrowserWindow, bounds: WindowGeometry | null): void {
|
|
if (!bounds || window.isDestroyed()) return;
|
|
window.setBounds({
|
|
x: bounds.x,
|
|
y: bounds.y,
|
|
width: bounds.width,
|
|
height: bounds.height,
|
|
});
|
|
}
|
|
|
|
function showStatsWindow(window: BrowserWindow, options: StatsWindowOptions): void {
|
|
syncStatsWindowBounds(window, options.resolveBounds());
|
|
promoteStatsWindowLevel(window);
|
|
window.show();
|
|
window.focus();
|
|
options.onVisibilityChanged?.(true);
|
|
promoteStatsWindowLevel(window);
|
|
}
|
|
|
|
/**
|
|
* Toggle the stats overlay window: create on first call, then show/hide.
|
|
* The React app stays mounted across toggles — state is preserved.
|
|
*/
|
|
export function toggleStatsOverlay(options: StatsWindowOptions): void {
|
|
if (!statsWindow) {
|
|
statsWindow = new BrowserWindow(
|
|
buildStatsWindowOptions({
|
|
preloadPath: options.preloadPath,
|
|
bounds: options.resolveBounds(),
|
|
}),
|
|
);
|
|
|
|
const indexPath = path.join(options.staticDir, 'index.html');
|
|
statsWindow.loadFile(indexPath, buildStatsWindowLoadFileOptions(options.getApiBaseUrl?.()));
|
|
|
|
statsWindow.on('closed', () => {
|
|
options.onVisibilityChanged?.(false);
|
|
statsWindow = null;
|
|
});
|
|
|
|
statsWindow.webContents.on('before-input-event', (event, input) => {
|
|
if (shouldHideStatsWindowForInput(input, options.getToggleKey())) {
|
|
event.preventDefault();
|
|
statsWindow?.hide();
|
|
options.onVisibilityChanged?.(false);
|
|
}
|
|
});
|
|
|
|
statsWindow.once('ready-to-show', () => {
|
|
if (!statsWindow) return;
|
|
showStatsWindow(statsWindow, options);
|
|
});
|
|
|
|
statsWindow.on('blur', () => {
|
|
if (!statsWindow || statsWindow.isDestroyed() || !statsWindow.isVisible()) {
|
|
return;
|
|
}
|
|
promoteStatsWindowLevel(statsWindow);
|
|
});
|
|
} else if (statsWindow.isVisible()) {
|
|
statsWindow.hide();
|
|
options.onVisibilityChanged?.(false);
|
|
} else {
|
|
showStatsWindow(statsWindow, options);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register the IPC command handler for toggling the overlay.
|
|
* Call this once during app initialization.
|
|
*/
|
|
export function registerStatsOverlayToggle(options: StatsWindowOptions): void {
|
|
if (toggleRegistered) return;
|
|
toggleRegistered = true;
|
|
ipcMain.on(IPC_CHANNELS.command.toggleStatsOverlay, () => {
|
|
toggleStatsOverlay(options);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Clean up — destroy the stats window if it exists.
|
|
* Call during app quit.
|
|
*/
|
|
export function destroyStatsWindow(): void {
|
|
if (statsWindow && !statsWindow.isDestroyed()) {
|
|
statsWindow.destroy();
|
|
statsWindow = null;
|
|
}
|
|
}
|