229 lines
6.8 KiB
Lua
229 lines
6.8 KiB
Lua
-- SETTINGS: Customize your engines and display names here
|
|
local engines = {
|
|
-- primary = { name = "Google", url = "https://google.ca/search?q=" },
|
|
primary = { name = "Google/StartPage", url = "https://www.startpage.com/do/search?query=" },
|
|
secondary = { name = "Wikipedia", url = "https://wikipedia.org/wiki/Special:Search?search="},
|
|
tertiary = { name = "YouTube", url = "https://youtube.com/results?search_query=" },
|
|
chatgpt = { name = "ChatGPT", url = "https://chatgpt.com/?q=" },
|
|
maps = { name = "Maps", url = "https://www.google.com/maps/search/" },
|
|
spotify = { name = "Spotify", url = "https://open.spotify.com/search/" }
|
|
}
|
|
|
|
local closeTimeout = 1.0
|
|
local gracePeriod = 3.0
|
|
|
|
-- Global variables
|
|
searchView = nil
|
|
searchTimer = nil
|
|
escWatcher = nil
|
|
clickOutsideWatcher = nil
|
|
|
|
local timeSinceLeft = 0
|
|
local hasEnteredWindow = false
|
|
local autoKillTime = 0
|
|
|
|
------------------------------------------------------------
|
|
-- CLOSE SEARCH
|
|
------------------------------------------------------------
|
|
local function closeSearch()
|
|
if searchView then searchView:delete() searchView = nil end
|
|
if searchTimer then searchTimer:stop() searchTimer = nil end
|
|
if escWatcher then escWatcher:stop() escWatcher = nil end
|
|
if clickOutsideWatcher then clickOutsideWatcher:stop() clickOutsideWatcher = nil end
|
|
|
|
timeSinceLeft = 0
|
|
hasEnteredWindow = false
|
|
autoKillTime = 0
|
|
end
|
|
|
|
------------------------------------------------------------
|
|
-- PERFORM SEARCH
|
|
------------------------------------------------------------
|
|
local function performSearch(engine)
|
|
hs.alert.show("Searching " .. engine.name .. "...", 1)
|
|
|
|
hs.eventtap.keyStroke({"cmd"}, "c")
|
|
|
|
hs.timer.doAfter(0.2, function()
|
|
|
|
local query = hs.pasteboard.getContents()
|
|
if not query or query == "" then return end
|
|
|
|
closeSearch()
|
|
|
|
-- RESET TRACKERS FOR NEW SEARCH
|
|
timeSinceLeft = 0
|
|
hasEnteredWindow = false
|
|
autoKillTime = 0
|
|
|
|
local url = engine.url .. hs.http.encodeForQuery(query)
|
|
|
|
local screen = hs.mouse.getCurrentScreen()
|
|
local screenFrame = screen:frame()
|
|
|
|
local width, height = 500, 600
|
|
local x = screenFrame.x + (screenFrame.w - width) / 2
|
|
local y = screenFrame.y + (screenFrame.h - height) / 2
|
|
|
|
searchView = hs.webview.new({
|
|
x = x,
|
|
y = y,
|
|
w = width,
|
|
h = height
|
|
})
|
|
|
|
searchView:windowStyle({"titled", "utility", "closable", "resizable"})
|
|
searchView:level(hs.drawing.windowLevels.floating)
|
|
|
|
searchView:userAgent(
|
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko)"
|
|
)
|
|
|
|
searchView:url(url)
|
|
searchView:show()
|
|
searchView:bringToFront()
|
|
|
|
--------------------------------------------------------
|
|
-- ESC CLOSE
|
|
--------------------------------------------------------
|
|
escWatcher = hs.eventtap.new(
|
|
{hs.eventtap.event.types.keyDown},
|
|
function(event)
|
|
|
|
if event:getKeyCode() == 53 then
|
|
closeSearch()
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
):start()
|
|
|
|
--------------------------------------------------------
|
|
-- CLICK OUTSIDE CLOSE
|
|
--------------------------------------------------------
|
|
clickOutsideWatcher = hs.eventtap.new(
|
|
{hs.eventtap.event.types.leftMouseDown},
|
|
function()
|
|
|
|
if not searchView then return false end
|
|
|
|
local mousePos = hs.mouse.absolutePosition()
|
|
local winFrame = searchView:frame()
|
|
|
|
local isOutside =
|
|
mousePos.x < winFrame.x or
|
|
mousePos.x > (winFrame.x + winFrame.w) or
|
|
mousePos.y < winFrame.y or
|
|
mousePos.y > (winFrame.y + winFrame.h)
|
|
|
|
if isOutside then closeSearch() end
|
|
|
|
return false
|
|
end
|
|
):start()
|
|
|
|
--------------------------------------------------------
|
|
-- AUTO CLOSE TIMER
|
|
--------------------------------------------------------
|
|
searchTimer = hs.timer.doEvery(0.1, function()
|
|
|
|
if not searchView then return end
|
|
|
|
local mousePos = hs.mouse.absolutePosition()
|
|
local winFrame = searchView:frame()
|
|
|
|
local isOutside =
|
|
mousePos.x < winFrame.x or
|
|
mousePos.x > (winFrame.x + winFrame.w) or
|
|
mousePos.y < winFrame.y or
|
|
mousePos.y > (winFrame.y + winFrame.h)
|
|
|
|
if not isOutside then
|
|
hasEnteredWindow = true
|
|
timeSinceLeft = 0
|
|
else
|
|
if hasEnteredWindow then
|
|
|
|
timeSinceLeft = timeSinceLeft + 0.1
|
|
|
|
if timeSinceLeft >= closeTimeout then
|
|
closeSearch()
|
|
end
|
|
|
|
else
|
|
|
|
autoKillTime = autoKillTime + 0.1
|
|
|
|
if autoKillTime >= gracePeriod then
|
|
closeSearch()
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
end)
|
|
|
|
end)
|
|
end
|
|
|
|
------------------------------------------------------------
|
|
-- RIGHT CLICK TRIGGERS
|
|
------------------------------------------------------------
|
|
if clickWatcher then clickWatcher:stop() end
|
|
|
|
clickWatcher = hs.eventtap.new(
|
|
{hs.eventtap.event.types.rightMouseDown},
|
|
function(event)
|
|
|
|
local flags = event:getFlags()
|
|
|
|
if flags.cmd and flags.shift then
|
|
performSearch(engines.maps)
|
|
return true
|
|
|
|
elseif flags.alt and flags.shift then
|
|
performSearch(engines.spotify)
|
|
return true
|
|
|
|
elseif flags.cmd then
|
|
performSearch(engines.primary)
|
|
return true
|
|
|
|
elseif flags.alt then
|
|
performSearch(engines.secondary)
|
|
return true
|
|
|
|
elseif flags.ctrl then
|
|
performSearch(engines.tertiary)
|
|
return true
|
|
|
|
elseif flags.shift then
|
|
performSearch(engines.chatgpt)
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
):start()
|
|
|
|
------------------------------------------------------------
|
|
-- CMD + ENTER → OPEN CURRENT PAGE
|
|
------------------------------------------------------------
|
|
hs.hotkey.bind({"cmd"}, "return", function()
|
|
|
|
if searchView then
|
|
|
|
local currentPage = searchView:url()
|
|
|
|
if currentPage then
|
|
hs.urlevent.openURL(currentPage)
|
|
else
|
|
hs.alert.show("No page loaded")
|
|
end
|
|
|
|
else
|
|
hs.alert.show("No search window open")
|
|
end
|
|
|
|
end) |