Compare commits
2 Commits
c9d516c6fd
...
75200ff664
| Author | SHA1 | Date | |
|---|---|---|---|
| 75200ff664 | |||
| 73e5617176 |
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"Work": {
|
||||||
|
"keepOpen": ["Slack", "Microsoft Teams", "Visual Studio Code", "Terminal", "Google Chrome"],
|
||||||
|
"close": ["Discord", "Steam", "Komga", "Kavita"],
|
||||||
|
"dimAlpha": 0.3
|
||||||
|
},
|
||||||
|
"Home": {
|
||||||
|
"keepOpen": ["Discord", "Steam", "Komga", "Kavita", "Plex"],
|
||||||
|
"close": ["Slack", "Microsoft Teams"],
|
||||||
|
"dimAlpha": 0.1
|
||||||
|
},
|
||||||
|
"Study": {
|
||||||
|
"keepOpen": ["AFFiNE", "Obsidian", "Anki", "Preview", "Safari"],
|
||||||
|
"close": ["Slack", "Microsoft Teams", "Discord", "Steam", "Instagram"],
|
||||||
|
"dimAlpha": 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,111 +1,53 @@
|
|||||||
-- Allow external apps like Raycast to execute custom Hammerspoon Lua functions
|
|
||||||
hs.allowAppleScript(true)
|
|
||||||
-- ========================================================================
|
-- ========================================================================
|
||||||
-- HEADLESS WORKSPACE BACKGROUND ENGINE (FORCED AT BOOT)
|
-- HEADLESS WORKSPACE BACKGROUND ENGINE (FORCED AT BOOT)
|
||||||
-- ========================================================================
|
-- ========================================================================
|
||||||
|
hs.allowAppleScript(true)
|
||||||
hs.menuIcon(false)
|
hs.menuIcon(false)
|
||||||
hs.dockIcon(false)
|
hs.dockIcon(false)
|
||||||
hs.ipc.cliInstall("/opt/homebrew")
|
hs.ipc.cliInstall("/opt/homebrew")
|
||||||
hs.alert.show("Hammerspoon Headless Daemon Active", 2)
|
hs.alert.show("Hammerspoon Headless Daemon Active", 2)
|
||||||
|
|
||||||
-- ~/.hammerspoon/init.lua
|
-- Core Subsystems and Global Bindings
|
||||||
-- Load Config First
|
|
||||||
-- config = require("Config")
|
|
||||||
--
|
|
||||||
-- Load your HyperKey
|
|
||||||
require("HyperKey")
|
require("HyperKey")
|
||||||
|
|
||||||
require('SearchWindows')
|
require('SearchWindows')
|
||||||
require('Caffeine')
|
require('Caffeine')
|
||||||
require('AppBorders')
|
require('AppBorders')
|
||||||
|
|
||||||
-- CRITICAL FOR RAYCAST INTERACTION: Bind the return value to a global variable
|
-- IPC Bridges & Trackers
|
||||||
LayoutSelector = require('LayoutSelector')
|
LayoutSelector = require('LayoutSelector')
|
||||||
|
|
||||||
-- require('System_Tweaks') -- Used for Time Machine Throttle Disable
|
-- Active Modules
|
||||||
-- require("Focus") -- Does not work with layout saver - Not needed if using Monocle - Focusdim is better than Monocle
|
require("modules.mouseJiggle").start()
|
||||||
-- require("FocusMode") -- Removed since I am using Focusdim
|
|
||||||
--
|
|
||||||
-- Network Center is currently disabled since menus are not working in Hammerspoon - Will re-enable once I can fix the menu issues
|
|
||||||
-- Network = require("NetworkCenter")
|
|
||||||
--
|
|
||||||
require("modules.mouseJiggle").start() -- used to trigger Focusdim
|
|
||||||
|
|
||||||
-- Load the window management module
|
|
||||||
local windowMgr = require("WindowManager")
|
local windowMgr = require("WindowManager")
|
||||||
local productivity = require("productivity")
|
local productivity = require("productivity")
|
||||||
|
|
||||||
-- For Affine
|
-- DEEP FOCUS MODULE (New Integration)
|
||||||
|
local focus = require("modules.focus")
|
||||||
|
|
||||||
|
-- Affine Note Engine Integration
|
||||||
local quickNote = require("affine_quick_note")
|
local quickNote = require("affine_quick_note")
|
||||||
quickNote.init()
|
quickNote.init()
|
||||||
require("affine_clipper"):init()
|
require("affine_clipper"):init()
|
||||||
|
|
||||||
-- Load Spoon Files
|
-- Spoons Engine & External Tool Integrations
|
||||||
hs.loadSpoon('SpoonInstall')
|
hs.loadSpoon('SpoonInstall')
|
||||||
hs.loadSpoon('SpeedMenu')
|
hs.loadSpoon('SpeedMenu')
|
||||||
hs.loadSpoon('BrewInfo')
|
hs.loadSpoon('BrewInfo')
|
||||||
|
|
||||||
-- ==========================================
|
-- Run isolated Spoon setup configurations
|
||||||
-- SPOON CONFIGURATION
|
require("modules.spoon_config")
|
||||||
-- ==========================================
|
|
||||||
|
|
||||||
---- SpeedMenu Config
|
-- ========================================================================
|
||||||
if spoon.SpeedMenu then
|
-- RAYCAST / INTER-PROCESS COMMUNICATION (IPC) BRIDGE BINDINGS
|
||||||
-- Define the Fix Function (includes MAC and IPv6)
|
-- ========================================================================
|
||||||
local function applyFullMenuFix()
|
-- Expose the focus module methods globally so the hs CLI tool can call them
|
||||||
local interface = spoon.SpeedMenu.interface or "en0"
|
function ActivateDeepFocus(profileName)
|
||||||
local ssid = hs.wifi.currentNetwork() or "Disconnected"
|
return focus.activate(profileName)
|
||||||
local details = hs.network.interfaceDetails(interface)
|
|
||||||
|
|
||||||
local ipv4 = (details and details.IPv4) and details.IPv4.Addresses[1] or "N/A"
|
|
||||||
local ipv6 = (details and details.IPv6) and details.IPv6.Addresses[1] or "N/A"
|
|
||||||
|
|
||||||
-- Get MAC Address via shell
|
|
||||||
local macaddr = hs.execute('ifconfig ' .. interface .. ' | grep ether | awk \'{print $2}\''):gsub("%s+", "")
|
|
||||||
|
|
||||||
local menuitems = {
|
|
||||||
{ title = "SSID: " .. ssid, fn = function() hs.pasteboard.setContents(ssid) end },
|
|
||||||
{ title = "IPv4: " .. ipv4, fn = function() hs.pasteboard.setContents(ipv4) end },
|
|
||||||
{ title = "IPv6: " .. ipv6, fn = function() hs.pasteboard.setContents(ipv6) end },
|
|
||||||
{ title = "MAC: " .. macaddr, fn = function() hs.pasteboard.setContents(macaddr) end },
|
|
||||||
{ title = "-" },
|
|
||||||
{ title = "Rescan Network Interfaces", fn = function() spoon.SpeedMenu:rescan() end }
|
|
||||||
}
|
|
||||||
spoon.SpeedMenu.menubar:setMenu(menuitems)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Hook the rescan method
|
function ClearDeepFocus()
|
||||||
local oldRescan = spoon.SpeedMenu.rescan
|
return focus.clear()
|
||||||
spoon.SpeedMenu.rescan = function(self)
|
|
||||||
oldRescan(self)
|
|
||||||
applyFullMenuFix()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Toggle Logic (Starts as OFF)
|
-- Boot Confirmation
|
||||||
local speedMenuRunning = false
|
|
||||||
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "W", function()
|
|
||||||
if speedMenuRunning then
|
|
||||||
spoon.SpeedMenu:stop()
|
|
||||||
hs.alert.show("SpeedMenu Stopped")
|
|
||||||
else
|
|
||||||
spoon.SpeedMenu:start() -- This puts it in the menu bar
|
|
||||||
applyFullMenuFix() -- This populates the data
|
|
||||||
hs.alert.show("SpeedMenu Started")
|
|
||||||
end
|
|
||||||
speedMenuRunning = not speedMenuRunning
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
---- SpeedMenu Config END
|
|
||||||
|
|
||||||
---- BrewInfo
|
|
||||||
if spoon.BrewInfo then
|
|
||||||
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "I", function()
|
|
||||||
spoon.BrewInfo:showBrewInfo()
|
|
||||||
end)
|
|
||||||
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "J", function()
|
|
||||||
spoon.BrewInfo:showBrewInfoCurSel()
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
---- BrewInfo END
|
|
||||||
|
|
||||||
hs.alert.show("Hammerspoon Config Reloaded")
|
hs.alert.show("Hammerspoon Config Reloaded")
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
local focus = require("modules.focus")
|
||||||
|
|
||||||
|
-- Expose clean functions globally for the hs CLI tool
|
||||||
|
function ActivateDeepFocus(profileName)
|
||||||
|
return focus.activate(profileName)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ClearDeepFocus()
|
||||||
|
return focus.clear()
|
||||||
|
end
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
local M = {}
|
||||||
|
|
||||||
|
local focusCanvas = nil
|
||||||
|
local activeProfile = nil
|
||||||
|
local configPath = hs.configdir .. "/focus_profiles.json"
|
||||||
|
|
||||||
|
local function loadProfiles()
|
||||||
|
local file = io.open(configPath, "r")
|
||||||
|
if not file then
|
||||||
|
print("Error: Could not open focus_profiles.json")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local content = file:read("*a")
|
||||||
|
file:close()
|
||||||
|
return hs.json.decode(content)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function manageApps(profileConfig)
|
||||||
|
for _, appName in ipairs(profileConfig.close) do
|
||||||
|
local app = hs.application.get(appName)
|
||||||
|
if app then app:kill() end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, appName in ipairs(profileConfig.keepOpen) do
|
||||||
|
hs.application.launchOrFocus(appName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function setFocusOverlay(alpha)
|
||||||
|
if focusCanvas then
|
||||||
|
focusCanvas:delete()
|
||||||
|
focusCanvas = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if not alpha or alpha == 0 then return end
|
||||||
|
|
||||||
|
local mainScreen = hs.screen.mainScreen()
|
||||||
|
local rect = mainScreen:frame()
|
||||||
|
|
||||||
|
focusCanvas = hs.canvas.new(rect)
|
||||||
|
focusCanvas:insertElement({
|
||||||
|
action = "fill",
|
||||||
|
type = "rectangle",
|
||||||
|
fillColor = { red = 0, green = 0, blue = 0, alpha = alpha }
|
||||||
|
})
|
||||||
|
|
||||||
|
focusCanvas:level(50)
|
||||||
|
focusCanvas:show()
|
||||||
|
|
||||||
|
local frontApp = hs.application.frontmostApplication()
|
||||||
|
if frontApp then
|
||||||
|
local win = frontApp:mainWindow()
|
||||||
|
if win then win:focus() end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.activate(profileName)
|
||||||
|
local profiles = loadProfiles()
|
||||||
|
if not profiles or not profiles[profileName] then
|
||||||
|
print("Error: Profile '" .. tostring(profileName) .. "' not found.")
|
||||||
|
return "Profile not found"
|
||||||
|
end
|
||||||
|
|
||||||
|
activeProfile = profileName
|
||||||
|
local currentProfileConfig = profiles[profileName]
|
||||||
|
|
||||||
|
manageApps(currentProfileConfig)
|
||||||
|
setFocusOverlay(currentProfileConfig.dimAlpha)
|
||||||
|
|
||||||
|
return "Activated " .. profileName .. " mode"
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.clear()
|
||||||
|
setFocusOverlay(nil)
|
||||||
|
activeProfile = nil
|
||||||
|
return "Focus mode cleared"
|
||||||
|
end
|
||||||
|
|
||||||
|
hs.screen.watcher.new(function()
|
||||||
|
if activeProfile then
|
||||||
|
local profiles = loadProfiles()
|
||||||
|
if profiles and profiles[activeProfile] then
|
||||||
|
setFocusOverlay(profiles[activeProfile].dimAlpha)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end):start()
|
||||||
|
|
||||||
|
return M
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
-- ========================================================================
|
||||||
|
-- SPOON RUNTIME CONFIGURATION MODULE
|
||||||
|
-- ========================================================================
|
||||||
|
|
||||||
|
---- SpeedMenu Implementation
|
||||||
|
if spoon.SpeedMenu then
|
||||||
|
-- Define the Fix Function (includes MAC and IPv6 parsing via shell)
|
||||||
|
local function applyFullMenuFix()
|
||||||
|
local interface = spoon.SpeedMenu.interface or "en0"
|
||||||
|
local ssid = hs.wifi.currentNetwork() or "Disconnected"
|
||||||
|
local details = hs.network.interfaceDetails(interface)
|
||||||
|
|
||||||
|
local ipv4 = (details and details.IPv4) and details.IPv4.Addresses[1] or "N/A"
|
||||||
|
local ipv6 = (details and details.IPv6) and details.IPv6.Addresses[1] or "N/A"
|
||||||
|
|
||||||
|
-- Get MAC Address via native shell pipe
|
||||||
|
local macaddr = hs.execute('ifconfig ' .. interface .. ' | grep ether | awk \'{print $2}\''):gsub("%s+", "")
|
||||||
|
|
||||||
|
local menuitems = {
|
||||||
|
{ title = "SSID: " .. ssid, fn = function() hs.pasteboard.setContents(ssid) end },
|
||||||
|
{ title = "IPv4: " .. ipv4, fn = function() hs.pasteboard.setContents(ipv4) end },
|
||||||
|
{ title = "IPv6: " .. ipv6, fn = function() hs.pasteboard.setContents(ipv6) end },
|
||||||
|
{ title = "MAC: " .. macaddr, fn = function() hs.pasteboard.setContents(macaddr) end },
|
||||||
|
{ title = "-" },
|
||||||
|
{ title = "Rescan Network Interfaces", fn = function() spoon.SpeedMenu:rescan() end }
|
||||||
|
}
|
||||||
|
spoon.SpeedMenu.menubar:setMenu(menuitems)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Overwrite the baseline rescan hook cleanly
|
||||||
|
local oldRescan = spoon.SpeedMenu.rescan
|
||||||
|
spoon.SpeedMenu.rescan = function(self)
|
||||||
|
oldRescan(self)
|
||||||
|
applyFullMenuFix()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Toggle Engine State Manager
|
||||||
|
local speedMenuRunning = false
|
||||||
|
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "W", function()
|
||||||
|
if speedMenuRunning then
|
||||||
|
spoon.SpeedMenu:stop()
|
||||||
|
hs.alert.show("SpeedMenu Stopped")
|
||||||
|
else
|
||||||
|
spoon.SpeedMenu:start()
|
||||||
|
applyFullMenuFix()
|
||||||
|
hs.alert.show("SpeedMenu Started")
|
||||||
|
end
|
||||||
|
speedMenuRunning = not speedMenuRunning
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
---- BrewInfo Implementation
|
||||||
|
if spoon.BrewInfo then
|
||||||
|
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "I", function()
|
||||||
|
spoon.BrewInfo:showBrewInfo()
|
||||||
|
end)
|
||||||
|
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "J", function()
|
||||||
|
spoon.BrewInfo:showBrewInfoCurSel()
|
||||||
|
end)
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user