uiohook mouse integration

This commit is contained in:
Siddharth
2025-10-13 15:44:56 -07:00
parent 7428afaa6d
commit 240794b2b1
12 changed files with 1613 additions and 231 deletions
+183 -69
View File
@@ -1,19 +1,13 @@
import { app, BrowserWindow, ipcMain, desktopCapturer, screen } from "electron";
import { createRequire } from "node:module";
import { BrowserWindow, screen, ipcMain, desktopCapturer, app } from "electron";
import { fileURLToPath } from "node:url";
import path from "node:path";
createRequire(import.meta.url);
const __dirname = path.dirname(fileURLToPath(import.meta.url));
process.env.APP_ROOT = path.join(__dirname, "..");
const VITE_DEV_SERVER_URL = process.env["VITE_DEV_SERVER_URL"];
const MAIN_DIST = path.join(process.env.APP_ROOT, "dist-electron");
const RENDERER_DIST = path.join(process.env.APP_ROOT, "dist");
process.env.VITE_PUBLIC = VITE_DEV_SERVER_URL ? path.join(process.env.APP_ROOT, "public") : RENDERER_DIST;
let win;
let sourceSelectorWindow = null;
let selectedSource = null;
import { uIOhook } from "uiohook-napi";
const __dirname$1 = path.dirname(fileURLToPath(import.meta.url));
const APP_ROOT = path.join(__dirname$1, "..");
const VITE_DEV_SERVER_URL$1 = process.env["VITE_DEV_SERVER_URL"];
const RENDERER_DIST$1 = path.join(APP_ROOT, "dist");
function createHudOverlayWindow() {
win = new BrowserWindow({
const win = new BrowserWindow({
width: 250,
height: 80,
minWidth: 250,
@@ -27,26 +21,26 @@ function createHudOverlayWindow() {
skipTaskbar: true,
hasShadow: false,
webPreferences: {
preload: path.join(__dirname, "preload.mjs"),
preload: path.join(__dirname$1, "preload.mjs"),
nodeIntegration: false,
contextIsolation: true,
backgroundThrottling: false
}
});
win.setResizable(false);
win.webContents.on("did-finish-load", () => {
win == null ? void 0 : win.webContents.send("main-process-message", (/* @__PURE__ */ new Date()).toLocaleString());
});
if (VITE_DEV_SERVER_URL) {
win.loadURL(VITE_DEV_SERVER_URL + "?windowType=hud-overlay");
if (VITE_DEV_SERVER_URL$1) {
win.loadURL(VITE_DEV_SERVER_URL$1 + "?windowType=hud-overlay");
} else {
win.loadFile(path.join(RENDERER_DIST, "index.html"), {
win.loadFile(path.join(RENDERER_DIST$1, "index.html"), {
query: { windowType: "hud-overlay" }
});
}
return win;
}
function createEditorWindow() {
win = new BrowserWindow({
const win = new BrowserWindow({
width: 1200,
height: 800,
minWidth: 800,
@@ -57,7 +51,7 @@ function createEditorWindow() {
alwaysOnTop: false,
skipTaskbar: false,
webPreferences: {
preload: path.join(__dirname, "preload.mjs"),
preload: path.join(__dirname$1, "preload.mjs"),
nodeIntegration: false,
contextIsolation: true
}
@@ -65,17 +59,18 @@ function createEditorWindow() {
win.webContents.on("did-finish-load", () => {
win == null ? void 0 : win.webContents.send("main-process-message", (/* @__PURE__ */ new Date()).toLocaleString());
});
if (VITE_DEV_SERVER_URL) {
win.loadURL(VITE_DEV_SERVER_URL + "?windowType=editor");
if (VITE_DEV_SERVER_URL$1) {
win.loadURL(VITE_DEV_SERVER_URL$1 + "?windowType=editor");
} else {
win.loadFile(path.join(RENDERER_DIST, "index.html"), {
win.loadFile(path.join(RENDERER_DIST$1, "index.html"), {
query: { windowType: "editor" }
});
}
return win;
}
function createSourceSelectorWindow() {
const { width, height } = screen.getPrimaryDisplay().workAreaSize;
sourceSelectorWindow = new BrowserWindow({
const win = new BrowserWindow({
width: 620,
height: 420,
minHeight: 350,
@@ -87,30 +82,174 @@ function createSourceSelectorWindow() {
alwaysOnTop: true,
backgroundColor: "#ffffff",
webPreferences: {
preload: path.join(__dirname, "preload.mjs"),
preload: path.join(__dirname$1, "preload.mjs"),
nodeIntegration: false,
contextIsolation: true
}
});
sourceSelectorWindow.on("closed", () => {
sourceSelectorWindow = null;
});
if (VITE_DEV_SERVER_URL) {
sourceSelectorWindow.loadURL(VITE_DEV_SERVER_URL + "?windowType=source-selector");
if (VITE_DEV_SERVER_URL$1) {
win.loadURL(VITE_DEV_SERVER_URL$1 + "?windowType=source-selector");
} else {
sourceSelectorWindow.loadFile(path.join(RENDERER_DIST, "index.html"), {
win.loadFile(path.join(RENDERER_DIST$1, "index.html"), {
query: { windowType: "source-selector" }
});
}
return sourceSelectorWindow;
return win;
}
let isMouseTrackingActive = false;
let isHookStarted = false;
function startMouseTracking() {
if (isMouseTrackingActive) {
console.log("⚠️ Mouse tracking already active");
return { success: false, message: "Already tracking" };
}
console.log("🎯 Starting mouse tracking...");
isMouseTrackingActive = true;
if (!isHookStarted) {
setupMouseEventListeners();
try {
uIOhook.start();
isHookStarted = true;
console.log("✅ Mouse tracking started successfully");
console.log('💡 If you see "Accessibility API is disabled" error:');
console.log(" Go to System Settings → Privacy & Security → Accessibility");
console.log(" Enable permissions for Electron/Terminal/VS Code");
return { success: true, message: "Mouse tracking started" };
} catch (error) {
console.error("❌ Failed to start mouse tracking:", error);
isMouseTrackingActive = false;
return { success: false, message: "Failed to start hook", error };
}
} else {
console.log("✅ Mouse tracking resumed");
return { success: true, message: "Mouse tracking resumed" };
}
}
function stopMouseTracking() {
if (!isMouseTrackingActive) {
console.log("⚠️ Mouse tracking not active");
return { success: false, message: "Not currently tracking" };
}
console.log("🛑 Stopping mouse tracking...");
isMouseTrackingActive = false;
console.log("✅ Mouse tracking stopped (events will still be captured but not logged)");
return { success: true, message: "Mouse tracking stopped" };
}
function setupMouseEventListeners() {
uIOhook.on("mousemove", (e) => {
if (isMouseTrackingActive) {
console.log(`[MOUSE MOVE] x: ${e.x}, y: ${e.y}`);
}
});
uIOhook.on("mousedown", (e) => {
if (isMouseTrackingActive) {
console.log(`[MOUSE DOWN] x: ${e.x}, y: ${e.y}, button: ${e.button}, clicks: ${e.clicks}`);
}
});
uIOhook.on("mouseup", (e) => {
if (isMouseTrackingActive) {
console.log(`[MOUSE UP] x: ${e.x}, y: ${e.y}, button: ${e.button}`);
}
});
uIOhook.on("click", (e) => {
if (isMouseTrackingActive) {
console.log(`[CLICK] x: ${e.x}, y: ${e.y}, button: ${e.button}, clicks: ${e.clicks}`);
}
});
uIOhook.on("wheel", (e) => {
if (isMouseTrackingActive) {
console.log(`[WHEEL] x: ${e.x}, y: ${e.y}, amount: ${e.amount}, direction: ${e.direction}, rotation: ${e.rotation}`);
}
});
}
function cleanupMouseTracking() {
if (isHookStarted) {
try {
uIOhook.stop();
isHookStarted = false;
isMouseTrackingActive = false;
console.log("🧹 Mouse tracking cleaned up");
} catch (error) {
console.error("Error cleaning up mouse tracking:", error);
}
}
}
let selectedSource = null;
function registerIpcHandlers(createEditorWindow2, createSourceSelectorWindow2, getMainWindow, getSourceSelectorWindow) {
ipcMain.handle("get-sources", async (_, opts) => {
const sources = await desktopCapturer.getSources(opts);
const processedSources = sources.map((source) => ({
id: source.id,
name: source.name,
display_id: source.display_id,
thumbnail: source.thumbnail ? source.thumbnail.toDataURL() : null,
appIcon: source.appIcon ? source.appIcon.toDataURL() : null
}));
return processedSources;
});
ipcMain.handle("select-source", (_, source) => {
selectedSource = source;
const sourceSelectorWin = getSourceSelectorWindow();
if (sourceSelectorWin) {
sourceSelectorWin.close();
}
return selectedSource;
});
ipcMain.handle("get-selected-source", () => {
return selectedSource;
});
ipcMain.handle("open-source-selector", () => {
const sourceSelectorWin = getSourceSelectorWindow();
if (sourceSelectorWin) {
sourceSelectorWin.focus();
return;
}
createSourceSelectorWindow2();
});
ipcMain.handle("switch-to-editor", () => {
const mainWin = getMainWindow();
if (mainWin) {
mainWin.close();
}
createEditorWindow2();
});
ipcMain.handle("start-mouse-tracking", () => {
return startMouseTracking();
});
ipcMain.handle("stop-mouse-tracking", () => {
return stopMouseTracking();
});
}
const __dirname = path.dirname(fileURLToPath(import.meta.url));
process.env.APP_ROOT = path.join(__dirname, "..");
const VITE_DEV_SERVER_URL = process.env["VITE_DEV_SERVER_URL"];
const MAIN_DIST = path.join(process.env.APP_ROOT, "dist-electron");
const RENDERER_DIST = path.join(process.env.APP_ROOT, "dist");
process.env.VITE_PUBLIC = VITE_DEV_SERVER_URL ? path.join(process.env.APP_ROOT, "public") : RENDERER_DIST;
let mainWindow = null;
let sourceSelectorWindow = null;
function createWindow() {
createHudOverlayWindow();
mainWindow = createHudOverlayWindow();
}
function createEditorWindowWrapper() {
if (mainWindow) {
mainWindow.close();
mainWindow = null;
}
mainWindow = createEditorWindow();
}
function createSourceSelectorWindowWrapper() {
sourceSelectorWindow = createSourceSelectorWindow();
sourceSelectorWindow.on("closed", () => {
sourceSelectorWindow = null;
});
return sourceSelectorWindow;
}
app.on("window-all-closed", () => {
if (process.platform !== "darwin") {
cleanupMouseTracking();
app.quit();
win = null;
mainWindow = null;
}
});
app.on("activate", () => {
@@ -118,43 +257,18 @@ app.on("activate", () => {
createWindow();
}
});
ipcMain.handle("get-sources", async (_, opts) => {
const sources = await desktopCapturer.getSources(opts);
const processedSources = sources.map((source) => ({
id: source.id,
name: source.name,
display_id: source.display_id,
thumbnail: source.thumbnail ? source.thumbnail.toDataURL() : null,
appIcon: source.appIcon ? source.appIcon.toDataURL() : null
}));
return processedSources;
app.on("will-quit", () => {
cleanupMouseTracking();
});
ipcMain.handle("select-source", (_, source) => {
selectedSource = source;
if (sourceSelectorWindow) {
sourceSelectorWindow.close();
sourceSelectorWindow = null;
}
return selectedSource;
app.whenReady().then(() => {
registerIpcHandlers(
createEditorWindowWrapper,
createSourceSelectorWindowWrapper,
() => mainWindow,
() => sourceSelectorWindow
);
createWindow();
});
ipcMain.handle("get-selected-source", () => {
return selectedSource;
});
ipcMain.handle("open-source-selector", () => {
if (sourceSelectorWindow) {
sourceSelectorWindow.focus();
return;
}
createSourceSelectorWindow();
});
ipcMain.handle("switch-to-editor", () => {
if (win) {
win.close();
win = null;
}
createEditorWindow();
});
app.whenReady().then(createWindow);
export {
MAIN_DIST,
RENDERER_DIST,
+6
View File
@@ -15,5 +15,11 @@ electron.contextBridge.exposeInMainWorld("electronAPI", {
},
getSelectedSource: () => {
return electron.ipcRenderer.invoke("get-selected-source");
},
startMouseTracking: () => {
return electron.ipcRenderer.invoke("start-mouse-tracking");
},
stopMouseTracking: () => {
return electron.ipcRenderer.invoke("stop-mouse-tracking");
}
});
+70
View File
@@ -0,0 +1,70 @@
import { ipcMain, desktopCapturer, BrowserWindow } from 'electron'
import { startMouseTracking, stopMouseTracking } from './mouseTracking'
// Store selected source
let selectedSource: any = null
export function registerIpcHandlers(
createEditorWindow: () => void,
createSourceSelectorWindow: () => BrowserWindow,
getMainWindow: () => BrowserWindow | null,
getSourceSelectorWindow: () => BrowserWindow | null
) {
// Get available desktop capturer sources
ipcMain.handle('get-sources', async (_, opts) => {
const sources = await desktopCapturer.getSources(opts)
const processedSources = sources.map(source => ({
id: source.id,
name: source.name,
display_id: source.display_id,
thumbnail: source.thumbnail ? source.thumbnail.toDataURL() : null,
appIcon: source.appIcon ? source.appIcon.toDataURL() : null
}))
return processedSources
})
// Select a source for recording
ipcMain.handle('select-source', (_, source) => {
selectedSource = source
const sourceSelectorWin = getSourceSelectorWindow()
if (sourceSelectorWin) {
sourceSelectorWin.close()
}
return selectedSource
})
// Get the currently selected source
ipcMain.handle('get-selected-source', () => {
return selectedSource
})
// Open the source selector window
ipcMain.handle('open-source-selector', () => {
const sourceSelectorWin = getSourceSelectorWindow()
if (sourceSelectorWin) {
sourceSelectorWin.focus()
return
}
createSourceSelectorWindow()
})
// Switch from HUD overlay to editor window
ipcMain.handle('switch-to-editor', () => {
const mainWin = getMainWindow()
if (mainWin) {
mainWin.close()
}
createEditorWindow()
})
// Start mouse tracking
ipcMain.handle('start-mouse-tracking', () => {
return startMouseTracking()
})
// Stop mouse tracking
ipcMain.handle('stop-mouse-tracking', () => {
return stopMouseTracking()
})
}
+99
View File
@@ -0,0 +1,99 @@
import { uIOhook } from 'uiohook-napi'
let isMouseTrackingActive = false
let isHookStarted = false
export function startMouseTracking() {
if (isMouseTrackingActive) {
console.log('⚠️ Mouse tracking already active')
return { success: false, message: 'Already tracking' }
}
console.log('🎯 Starting mouse tracking...')
isMouseTrackingActive = true
// Only start the hook once
if (!isHookStarted) {
setupMouseEventListeners()
try {
uIOhook.start()
isHookStarted = true
console.log('✅ Mouse tracking started successfully')
console.log('💡 If you see "Accessibility API is disabled" error:')
console.log(' Go to System Settings → Privacy & Security → Accessibility')
console.log(' Enable permissions for Electron/Terminal/VS Code')
return { success: true, message: 'Mouse tracking started' }
} catch (error) {
console.error('❌ Failed to start mouse tracking:', error)
isMouseTrackingActive = false
return { success: false, message: 'Failed to start hook', error }
}
} else {
console.log('✅ Mouse tracking resumed')
return { success: true, message: 'Mouse tracking resumed' }
}
}
export function stopMouseTracking() {
if (!isMouseTrackingActive) {
console.log('⚠️ Mouse tracking not active')
return { success: false, message: 'Not currently tracking' }
}
console.log('🛑 Stopping mouse tracking...')
isMouseTrackingActive = false
console.log('✅ Mouse tracking stopped (events will still be captured but not logged)')
return { success: true, message: 'Mouse tracking stopped' }
}
function setupMouseEventListeners() {
// Track mouse movement
uIOhook.on('mousemove', (e) => {
if (isMouseTrackingActive) {
console.log(`[MOUSE MOVE] x: ${e.x}, y: ${e.y}`)
}
})
// Track mouse button press
uIOhook.on('mousedown', (e) => {
if (isMouseTrackingActive) {
console.log(`[MOUSE DOWN] x: ${e.x}, y: ${e.y}, button: ${e.button}, clicks: ${e.clicks}`)
}
})
// Track mouse button release
uIOhook.on('mouseup', (e) => {
if (isMouseTrackingActive) {
console.log(`[MOUSE UP] x: ${e.x}, y: ${e.y}, button: ${e.button}`)
}
})
// Track complete click events
uIOhook.on('click', (e) => {
if (isMouseTrackingActive) {
console.log(`[CLICK] x: ${e.x}, y: ${e.y}, button: ${e.button}, clicks: ${e.clicks}`)
}
})
// Track mouse wheel scrolling
uIOhook.on('wheel', (e) => {
if (isMouseTrackingActive) {
console.log(`[WHEEL] x: ${e.x}, y: ${e.y}, amount: ${e.amount}, direction: ${e.direction}, rotation: ${e.rotation}`)
}
})
}
export function cleanupMouseTracking() {
if (isHookStarted) {
try {
uIOhook.stop()
isHookStarted = false
isMouseTrackingActive = false
console.log('🧹 Mouse tracking cleaned up')
} catch (error) {
console.error('Error cleaning up mouse tracking:', error)
}
}
}
+36 -152
View File
@@ -1,9 +1,10 @@
import { app, BrowserWindow, ipcMain, desktopCapturer, screen } from 'electron'
import { createRequire } from 'node:module'
import { app, BrowserWindow } from 'electron'
import { fileURLToPath } from 'node:url'
import path from 'node:path'
import { createHudOverlayWindow, createEditorWindow, createSourceSelectorWindow } from './windows'
import { registerIpcHandlers } from './ipc/handlers'
import { cleanupMouseTracking } from './ipc/mouseTracking'
const require = createRequire(import.meta.url)
const __dirname = path.dirname(fileURLToPath(import.meta.url))
// The built directory structure
@@ -24,117 +25,28 @@ export const RENDERER_DIST = path.join(process.env.APP_ROOT, 'dist')
process.env.VITE_PUBLIC = VITE_DEV_SERVER_URL ? path.join(process.env.APP_ROOT, 'public') : RENDERER_DIST
let win: BrowserWindow | null
// Window references
let mainWindow: BrowserWindow | null = null
let sourceSelectorWindow: BrowserWindow | null = null
let selectedSource: any = null
function createHudOverlayWindow() {
win = new BrowserWindow({
width: 250,
height: 80,
minWidth: 250,
maxWidth: 250,
minHeight: 80,
maxHeight: 80,
frame: false,
transparent: true,
resizable: false,
alwaysOnTop: true,
skipTaskbar: true,
hasShadow: false,
webPreferences: {
preload: path.join(__dirname, 'preload.mjs'),
nodeIntegration: false,
contextIsolation: true,
backgroundThrottling: false,
},
})
// Absolutely lock the size
win.setResizable(false)
win.webContents.on('did-finish-load', () => {
win?.webContents.send('main-process-message', (new Date).toLocaleString())
})
if (VITE_DEV_SERVER_URL) {
win.loadURL(VITE_DEV_SERVER_URL + '?windowType=hud-overlay')
} else {
win.loadFile(path.join(RENDERER_DIST, 'index.html'), {
query: { windowType: 'hud-overlay' }
})
}
}
function createEditorWindow() {
win = new BrowserWindow({
width: 1200,
height: 800,
minWidth: 800,
minHeight: 600,
frame: true,
transparent: false,
resizable: true,
alwaysOnTop: false,
skipTaskbar: false,
webPreferences: {
preload: path.join(__dirname, 'preload.mjs'),
nodeIntegration: false,
contextIsolation: true,
},
})
win.webContents.on('did-finish-load', () => {
win?.webContents.send('main-process-message', (new Date).toLocaleString())
})
if (VITE_DEV_SERVER_URL) {
win.loadURL(VITE_DEV_SERVER_URL + '?windowType=editor')
} else {
win.loadFile(path.join(RENDERER_DIST, 'index.html'), {
query: { windowType: 'editor' }
})
}
}
function createSourceSelectorWindow() {
const { width, height } = screen.getPrimaryDisplay().workAreaSize;
sourceSelectorWindow = new BrowserWindow({
width: 620,
height: 420,
minHeight: 350,
maxHeight: 500,
x: Math.round((width - 620) / 2),
y: Math.round((height - 420) / 2),
frame: false,
resizable: false,
alwaysOnTop: true,
backgroundColor: '#ffffff',
webPreferences: {
preload: path.join(__dirname, 'preload.mjs'),
nodeIntegration: false,
contextIsolation: true,
},
});
sourceSelectorWindow.on('closed', () => {
sourceSelectorWindow = null;
});
if (VITE_DEV_SERVER_URL) {
sourceSelectorWindow.loadURL(VITE_DEV_SERVER_URL + '?windowType=source-selector');
} else {
sourceSelectorWindow.loadFile(path.join(RENDERER_DIST, 'index.html'), {
query: { windowType: 'source-selector' }
});
}
return sourceSelectorWindow;
}
function createWindow() {
createHudOverlayWindow()
mainWindow = createHudOverlayWindow()
}
function createEditorWindowWrapper() {
if (mainWindow) {
mainWindow.close()
mainWindow = null
}
mainWindow = createEditorWindow()
}
function createSourceSelectorWindowWrapper() {
sourceSelectorWindow = createSourceSelectorWindow()
sourceSelectorWindow.on('closed', () => {
sourceSelectorWindow = null
})
return sourceSelectorWindow
}
// Quit when all windows are closed, except on macOS. There, it's common
@@ -142,8 +54,9 @@ function createWindow() {
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
cleanupMouseTracking()
app.quit()
win = null
mainWindow = null
}
})
@@ -155,46 +68,17 @@ app.on('activate', () => {
}
})
ipcMain.handle('get-sources', async (_, opts) => {
const sources = await desktopCapturer.getSources(opts)
const processedSources = sources.map(source => ({
id: source.id,
name: source.name,
display_id: source.display_id,
thumbnail: source.thumbnail ? source.thumbnail.toDataURL() : null,
appIcon: source.appIcon ? source.appIcon.toDataURL() : null
}))
return processedSources
app.on('will-quit', () => {
cleanupMouseTracking()
})
ipcMain.handle('select-source', (_, source) => {
selectedSource = source
if (sourceSelectorWindow) {
sourceSelectorWindow.close();
sourceSelectorWindow = null;
}
return selectedSource
// Register all IPC handlers when app is ready
app.whenReady().then(() => {
registerIpcHandlers(
createEditorWindowWrapper,
createSourceSelectorWindowWrapper,
() => mainWindow,
() => sourceSelectorWindow
)
createWindow()
})
ipcMain.handle('get-selected-source', () => {
return selectedSource
})
ipcMain.handle('open-source-selector', () => {
if (sourceSelectorWindow) {
sourceSelectorWindow.focus();
return;
}
createSourceSelectorWindow();
})
ipcMain.handle('switch-to-editor', () => {
if (win) {
win.close()
win = null
}
createEditorWindow()
})
app.whenReady().then(createWindow)
+6
View File
@@ -15,5 +15,11 @@ contextBridge.exposeInMainWorld('electronAPI', {
},
getSelectedSource: () => {
return ipcRenderer.invoke('get-selected-source')
},
startMouseTracking: () => {
return ipcRenderer.invoke('start-mouse-tracking')
},
stopMouseTracking: () => {
return ipcRenderer.invoke('stop-mouse-tracking')
}
})
+112
View File
@@ -0,0 +1,112 @@
import { BrowserWindow, screen } from 'electron'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const APP_ROOT = path.join(__dirname, '..')
const VITE_DEV_SERVER_URL = process.env['VITE_DEV_SERVER_URL']
const RENDERER_DIST = path.join(APP_ROOT, 'dist')
export function createHudOverlayWindow(): BrowserWindow {
const win = new BrowserWindow({
width: 250,
height: 80,
minWidth: 250,
maxWidth: 250,
minHeight: 80,
maxHeight: 80,
frame: false,
transparent: true,
resizable: false,
alwaysOnTop: true,
skipTaskbar: true,
hasShadow: false,
webPreferences: {
preload: path.join(__dirname, 'preload.mjs'),
nodeIntegration: false,
contextIsolation: true,
backgroundThrottling: false,
},
})
win.webContents.on('did-finish-load', () => {
win?.webContents.send('main-process-message', (new Date).toLocaleString())
})
if (VITE_DEV_SERVER_URL) {
win.loadURL(VITE_DEV_SERVER_URL + '?windowType=hud-overlay')
} else {
win.loadFile(path.join(RENDERER_DIST, 'index.html'), {
query: { windowType: 'hud-overlay' }
})
}
return win
}
export function createEditorWindow(): BrowserWindow {
const win = new BrowserWindow({
width: 1200,
height: 800,
minWidth: 800,
minHeight: 600,
frame: true,
transparent: false,
resizable: true,
alwaysOnTop: false,
skipTaskbar: false,
webPreferences: {
preload: path.join(__dirname, 'preload.mjs'),
nodeIntegration: false,
contextIsolation: true,
},
})
win.webContents.on('did-finish-load', () => {
win?.webContents.send('main-process-message', (new Date).toLocaleString())
})
if (VITE_DEV_SERVER_URL) {
win.loadURL(VITE_DEV_SERVER_URL + '?windowType=editor')
} else {
win.loadFile(path.join(RENDERER_DIST, 'index.html'), {
query: { windowType: 'editor' }
})
}
return win
}
export function createSourceSelectorWindow(): BrowserWindow {
const { width, height } = screen.getPrimaryDisplay().workAreaSize
const win = new BrowserWindow({
width: 620,
height: 420,
minHeight: 350,
maxHeight: 500,
x: Math.round((width - 620) / 2),
y: Math.round((height - 420) / 2),
frame: false,
resizable: false,
alwaysOnTop: true,
backgroundColor: '#ffffff',
webPreferences: {
preload: path.join(__dirname, 'preload.mjs'),
nodeIntegration: false,
contextIsolation: true,
},
})
if (VITE_DEV_SERVER_URL) {
win.loadURL(VITE_DEV_SERVER_URL + '?windowType=source-selector')
} else {
win.loadFile(path.join(RENDERER_DIST, 'index.html'), {
query: { windowType: 'source-selector' }
})
}
return win
}
+1081 -9
View File
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -19,7 +19,8 @@
"react-dom": "^18.2.0",
"react-icons": "^5.5.0",
"tailwind-merge": "^3.3.1",
"tailwindcss-animate": "^1.0.7"
"tailwindcss-animate": "^1.0.7",
"uiohook-napi": "^1.5.4"
},
"devDependencies": {
"@types/react": "^18.2.64",
@@ -30,6 +31,7 @@
"autoprefixer": "^10.4.21",
"electron": "^30.0.1",
"electron-builder": "^24.13.3",
"electron-rebuild": "^3.2.9",
"eslint": "^8.57.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.5",
+8
View File
@@ -36,6 +36,10 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
return;
}
// Start mouse tracking
console.log('Starting mouse tracking from renderer...')
await window.electronAPI.startMouseTracking();
// Use the selected source
const stream = await (navigator.mediaDevices as any).getUserMedia({
audio: false,
@@ -101,6 +105,10 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
) {
mediaRecorderRef.current.stop();
setRecording(false);
// Stop mouse tracking
console.log('Stopping mouse tracking from renderer...')
window.electronAPI.stopMouseTracking();
}
};
+2
View File
@@ -15,5 +15,7 @@ interface Window {
openSourceSelector: () => Promise<void>
selectSource: (source: any) => Promise<any>
getSelectedSource: () => Promise<any>
startMouseTracking: () => Promise<{ success: boolean }>
stopMouseTracking: () => Promise<{ success: boolean }>
}
}
+7
View File
@@ -12,6 +12,13 @@ export default defineConfig({
main: {
// Shortcut of `build.lib.entry`.
entry: 'electron/main.ts',
vite: {
build: {
rollupOptions: {
external: ['uiohook-napi']
}
}
}
},
preload: {
// Shortcut of `build.rollupOptions.input`.