Skip to content

Instantly share code, notes, and snippets.

@itissid
Created January 12, 2025 17:08
Show Gist options
  • Save itissid/d9fb9500a27ab52a8d978e867094e8b9 to your computer and use it in GitHub Desktop.
Save itissid/d9fb9500a27ab52a8d978e867094e8b9 to your computer and use it in GitHub Desktop.
--------------------------------------------------------------------------------
-- HYPER KEYS + GLOBAL VARIABLES
--------------------------------------------------------------------------------
local hyper = { "ctrl", "alt", "cmd" }
-- We'll store the 'targetScreen' the user picks.
local targetScreen = nil
-- Which application do we want to open/move?
-- You can change this to any bundle ID or app name you like (e.g., "Safari").
local myAppBundleID = "com.openai.chat"
--------------------------------------------------------------------------------
-- Global table to store frames, and a reference to the last screen used
--------------------------------------------------------------------------------
local windowFramesByScreen = {}
local lastScreenName = nil
--------------------------------------------------------------------------------
-- Store the current window frame for the window’s screen
--------------------------------------------------------------------------------
local function storeWindowFrameForScreen(win)
local screen = win:screen()
if not screen then return end
local screenName = screen:name()
local frame = win:frame()
windowFramesByScreen[screenName] = frame
hs.alert.show("Stored frame for screen: " .. screenName)
end
--------------------------------------------------------------------------------
-- Restore the frame if we have it stored for the given screen
--------------------------------------------------------------------------------
local function restoreWindowFrameForScreen(win, screen)
local screenName = screen:name()
local storedFrame = windowFramesByScreen[screenName]
if storedFrame then
win:setFrame(storedFrame)
hs.alert.show("Restored frame on " .. screenName)
else
hs.alert.show("No stored frame for screen: " .. screenName)
end
end
--------------------------------------------------------------------------------
-- STEP 1: DESIGNATE A SCREEN (ctrl + alt + cmd + [)
--------------------------------------------------------------------------------
hs.hotkey.bind(hyper, "[", function()
-- Option 1: Mark the screen under the mouse pointer:
-- targetScreen = hs.mouse.getCurrentScreen()
-- Option 2: Mark the screen of the currently focused window:
local focusedWin = hs.window.focusedWindow()
if focusedWin then
targetScreen = focusedWin:screen()
else
targetScreen = hs.screen.mainScreen() -- fallback if no window is focused
end
if targetScreen then
hs.alert.show("Screen designated: " .. targetScreen:name())
else
hs.alert.show("No screen found to designate.")
end
end)
--------------------------------------------------------------------------------
-- STEP 2: OPEN OR MOVE APP TO THAT SCREEN & SPACE (ctrl + alt + cmd + ])
--------------------------------------------------------------------------------
hs.hotkey.bind(hyper, "]", function()
if not targetScreen then
hs.alert.show("No target screen selected (press [ first).")
return
end
-- Attempt to get the running application
local app = hs.application.get(myAppBundleID)
-- Debug info
for k, w in pairs(app:allWindows()) do
print(k, w:title(), w:role(), w:subrole())
end
-- If the app isn't running, launch it
if not app then
hs.alert.show("App not running; opening " .. myAppBundleID)
hs.application.open(myAppBundleID)
-- Wait a moment for the app to launch
hs.timer.doAfter(1.0, function()
local newApp = hs.application.get(myAppBundleID)
if newApp then
moveAppToTargetScreen(newApp, targetScreen)
end
end)
else
-- If already running, just move it
moveAppToTargetScreen(app, targetScreen)
end
end)
--------------------------------------------------------------------------------
-- HELPER FUNCTION: MOVE THE APP'S MAIN WINDOW TO THE CHOSEN SCREEN & SPACE
--------------------------------------------------------------------------------
function moveAppToTargetScreen(app, screen)
local win = app:mainWindow()
if not win then
print(".")
print("W:No Main Window Found for " .. app:name().. "Trying another API.")
return
end
-- We will move the window to the user-selected screen
storeWindowFrameForScreen(win)
win:moveToScreen(screen)
win:focus()
-- If you want to keep the “exact same sizing” it had last time it was on this screen:
restoreWindowFrameForScreen(win, screen)
----------------------------------------------------------------------------
-- (Optional) ALSO MOVE TO THE CURRENTLY ACTIVE SPACE ON THAT SCREEN:
----------------------------------------------------------------------------
-- This part requires an "undocumented" Hammerspoon extension, or the
-- asmagill 'Spaces' spoon, to explicitly move windows between Spaces.
-- If you want the window to follow the *active* desktop on that screen,
-- you can do something like:
--
-- local spaces = require("hs._asm.undocumented.spaces")
-- local uuid = screen:getUUID()
-- local activeSpace = spaces.activeSpaceOnScreen(uuid)
-- if activeSpace then
-- spaces.moveWindowToSpace(win:id(), activeSpace)
-- end
----------------------------------------------------------------------------
hs.alert.show("Moved " .. app:name() .. " to " .. screen:name())
lastScreenName = win:screen():name()
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment