Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ dist-ssr
*.sln
*.sw?
release/**

*.kiro/
# npx electron-builder --mac --win
79 changes: 51 additions & 28 deletions dist-electron/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,16 @@ function createHudOverlayWindow() {
return win;
}
function createEditorWindow() {
const isMac = process.platform === "darwin";
const win = new BrowserWindow({
width: 1200,
height: 800,
minWidth: 800,
minHeight: 600,
titleBarStyle: "hiddenInset",
trafficLightPosition: { x: 12, y: 12 },
...isMac && {
titleBarStyle: "hiddenInset",
trafficLightPosition: { x: 12, y: 12 }
},
transparent: false,
resizable: true,
alwaysOnTop: false,
Expand Down Expand Up @@ -223,12 +226,13 @@ function registerIpcHandlers(createEditorWindow2, createSourceSelectorWindow2, g
});
ipcMain.handle("save-exported-video", async (_, videoData, fileName) => {
try {
const result = await dialog.showSaveDialog({
title: "Save Exported Video",
const mainWindow2 = getMainWindow();
const isGif = fileName.toLowerCase().endsWith(".gif");
const filters = isGif ? [{ name: "GIF Image", extensions: ["gif"] }] : [{ name: "MP4 Video", extensions: ["mp4"] }];
const result = await dialog.showSaveDialog(mainWindow2 || void 0, {
title: isGif ? "Save Exported GIF" : "Save Exported Video",
defaultPath: path.join(app.getPath("downloads"), fileName),
filters: [
{ name: "MP4 Video", extensions: ["mp4"] }
],
filters,
properties: ["createDirectory", "showOverwriteConfirmation"]
});
if (result.canceled || !result.filePath) {
Expand Down Expand Up @@ -316,19 +320,26 @@ let mainWindow = null;
let sourceSelectorWindow = null;
let tray = null;
let selectedSourceName = "";
const defaultTrayIcon = getTrayIcon("openscreen.png");
const recordingTrayIcon = getTrayIcon("rec-button.png");
function createWindow() {
mainWindow = createHudOverlayWindow();
}
function createTray() {
const iconPath = path.join(process.env.VITE_PUBLIC || RENDERER_DIST, "rec-button.png");
let icon = nativeImage.createFromPath(iconPath);
icon = icon.resize({ width: 24, height: 24, quality: "best" });
tray = new Tray(icon);
updateTrayMenu();
tray = new Tray(defaultTrayIcon);
}
function getTrayIcon(filename) {
return nativeImage.createFromPath(path.join(process.env.VITE_PUBLIC || RENDERER_DIST, filename)).resize({
width: 24,
height: 24,
quality: "best"
});
}
function updateTrayMenu() {
function updateTrayMenu(recording = false) {
if (!tray) return;
const menuTemplate = [
const trayIcon = recording ? recordingTrayIcon : defaultTrayIcon;
const trayToolTip = recording ? `Recording: ${selectedSourceName}` : "OpenScreen";
const menuTemplate = recording ? [
{
label: "Stop Recording",
click: () => {
Expand All @@ -337,10 +348,27 @@ function updateTrayMenu() {
}
}
}
] : [
{
label: "Open",
click: () => {
if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.isMinimized() && mainWindow.restore();
} else {
createWindow();
}
}
},
{
label: "Quit",
click: () => {
app.quit();
}
}
];
const contextMenu = Menu.buildFromTemplate(menuTemplate);
tray.setContextMenu(contextMenu);
tray.setToolTip(`Recording: ${selectedSourceName}`);
tray.setImage(trayIcon);
tray.setToolTip(trayToolTip);
tray.setContextMenu(Menu.buildFromTemplate(menuTemplate));
}
function createEditorWindowWrapper() {
if (mainWindow) {
Expand All @@ -366,10 +394,10 @@ app.on("activate", () => {
app.whenReady().then(async () => {
const { ipcMain: ipcMain2 } = await import("electron");
ipcMain2.on("hud-overlay-close", () => {
if (process.platform === "darwin") {
app.quit();
}
app.quit();
});
createTray();
updateTrayMenu();
await ensureRecordingsDir();
registerIpcHandlers(
createEditorWindowWrapper,
Expand All @@ -378,14 +406,9 @@ app.whenReady().then(async () => {
() => sourceSelectorWindow,
(recording, sourceName) => {
selectedSourceName = sourceName;
if (recording) {
if (!tray) createTray();
updateTrayMenu();
} else {
if (tray) {
tray.destroy();
tray = null;
}
if (!tray) createTray();
updateTrayMenu(recording);
if (!recording) {
if (mainWindow) mainWindow.restore();
}
}
Expand Down
19 changes: 13 additions & 6 deletions electron/ipc/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,18 @@ export function registerIpcHandlers(

ipcMain.handle('save-exported-video', async (_, videoData: ArrayBuffer, fileName: string) => {
try {
const result = await dialog.showSaveDialog({
title: 'Save Exported Video',
const mainWindow = getMainWindow();

// Determine file type from extension
const isGif = fileName.toLowerCase().endsWith('.gif');
const filters = isGif
? [{ name: 'GIF Image', extensions: ['gif'] }]
: [{ name: 'MP4 Video', extensions: ['mp4'] }];

const result = await dialog.showSaveDialog(mainWindow || undefined, {
title: isGif ? 'Save Exported GIF' : 'Save Exported Video',
defaultPath: path.join(app.getPath('downloads'), fileName),
filters: [
{ name: 'MP4 Video', extensions: ['mp4'] }
],
filters,
properties: ['createDirectory', 'showOverwriteConfirmation']
});

Expand All @@ -146,8 +152,9 @@ export function registerIpcHandlers(
message: 'Export cancelled'
};
}

await fs.writeFile(result.filePath, Buffer.from(videoData));

return {
success: true,
path: result.filePath,
Expand Down
Loading