sunset windows support

This commit is contained in:
Siddharth
2025-11-20 18:45:06 -07:00
parent 27377c2194
commit dda08172d9
14 changed files with 12 additions and 214 deletions
+1 -28
View File
@@ -6,34 +6,7 @@ on:
workflow_dispatch:
jobs:
build-windows:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '22'
- name: Install dependencies
run: npm ci
- name: Install app dependencies
run: npx electron-builder install-app-deps
- name: Build Windows app
run: npm run build:win
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload Windows build
uses: actions/upload-artifact@v4
with:
name: windows-installer
path: release/**/*.exe
retention-days: 30
build-macos:
runs-on: macos-latest
-1
View File
@@ -37,7 +37,6 @@ OpenScreen is 100% free for personal and commercial use. Use it, modify it, dist
- Motion blur and exponential easing for smoother pan and zoom effects
**Note:**
- OpenScreen is designed to support both Windows and macOS. We did not use any native APIs specific to one OS, so it should work on both. However, it hasn't been fully tested on Windows yet, if you run into issues, please let me know!
- After you install the app, you'll need to grant it accessibility and screen recording permissions for it to work properly.
+2 -37
View File
@@ -41,21 +41,13 @@ function createHudOverlayWindow() {
return win;
}
function createEditorWindow() {
const isMac = process.platform === "darwin";
const win = new BrowserWindow({
width: 1200,
height: 800,
minWidth: 800,
minHeight: 600,
// On macOS, use hiddenInset for native controls; on Windows, frameless
...isMac ? {
titleBarStyle: "hiddenInset",
trafficLightPosition: { x: 12, y: 12 }
} : {
frame: false,
icon: void 0
// No app icon on Windows
},
titleBarStyle: "hiddenInset",
trafficLightPosition: { x: 12, y: 12 },
transparent: false,
resizable: true,
alwaysOnTop: false,
@@ -369,28 +361,6 @@ function registerIpcHandlers(createEditorWindow2, createSourceSelectorWindow2, g
};
}
});
ipcMain.handle("minimize-window", () => {
const mainWin = getMainWindow();
if (mainWin) {
mainWin.minimize();
}
});
ipcMain.handle("maximize-window", () => {
const mainWin = getMainWindow();
if (mainWin) {
if (mainWin.isMaximized()) {
mainWin.unmaximize();
} else {
mainWin.maximize();
}
}
});
ipcMain.handle("close-window", () => {
const mainWin = getMainWindow();
if (mainWin) {
mainWin.close();
}
});
}
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const RECORDINGS_DIR = path.join(app.getPath("userData"), "recordings");
@@ -469,11 +439,6 @@ function createSourceSelectorWindowWrapper() {
return sourceSelectorWindow;
}
app.on("window-all-closed", () => {
if (process.platform !== "darwin") {
cleanupMouseTracking();
app.quit();
mainWindow = null;
}
});
app.on("activate", () => {
if (BrowserWindow.getAllWindows().length === 0) {
-9
View File
@@ -47,14 +47,5 @@ electron.contextBridge.exposeInMainWorld("electronAPI", {
},
saveExportedVideo: (videoData, fileName) => {
return electron.ipcRenderer.invoke("save-exported-video", videoData, fileName);
},
minimizeWindow: () => {
return electron.ipcRenderer.invoke("minimize-window");
},
maximizeWindow: () => {
return electron.ipcRenderer.invoke("maximize-window");
},
closeWindow: () => {
return electron.ipcRenderer.invoke("close-window");
}
});
-19
View File
@@ -26,25 +26,6 @@
"icon": "icons/icons/mac/icon.icns",
"artifactName": "${productName}-Mac-${version}-Installer.${ext}"
},
"win": {
"target": [
{
"target": "nsis",
"arch": [
"x64"
]
}
],
"icon": "icons/icons/win/icon.ico",
"artifactName": "${productName}-Windows-${version}-Setup.${ext}",
"requestedExecutionLevel": "asInvoker"
},
"nsis": {
"oneClick": false,
"perMachine": false,
"allowToChangeInstallationDirectory": true,
"deleteAppDataOnUninstall": false
},
"linux": {
"target": [
"AppImage"
-3
View File
@@ -38,9 +38,6 @@ interface Window {
onStopRecordingFromTray: (callback: () => void) => () => void
openExternalUrl: (url: string) => Promise<{ success: boolean; error?: string }>
saveExportedVideo: (videoData: ArrayBuffer, fileName: string) => Promise<{ success: boolean; path?: string; message?: string }>
minimizeWindow: () => Promise<void>
maximizeWindow: () => Promise<void>
closeWindow: () => Promise<void>
}
}
-26
View File
@@ -178,30 +178,4 @@ export function registerIpcHandlers(
}
}
})
// Window control handlers for frameless window
ipcMain.handle('minimize-window', () => {
const mainWin = getMainWindow()
if (mainWin) {
mainWin.minimize()
}
})
ipcMain.handle('maximize-window', () => {
const mainWin = getMainWindow()
if (mainWin) {
if (mainWin.isMaximized()) {
mainWin.unmaximize()
} else {
mainWin.maximize()
}
}
})
ipcMain.handle('close-window', () => {
const mainWin = getMainWindow()
if (mainWin) {
mainWin.close()
}
})
}
+2 -7
View File
@@ -109,15 +109,10 @@ function createSourceSelectorWindowWrapper() {
return sourceSelectorWindow
}
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// On macOS, applications and their menu bar stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
cleanupMouseTracking()
app.quit()
mainWindow = null
}
// Keep app running (macOS behavior)
})
app.on('activate', () => {
-9
View File
@@ -49,13 +49,4 @@ contextBridge.exposeInMainWorld('electronAPI', {
saveExportedVideo: (videoData: ArrayBuffer, fileName: string) => {
return ipcRenderer.invoke('save-exported-video', videoData, fileName)
},
minimizeWindow: () => {
return ipcRenderer.invoke('minimize-window')
},
maximizeWindow: () => {
return ipcRenderer.invoke('maximize-window')
},
closeWindow: () => {
return ipcRenderer.invoke('close-window')
},
})
+2 -10
View File
@@ -47,21 +47,13 @@ export function createHudOverlayWindow(): BrowserWindow {
}
export function createEditorWindow(): BrowserWindow {
const isMac = process.platform === 'darwin'
const win = new BrowserWindow({
width: 1200,
height: 800,
minWidth: 800,
minHeight: 600,
// On macOS, use hiddenInset for native controls; on Windows, frameless
...(isMac ? {
titleBarStyle: 'hiddenInset',
trafficLightPosition: { x: 12, y: 12 },
} : {
frame: false,
icon: undefined, // No app icon on Windows
}),
titleBarStyle: 'hiddenInset',
trafficLightPosition: { x: 12, y: 12 },
transparent: false,
resizable: true,
alwaysOnTop: false,
+2 -3
View File
@@ -8,8 +8,7 @@
"build": "tsc && vite build && electron-builder",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"build:mac": "tsc && vite build && electron-builder --mac",
"build:win": "tsc && vite build && electron-builder --win --x64"
"build:mac": "tsc && vite build && electron-builder --mac"
},
"dependencies": {
"@fix-webm-duration/fix": "^1.0.1",
@@ -57,4 +56,4 @@
"vite-plugin-electron-renderer": "^0.14.5"
},
"main": "dist-electron/main.js"
}
}
+2 -2
View File
@@ -9,7 +9,7 @@ import PlaybackControls from "./PlaybackControls";
import TimelineEditor from "./timeline/TimelineEditor";
import { SettingsPanel } from "./SettingsPanel";
import { ExportDialog } from "./ExportDialog";
import { WindowControls } from "./WindowControls";
import type { Span } from "dnd-timeline";
import {
DEFAULT_ZOOM_DEPTH,
@@ -275,7 +275,7 @@ export default function VideoEditor() {
style={{ WebkitAppRegion: 'drag' } as React.CSSProperties}
>
<div className="flex-1" />
<WindowControls />
</div>
<div className="flex-1 p-4 gap-4 flex min-h-0 relative">
@@ -1,57 +0,0 @@
export function WindowControls() {
// Only show custom controls on Windows
const isWindows = navigator.userAgent.includes('Windows');
if (!isWindows) {
return null;
}
const handleMinimize = () => {
window.electronAPI?.minimizeWindow?.();
};
const handleMaximize = () => {
window.electronAPI?.maximizeWindow?.();
};
const handleClose = () => {
window.electronAPI?.closeWindow?.();
};
return (
<div className="flex items-center" style={{ WebkitAppRegion: 'no-drag' } as React.CSSProperties}>
{/* Minimize - Horizontal Line */}
<button
onClick={handleMinimize}
className="w-12 h-8 flex items-center justify-center hover:bg-white/10 transition-colors group"
aria-label="Minimize"
>
<svg width="12" height="1" viewBox="0 0 12 1" className="text-gray-400 group-hover:text-white transition-colors">
<rect width="12" height="1" fill="currentColor" />
</svg>
</button>
{/* Maximize - Square */}
<button
onClick={handleMaximize}
className="w-12 h-8 flex items-center justify-center hover:bg-white/10 transition-colors group"
aria-label="Maximize"
>
<svg width="10" height="10" viewBox="0 0 10 10" className="text-gray-400 group-hover:text-white transition-colors">
<rect x="0" y="0" width="10" height="10" fill="none" stroke="currentColor" strokeWidth="1" />
</svg>
</button>
{/* Close - X */}
<button
onClick={handleClose}
className="w-12 h-8 flex items-center justify-center hover:bg-[#e81123] transition-colors group"
aria-label="Close"
>
<svg width="10" height="10" viewBox="0 0 10 10" className="text-gray-400 group-hover:text-white transition-colors">
<path d="M 0 0 L 10 10 M 10 0 L 0 10" stroke="currentColor" strokeWidth="1" />
</svg>
</button>
</div>
);
}
+1 -3
View File
@@ -119,9 +119,7 @@ export class VideoExporter {
const canvas = this.renderer!.getCanvas();
// CRITICAL FIX for Windows: Explicitly specify colorSpace when creating VideoFrame from canvas
// Without this, VideoFrame.colorSpace can be null on Windows, causing "Cannot read properties of null" error
// Using BT.709 with sRGB transfer (IEC 61966-2-1) which is standard for HD video
// @ts-ignore - TypeScript definitions may not include all VideoFrameInit properties
const exportFrame = new VideoFrame(canvas, {
timestamp,