Created
October 28, 2024 12:13
-
-
Save webpro/21cf05c81d2f624f1ece67ff722dc32d to your computer and use it in GitHub Desktop.
Raycast extension: organize windows
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { WindowManagement } from "@raycast/api"; | |
import { runAppleScript } from "run-applescript"; | |
type Desktop = WindowManagement.Desktop; | |
type Window = WindowManagement.Window; | |
const maximize = (screen: Desktop, win: Window) => setWindowBounds(screen, win.id); | |
// 75% left on/if second display | |
const left = (screen: Desktop, win: Window) => setWindowBounds(screen, win.id, screen.size.width * 0.75); | |
// 25% right stacked on/if second display | |
const rightStacked = (screen: Desktop, win: Window, size: number, index: number) => { | |
const height = screen.size.height / size; | |
setWindowBounds(screen, win.id, screen.size.width * 0.25, height, screen.size.width * 0.75, height * index); | |
}; | |
const configuration = { | |
"com.google.Chrome": { name: "Google Chrome", behavior: maximize, windows: new Set<Window>() }, | |
"com.microsoft.VSCode": { name: "Code", behavior: left, windows: new Set<Window>() }, | |
"com.apple.Terminal": { name: "Terminal", behavior: rightStacked, windows: new Set<Window>() }, | |
"com.DanPristupov.Fork": { name: "Fork", behavior: maximize, windows: new Set<Window>() }, | |
} as const; | |
const isSupportedApp = (bundleId: string): bundleId is keyof typeof configuration => bundleId in configuration; | |
const moveWindows = async (name: string, x: number) => | |
await runAppleScript(`tell application "System Events" | |
tell process "${name}" | |
repeat with win in windows | |
set position of win to {${x + 1}, 0} | |
end repeat | |
end tell | |
end tell | |
`); | |
const setWindowBounds = async (d: Desktop, id: string, width = d.size.width, height = d.size.height, x = 0, y = 0) => { | |
WindowManagement.setWindowBounds({ id, bounds: { position: { x, y }, size: { width, height } } }); | |
}; | |
export default async function Command() { | |
const desktops = await WindowManagement.getDesktops(); | |
const builtin = desktops.find((desktop) => desktop.screenId.toLowerCase().includes("built-in")); | |
const wide = desktops.find((desktop) => desktop.size.width >= 2560); | |
// since we only have `WindowManagement.getWindowsOnActiveDesktop` we bring in two scripts to prep the app windows | |
if (wide) { | |
// script 1: move the windows to the second screen | |
for (const config of Object.values(configuration)) | |
if (config.behavior === left || config.behavior === rightStacked) await moveWindows(config.name, wide.size.width); | |
// script 2: bad way to give focus to the second screen we want to move windows to... | |
await runAppleScript(`tell application "Code" to activate`); | |
} | |
// Ideally we'd be able to get all windows on all desktops, WindowManagement.setWindowBounds already has desktopId | |
const windows = await WindowManagement.getWindowsOnActiveDesktop(); | |
for (const config of Object.values(configuration)) config.windows.clear(); | |
for (const window of windows) { | |
const id = window.application?.bundleId; | |
if (id && isSupportedApp(id)) configuration[id].windows.add(window); | |
} | |
const screen = wide ?? builtin; | |
for (const config of Object.values(configuration)) { | |
for (const window of config.windows) { | |
config.behavior(screen, window, config.windows.size, Array.from(config.windows).indexOf(window)); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment