From 9e9bf292cf2826feb191ad28cd17188677834547 Mon Sep 17 00:00:00 2001 From: eedali Date: Mon, 15 Jun 2026 13:12:33 +0300 Subject: [PATCH] refactor: unify Linux window management with macOS logic and remove hardcoded screen offsets --- electron/main.cts | 59 ++++++++++++++++---------- electron/smtc-worker.cts | 1 + package-lock.json | 42 +++++++----------- package.json | 2 +- src/App.tsx | 4 +- src/components/layout/FloatingEye.tsx | 4 +- src/components/layout/Sidebar.tsx | 13 +++--- src/components/notes/SettingsPanel.tsx | 2 +- src/store/useAppStore.ts | 8 ++-- 9 files changed, 70 insertions(+), 65 deletions(-) diff --git a/electron/main.cts b/electron/main.cts index e69b9de..f06884d 100644 --- a/electron/main.cts +++ b/electron/main.cts @@ -12,6 +12,7 @@ let smtcWorker: Worker | null = null; const isMac = process.platform === 'darwin'; const isWin = process.platform === 'win32'; +const isLinux = process.platform === 'linux'; const isDev = !app.isPackaged; if (isDev) { @@ -28,6 +29,9 @@ function getSystemConfig() { } console.log("[SYS] Hardware Acceleration: FORCED ENABLED"); +if (isLinux) { + app.commandLine.appendSwitch('enable-transparent-visuals'); +} // SURGICAL FIX FOR VIDEO BLACKOUT & OCCLUSION app.commandLine.appendSwitch('disable-features', 'CalculateNativeWinOcclusion,WindowOcclusion,HardwareMediaKeyHandling,MediaSessionService'); @@ -140,7 +144,7 @@ function createWindow() { // Mac: use `bounds` (full screen incl. menu bar + Dock area) instead of `workArea`. // This prevents macOS from clipping the transparent window at the Dock boundary. - const macScreen = isMac ? primary.bounds : primary.workArea; + const macScreen = isMac || isLinux ? primary.bounds : primary.workArea; const winWidth = isWin ? 6000 : macScreen.width; const winHeight = isWin ? 4000 : macScreen.height; const winX = isWin ? x : macScreen.x; @@ -158,11 +162,11 @@ function createWindow() { backgroundColor: '#00000000', // Explicit transparent alpha channel to fix Windows DWM occlusion blurring alwaysOnTop: true, skipTaskbar: true, - type: (isWin ? 'toolbar' : 'panel') as 'toolbar', // Win: toolbar (DWM throttle fix) | Mac: panel (NSPanel, floats above Dock) + type: (isMac ? 'panel' : 'toolbar') as any, // Win/Linux: toolbar (DWM throttle fix / Taskbar hide) | Mac: panel (NSPanel, floats above Dock) resizable: false, maximizable: false, enableLargerThanScreen: isWin, - hasShadow: isMac ? false : true, // Mac: prevent native shadow being cast on invisible transparent divs (ghost stroke) + hasShadow: (isMac || isLinux) ? false : true, // Mac/Linux: prevent native shadow being cast on invisible transparent divs (ghost stroke) icon: path.join(__dirname, '../build/icon.png'), webPreferences: { nodeIntegration: false, @@ -175,13 +179,13 @@ function createWindow() { mainWindow.setMinimumSize(6000, 4000); mainWindow.setMaximumSize(12000, 12000); mainWindow.setSize(6000, 4000); - } else if (isMac) { + } else if (isMac || isLinux) { mainWindow.setMinimumSize(winWidth, winHeight); mainWindow.setSize(winWidth, winHeight); } // Win: screen-saver (highest DWM level) | Mac: floating (above Dock, below TCC prompts) - mainWindow.setAlwaysOnTop(true, isMac ? 'floating' : 'screen-saver', 1); - if (isMac) { + mainWindow.setAlwaysOnTop(true, (isMac || isLinux) ? 'floating' : 'screen-saver', 1); + if (isMac || isLinux) { // Ensure KoBar stays above Dock, Mission Control overlays, and full-screen apps on macOS mainWindow.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true }); mainWindow.setAlwaysOnTop(true, 'floating', 1); // re-enforce after workspace registration @@ -211,8 +215,8 @@ function createWindow() { if (!isHidden) { mainWindow.show(); } - mainWindow.setAlwaysOnTop(true, isMac ? 'floating' : 'screen-saver', 1); - if (isMac) { + mainWindow.setAlwaysOnTop(true, (isMac || isLinux) ? 'floating' : 'screen-saver', 1); + if (isMac || isLinux) { // Re-enforce workspace visibility after window is fully loaded mainWindow.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true }); mainWindow.setAlwaysOnTop(true, 'floating', 1); @@ -242,7 +246,7 @@ function createWindow() { // Re-enforce Always on Top when losing focus to taskbar mainWindow.on('blur', () => { if (!isAwaitingPinTarget && mainWindow) { - mainWindow.setAlwaysOnTop(true, 'screen-saver', 1); + mainWindow.setAlwaysOnTop(true, (isMac || isLinux) ? 'floating' : 'screen-saver', 1); } }); } @@ -291,7 +295,7 @@ function calculatePrimaryCenterPosition(): { x: number; y: number } { const primary = screen.getPrimaryDisplay(); const wa = primary.workArea; // { x, y, width, height } in OS coords - if (isMac) { + if (isMac || isLinux) { return { x: wa.x, y: wa.y }; } @@ -313,7 +317,7 @@ function teleportToPrimaryCenter(showWindow = true) { if (showWindow) { mainWindow.show(); if (mainWindow.isMinimized()) mainWindow.restore(); - mainWindow.setAlwaysOnTop(true, 'screen-saver', 1); + mainWindow.setAlwaysOnTop(true, (isMac || isLinux) ? 'floating' : 'screen-saver', 1); mainWindow.focus(); } @@ -373,7 +377,7 @@ function startClipboardPolling() { } } } - }, isMac ? 1000 : 500); + }, (isMac || isLinux) ? 1000 : 500); } function stopClipboardPolling() { @@ -473,8 +477,17 @@ app.whenReady().then(() => { psProcess?.stdin?.write(`Add-Type -TypeDefinition 'using System; using System.Runtime.InteropServices; public class K { [DllImport("user32.dll")] public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, uint dwExtraInfo); [DllImport("user32.dll")] public static extern short GetAsyncKeyState(int vKey); [DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll")] public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); [DllImport("user32.dll")] public static extern int GetWindowLong(IntPtr hWnd, int nIndex); [DllImport("user32.dll")] public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); [DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); [StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left; public int Top; public int Right; public int Bottom; } }'\n`); } - createWindow(); - createTray(); + if (isLinux) { + // Linux requires a delay after 'enable-transparent-visuals' for the compositor + // to be ready before creating transparent BrowserWindows. + setTimeout(() => { + createWindow(); + createTray(); + }, 1000); + } else { + createWindow(); + createTray(); + } // Handle microphone permissions explicitly for voice-to-text session.defaultSession.setPermissionCheckHandler((_webContents: WebContents | null, permission: string) => { @@ -542,7 +555,7 @@ app.whenReady().then(() => { }); psProcess.stdin!.write(psPinScript + '\n'); } - } else if (isMac) { + } else if (isMac || isLinux) { if (mainWindow) { mainWindow.setAlwaysOnTop(true, 'screen-saver', 1); mainWindow.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true }); @@ -655,8 +668,8 @@ ipcMain.handle('check-for-updates-manual', async () => { }); ipcMain.on('enter-pin-targeting', () => { - if (isMac) { - // macOS: Panel windows don't fire blur events reliably. + if (isMac || isLinux) { + // macOS/Linux: Implement pseudo-pin: immediately elevate KoBar to screen-saver level. // Implement pseudo-pin: immediately elevate KoBar to screen-saver level. if (mainWindow) { mainWindow.setAlwaysOnTop(true, 'screen-saver', 1); @@ -683,7 +696,7 @@ ipcMain.on('unpin-current-window', () => { } allPinnedHwnds.delete(hwndToUnpin); stopWindowTracking(); - } else if (isMac && mainWindow) { + } else if ((isMac || isLinux) && mainWindow) { mainWindow.setAlwaysOnTop(true, 'floating', 1); pinnedHwnd = null; mainWindow.webContents.send('pinned-window-changed', null); @@ -1457,7 +1470,7 @@ ipcMain.handle('start-screenshot-capture', async () => { // 7. Show KoBar back so the overlay React component becomes visible if (mainWindow) { mainWindow.show(); - mainWindow.setAlwaysOnTop(true, isMac ? 'floating' : 'screen-saver', 1); + mainWindow.setAlwaysOnTop(true, (isMac || isLinux) ? 'floating' : 'screen-saver', 1); } return { @@ -1471,7 +1484,7 @@ ipcMain.on('cancel-screenshot', () => { if (mainWindow) { if (preScreenshotBounds) mainWindow.setBounds(preScreenshotBounds); mainWindow.show(); - mainWindow.setAlwaysOnTop(true, isMac ? 'floating' : 'screen-saver', 1); + mainWindow.setAlwaysOnTop(true, (isMac || isLinux) ? 'floating' : 'screen-saver', 1); } }); @@ -1499,7 +1512,7 @@ ipcMain.handle('save-screenshot', async (_event, data: { buffer: string; format: }); // Restore alwaysOnTop - if (mainWindow) mainWindow.setAlwaysOnTop(true, 'screen-saver', 1); + if (mainWindow) mainWindow.setAlwaysOnTop(true, (isMac || isLinux) ? 'floating' : 'screen-saver', 1); if (result.canceled || !result.filePath) return { success: false, reason: 'cancelled' }; savePath = result.filePath; @@ -1528,7 +1541,7 @@ ipcMain.on('screenshot-session-complete', () => { if (mainWindow) { if (preScreenshotBounds) mainWindow.setBounds(preScreenshotBounds); mainWindow.show(); - mainWindow.setAlwaysOnTop(true, isMac ? 'floating' : 'screen-saver', 1); + mainWindow.setAlwaysOnTop(true, (isMac || isLinux) ? 'floating' : 'screen-saver', 1); } }); @@ -1642,7 +1655,7 @@ function createPipWindow(videoUrl: string, title: string, albumArt?: string | nu pipWindow.webContents.userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'; pipWindow.setAspectRatio(16 / 9); - pipWindow.setAlwaysOnTop(true, isMac ? 'floating' : 'screen-saver', 2); + pipWindow.setAlwaysOnTop(true, (isMac || isLinux) ? 'floating' : 'screen-saver', 2); const encodedUrl = encodeURIComponent(videoUrl); const encodedTitle = encodeURIComponent(title); diff --git a/electron/smtc-worker.cts b/electron/smtc-worker.cts index 2982f50..5acd179 100644 --- a/electron/smtc-worker.cts +++ b/electron/smtc-worker.cts @@ -4,6 +4,7 @@ let SMTCMonitor: any = null; let smtcAvailable = false; const isWin = process.platform === 'win32'; +const isLinux = process.platform === 'linux'; if (isWin) { try { diff --git a/package-lock.json b/package-lock.json index d2e84e7..5b5a889 100644 --- a/package-lock.json +++ b/package-lock.json @@ -96,7 +96,6 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -790,6 +789,7 @@ "dev": true, "license": "BSD-2-Clause", "optional": true, + "peer": true, "dependencies": { "cross-dirname": "^0.1.0", "debug": "^4.3.4", @@ -811,6 +811,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -827,6 +828,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "universalify": "^2.0.0" }, @@ -841,6 +843,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">= 10.0.0" } @@ -1465,7 +1468,6 @@ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", "license": "MIT", - "peer": true, "dependencies": { "@floating-ui/core": "^1.7.5", "@floating-ui/utils": "^0.2.11" @@ -2745,7 +2747,6 @@ "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.20.1.tgz", "integrity": "sha512-SwkPEWIfaDEZjC8SEIi4kZjqIYUbRgLUHUuQezo5GbphUNC8kM1pi3C3EtoOPtxXrEbY6e4pWEzW54Pcrd+rVA==", "license": "MIT", - "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -2981,7 +2982,6 @@ "resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.20.1.tgz", "integrity": "sha512-euBRAn0mkV7R2VEE+AuOt3R0j9RHEMFXamPFmtvTo8IInxDClusrm6mJoDjS8gCGAXsQCRiAe1SCQBPgGbOOwg==", "license": "MIT", - "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -3100,7 +3100,6 @@ "resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.20.1.tgz", "integrity": "sha512-JRc/v+OBH0qLTdvQ7HvHWTxGJH73QOf1MC0R8NhOX2QnAbg2mPFv1h+FjGa2gfLGuCXBdWQomjekWkUKbC4e5A==", "license": "MIT", - "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -3115,7 +3114,6 @@ "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.20.1.tgz", "integrity": "sha512-6kCiGLvpES4AxcEuOhb7HR7/xIeJWMjZlb6J7e8zpiIh5BoQc7NoRdctsnmFEjZvC19bIasccshHQ7H2zchWqw==", "license": "MIT", - "peer": true, "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", @@ -3415,7 +3413,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -3425,7 +3422,6 @@ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -3534,7 +3530,6 @@ "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", @@ -3883,7 +3878,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3924,7 +3918,6 @@ "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -4521,7 +4514,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -5277,7 +5269,8 @@ "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==", "dev": true, "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/cross-env": { "version": "10.1.0", @@ -5561,7 +5554,6 @@ "integrity": "sha512-glMJgnTreo8CFINujtAhCgN96QAqApDMZ8Vl1r8f0QT8QprvC1UCltV4CcWj20YoIyLZx6IUskaJZ0NV8fokcg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "app-builder-lib": "26.8.1", "builder-util": "26.8.1", @@ -6008,6 +6000,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "@electron/asar": "^3.2.1", "debug": "^4.1.1", @@ -6028,6 +6021,7 @@ "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", @@ -6262,7 +6256,6 @@ "integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -8050,8 +8043,7 @@ "url": "https://github.com/sponsors/lavrton" } ], - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/lazy-val": { "version": "1.0.5", @@ -9664,6 +9656,7 @@ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "minimist": "^1.2.6" }, @@ -10261,7 +10254,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -10304,7 +10296,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -10328,6 +10319,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "commander": "^9.4.0" }, @@ -10345,6 +10337,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "engines": { "node": "^12.20.0 || >=14" } @@ -10536,7 +10529,6 @@ "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz", "integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==", "license": "MIT", - "peer": true, "dependencies": { "orderedmap": "^2.0.0" } @@ -10566,7 +10558,6 @@ "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz", "integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==", "license": "MIT", - "peer": true, "dependencies": { "prosemirror-model": "^1.0.0", "prosemirror-transform": "^1.0.0", @@ -10615,7 +10606,6 @@ "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.6.tgz", "integrity": "sha512-mxpcDG4hNQa/CPtzxjdlir5bJFDlm0/x5nGBbStB2BWX+XOQ9M8ekEG+ojqB5BcVu2Rc80/jssCMZzSstJuSYg==", "license": "MIT", - "peer": true, "dependencies": { "prosemirror-model": "^1.20.0", "prosemirror-state": "^1.0.0", @@ -10754,7 +10744,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -11073,6 +11062,7 @@ "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "glob": "^7.1.3" }, @@ -11837,6 +11827,7 @@ "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "mkdirp": "^0.5.1", "rimraf": "~2.6.2" @@ -12098,7 +12089,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -12417,7 +12407,6 @@ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -12693,7 +12682,6 @@ "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -12751,4 +12739,4 @@ } } } -} \ No newline at end of file +} diff --git a/package.json b/package.json index 889a384..0fba5ab 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "type": "module", "main": "dist-electron/main.cjs", "scripts": { - "dev": "concurrently \"vite\" \"tsc -w -p electron/tsconfig.json\" \"wait-on tcp:5173 && wait-on dist-electron/main.cjs && wait-on dist-electron/preload.cjs && wait-on dist-electron/smtc-worker.cjs && electron .\"", + "dev": "concurrently \"vite\" \"tsc -w -p electron/tsconfig.json\" \"wait-on tcp:5173 && wait-on dist-electron/main.cjs && wait-on dist-electron/preload.cjs && wait-on dist-electron/smtc-worker.cjs && electron . --no-sandbox\"", "build": "tsc -b && vite build && tsc -p electron/tsconfig.json", "kobar-build": "node kobar-build.js", "lint": "eslint .", diff --git a/src/App.tsx b/src/App.tsx index 832c64f..a236ca3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -227,9 +227,9 @@ const App: React.FC = () => { unsubs.push(window.api.onForceCenterMiniMode(() => { const screenBounds = useAppStore.getState().screenBounds; const visibleHeight = screenBounds?.height ?? 800; - const isMac = useAppStore.getState().isMac; + // Vertically center it inside the visible screen bounds (Y=0 is top of screen) - useAppStore.getState().setMiniMode(true, { x: isMac ? Math.floor(window.innerWidth / 2) : 3000, y: Math.floor(visibleHeight / 2) }); + useAppStore.getState().setMiniMode(true, { x: Math.floor(window.innerWidth / 2), y: Math.floor(visibleHeight / 2) }); })); } if (window.api?.onOpenSettings) { diff --git a/src/components/layout/FloatingEye.tsx b/src/components/layout/FloatingEye.tsx index c8f7150..eba8b24 100644 --- a/src/components/layout/FloatingEye.tsx +++ b/src/components/layout/FloatingEye.tsx @@ -26,7 +26,7 @@ const FloatingEye: React.FC = () => { // left edge → slightly inward from the sidebar's expected CSS position // right edge → slightly inward from the other side const isMac = useAppStore(state => state.isMac); - const centerX = isMac ? Math.floor(window.innerWidth / 2) : 3000; + const centerX = Math.floor(window.innerWidth / 2); const safeDefaultX = edgePosition === 'left' ? (centerX - visibleW / 2 + 60) // Near left edge of visible area within ghost window : (centerX + visibleW / 2 - 60); // Near right edge of visible area within ghost window @@ -102,7 +102,7 @@ const FloatingEye: React.FC = () => { ); if (activeDisplay) { - const windowWidth = 6000; + const windowWidth = window.innerWidth; const newWinX = Math.floor(activeDisplay.workArea.x + (activeDisplay.workArea.width / 2) - (windowWidth / 2)); const newWinY = activeDisplay.workArea.y; diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx index 4930517..94dd6e4 100644 --- a/src/components/layout/Sidebar.tsx +++ b/src/components/layout/Sidebar.tsx @@ -237,7 +237,7 @@ const Sidebar: React.FC = () => { ); if (activeDisplay) { - const windowWidth = 6000; + const windowWidth = window.innerWidth; const newWinX = Math.floor(activeDisplay.workArea.x + (activeDisplay.workArea.width / 2) - (windowWidth / 2)); const newWinY = activeDisplay.workArea.y; @@ -307,8 +307,8 @@ const Sidebar: React.FC = () => { const physicalCurrentX = physicalOriginX + newX + (orientation === 'horizontal' ? 0 : sidebarWidth / 2); const physicalCurrentY = physicalOriginY + newY + (orientation === 'horizontal' ? sidebarWidth / 2 : 0); - let activeScreenPhysicalCenter = physicalOriginX + 3000; // Fallback to primary - let activeScreenPhysicalCenterY = physicalOriginY + 2000; + let activeScreenPhysicalCenter = physicalOriginX + Math.floor(window.innerWidth / 2); // Fallback to primary + let activeScreenPhysicalCenterY = physicalOriginY + Math.floor(window.innerHeight / 2); const activeMonitor = allDisplays.find(d => physicalCurrentX >= d.bounds.x && physicalCurrentX < (d.bounds.x + d.bounds.width) && @@ -389,11 +389,12 @@ const Sidebar: React.FC = () => { // and ends at 3000 + activeMonitorW / 2. const activeMonitorW = displayBounds?.width ?? screenBounds?.width ?? window.innerWidth; const activeMonitorH = displayBounds?.height ?? screenBounds?.height ?? window.innerHeight; - visibleLeft = 3000 - activeMonitorW / 2; - visibleRight = 3000 + activeMonitorW / 2; + const centerW = window.innerWidth / 2; + visibleLeft = centerW - activeMonitorW / 2; + visibleRight = centerW + activeMonitorW / 2; visibleTop = 0; visibleBottom = activeMonitorH; - activeScreenCenter = 3000; + activeScreenCenter = centerW; activeScreenCenterY = activeMonitorH / 2; } diff --git a/src/components/notes/SettingsPanel.tsx b/src/components/notes/SettingsPanel.tsx index 6cecca2..673e5ae 100644 --- a/src/components/notes/SettingsPanel.tsx +++ b/src/components/notes/SettingsPanel.tsx @@ -430,7 +430,7 @@ const SettingsPanel: React.FC = () => { const state = useAppStore.getState(); const visibleHeight = state.screenBounds?.height ?? 800; state.setMiniMode(true, { - x: state.isMac ? Math.floor(window.innerWidth / 2) : 3000, + x: Math.floor(window.innerWidth / 2), y: Math.floor(visibleHeight / 2) }); } diff --git a/src/store/useAppStore.ts b/src/store/useAppStore.ts index a83b842..17265f9 100644 --- a/src/store/useAppStore.ts +++ b/src/store/useAppStore.ts @@ -353,7 +353,10 @@ const defaultNotes: Note[] = [ export const useAppStore = create()( persist( (set, get) => ({ - isMac: window.api?.getPlatform ? window.api.getPlatform() === 'darwin' : false, + // isMac: true for macOS AND Linux — both use real screen-sized windows + // (as opposed to Windows' oversized 6000x4000 ghost window). + // This flag controls frontend layout calculations, NOT OS-specific features. + isMac: window.api?.getPlatform ? (window.api.getPlatform() === 'darwin' || window.api.getPlatform() === 'linux') : false, closeAllUtilityPopups: () => set({ @@ -560,8 +563,7 @@ export const useAppStore = create()( // Recalculate edgePosition so NotePanel/SettingsPanel opens on the correct side const screenW = state.screenBounds?.width ?? 1920; - const isMacPlatform = state.isMac; - const screenCenter = isMacPlatform ? (screenW / 2) : 3000; + const screenCenter = screenW / 2; const sidebarCenterX = sidebarX + (state.sidebarWidth / 2); updates.edgePosition = sidebarCenterX < screenCenter ? 'left' : 'right'; }