---------------------------------------------------------------------------------------------------- -- Settings ---------------------------------------------------------------------------------------------------- hs.autoLaunch(true) hs.automaticallyCheckForUpdates(true) hs.consoleOnTop(true) hs.dockIcon(false) hs.menuIcon(false) hs.uploadCrashData(false) hs.window.animationDuration = 0 configWatcher = hs.pathwatcher.new(hs.configdir, hs.reload) configWatcher:start() local moonlanderMode = false local maximizeMode = false ---------------------------------------------------------------------------------------------------- -- Utilities ---------------------------------------------------------------------------------------------------- local modifier = { cmd = "cmd", shift = "shift", ctrl = "ctrl", option = "alt", } local modifiers = { hyper = { modifier.cmd, modifier.shift, modifier.ctrl, modifier.option }, window = { modifier.ctrl, modifier.option }, clipboard = { modifier.ctrl, modifier.cmd } } local bundleID = { activityMonitor = "com.apple.ActivityMonitor", finder = "com.apple.finder", firefox = "org.mozilla.firefox", emacs = "org.gnu.emacs", iterm = "com.googlecode.iterm2", orion = "com.kagi.kagimacOS", safariTechnologyPreview = "com.apple.SafariTechnologyPreview", spotify = "com.spotify.client", bitwarden = "com.bitwarden.desktop", teams = "com.microsoft.teams", faclieThings = "com.electron.nativefier.facilethings-nativefier-cf88de", timeular = "com.timeular.zei", logseq = "com.electron.logseq" } local usbDevice = { moonlander = "Moonlander Mark I" } local function languageIsGerman() return hs.host.locale.preferredLanguages()[1]:sub(0, 2) == "de" end ---------------------------------------------------------------------------------------------------- -- Menu ---------------------------------------------------------------------------------------------------- local function menuItems() return { { title = "Hammerspoon " .. hs.processInfo.version, disabled = true }, { title = "-" }, { title = "Moonlander Mode", checked = moonlanderMode, fn = function() moonlanderDetected(not moonlanderMode) end }, -- { -- title = "Maximize Mode", -- checked = maximizeMode, -- fn = function() maximizeMode = not maximizeMode end -- }, { title = "-" }, { title = "Reload", fn = hs.reload }, { title = "Console...", fn = hs.openConsole }, { title = "-" }, { title = "Quit", fn = function() hs.application.get(hs.processInfo.processID):kill() end } } end menu = hs.menubar.new() menu:setMenu(menuItems) ---------------------------------------------------------------------------------------------------- -- Moonlander Detection ---------------------------------------------------------------------------------------------------- local moonlanderModeConfig = { [false] = { keyboardLayout = "Colemak DH ISO copy", icon = hs.configdir .. "/assets/statusicon_off.tiff" }, [true] = { keyboardLayout = "EurKEY v1.2", icon = hs.configdir .. "/assets/statusicon_on.tiff" } } local function isDeviceMoonlander(device) return device.productName == usbDevice.moonlander end function moonlanderDetected(connected) moonlanderMode = connected hs.keycodes.setLayout(moonlanderModeConfig[connected].keyboardLayout) menu:setIcon(moonlanderModeConfig[connected].icon) end local function searchMoonlander() local usbDevices = hs.usb.attachedDevices() local moonlanderConnected = hs.fnutils.find(usbDevices, isDeviceMoonlander) ~= nil moonlanderDetected(moonlanderConnected) end searchMoonlander() usbWatcher = hs.usb.watcher.new(function(event) if event.productName == usbDevice.moonlander then moonlanderDetected(event.eventType == "added") end end) usbWatcher:start() caffeinateWatcher = hs.caffeinate.watcher.new(function(event) if event == hs.caffeinate.watcher.systemDidWake then searchMoonlander() end end) caffeinateWatcher:start() ---------------------------------------------------------------------------------------------------- -- Window Management ---------------------------------------------------------------------------------------------------- --hs.window.filter.ignoreAlways = { -- ["Mail Web Content"] = true, -- ["Mail-Webinhalt"] = true, -- ["QLPreviewGenerationExtension (Finder)"] = true, -- ["Reeder Web Content"] = true, -- ["Reeder-Webinhalt"] = true, -- ["Safari Web Content (Cached)"] = true, -- ["Safari Web Content (Prewarmed)"] = true, -- ["Safari Web Content"] = true, -- ["Safari Technology Preview Web Content (Cached)"] = true, -- ["Safari Technology Preview Web Content (Prewarmed)"] = true, -- ["Safari Technology Preview Web Content"] = true, -- ["Safari-Webinhalt (im Cache)"] = true, -- ["Safari-Webinhalt (vorgeladen)"] = true, -- ["Safari-Webinhalt"] = true, -- ["Strongbox (Safari)"] = true, --} --windowFilter = hs.window.filter.new({ -- "App Store", -- "Code", -- "DataGrip", -- "Firefox", -- "Fork", -- "Fotos", -- "Google Chrome", -- "Vivaldi", -- "IntelliJ IDEA", -- "Mail", -- "Emacs", -- "Microsoft Outlook", -- "Microsoft Teams", -- "Music", -- "Musik", -- "Photos", -- "Postman", -- "Reeder", -- "Safari", -- "Safari Technology Preview", -- "Spotify", -- "Strongbox", -- "BitWarden", -- "Logseq", -- "Timeular", -- "Tower", --}) --windowFilter:subscribe({ hs.window.filter.windowCreated, hs.window.filter.windowFocused }, function(window) -- if maximizeMode and window ~= nil and window:isStandard() and window:frame().h > 500 then -- window:maximize() -- end --end) ---------------------------------------------------------------------------------------------------- -- Keyboard Shortcuts ---------------------------------------------------------------------------------------------------- -- function showHideBundleId(bundleId) -- local focusedWindow = hs.window.focusedWindow() -- if focusedWindow ~= nil and focusedWindow:application():bundleID() == bundleId then -- window is focused -- focusedWindow:close() -- hide -- else -- hs.application.launchOrFocusByBundleID(bundleId) -- hs.window.focusedWindow():centerOnScreen(hs.mouse.getCurrentScreen()) -- end -- end -- hs.loadSpoon("MiroWindowsManager") -- hs.window.animationDuration = 0 -- spoon.MiroWindowsManager:bindHotkeys({ -- up = {modifiers.window, "up"}, -- right = {modifiers.window, "right"}, -- down = {modifiers.window, "down"}, -- left = {modifiers.window, "left"}, -- fullscreen = {modifiers.window, "return"}, -- nextscreen = {modifiers.hyper, "right"} -- }) -- hs.hotkey.bind(modifiers.hyper, hs.keycodes.map.delete, function() hs.caffeinate.lockScreen() end) -- hs.hotkey.bind(modifiers.hyper, "a", function() showHideBundleId(bundleID.activityMonitor) end) -- hs.hotkey.bind(modifiers.hyper, "o", function() showHideBundleId(bundleID.orion) end) -- hs.hotkey.bind(modifiers.hyper, "f", function() showHideBundleId(bundleID.faclieThings) end) -- hs.hotkey.bind(modifiers.hyper, "p", function() showHideBundleId(bundleID.timeular) end) -- hs.hotkey.bind(modifiers.hyper, "b", function() showHideBundleId(bundleID.bitwarden) end) -- hs.hotkey.bind(modifiers.hyper, "t", function() showHideBundleId(bundleID.iterm) end) ---------------------------------------------------------------------------------------------------- -- Mouse Shortcuts ---------------------------------------------------------------------------------------------------- local function handleMouse4() hs.eventtap.keyStroke({ modifier.cmd }, "left") end local function handleMouse5() hs.eventtap.keyStroke({ modifier.cmd }, "right") end -- bind mouse3/4 to back and forward mouseTap = hs.eventtap.new({ hs.eventtap.event.types.otherMouseDown }, function(event) if event:getButtonState(3) then handleMouse4() return true elseif event:getButtonState(4) then handleMouse5() return true end return false end) mouseTap:start()