From 6ae23306ca3ffba896b3088310dc9be68098548c Mon Sep 17 00:00:00 2001 From: Silas Burger Date: Sat, 7 Jun 2025 21:36:23 +0200 Subject: [PATCH 1/2] Add DockClick spoon Signed-off-by: Silas Burger --- Source/DockClick.spoon/docs.json | 195 +++++++++++++++++++++++++++++++ Source/DockClick.spoon/init.lua | 172 +++++++++++++++++++++++++++ 2 files changed, 367 insertions(+) create mode 100644 Source/DockClick.spoon/docs.json create mode 100644 Source/DockClick.spoon/init.lua diff --git a/Source/DockClick.spoon/docs.json b/Source/DockClick.spoon/docs.json new file mode 100644 index 00000000..2c494d53 --- /dev/null +++ b/Source/DockClick.spoon/docs.json @@ -0,0 +1,195 @@ +[ + { + "Constant" : [ + + ], + "submodules" : [ + + ], + "Function" : [ + + ], + "Variable" : [ + + ], + "stripped_doc" : [ + + ], + "desc" : "Emulates clicking the currently active application in the dock to avoid hunting for app icons.", + "Deprecated" : [ + + ], + "type" : "Module", + "Constructor" : [ + + ], + "Field" : [ + + ], + "Method" : [ + { + "desc" : "Finds and clicks the currently active application in the dock", + "stripped_doc" : [ + "Finds and clicks the currently active application in the dock", + "" + ], + "def" : "DockClick:restoreWindow()", + "doc" : "Finds and clicks the currently active application in the dock\n\nParameters:\n * None\n\nReturns:\n * None\n\nNotes:\n * This method uses accessibility features to interact with the dock\n * The method will match partial application names (e.g., \"Chrome\" matches \"Google Chrome\")\n * If the active application is not found in the dock, nothing happens", + "notes" : [ + " * This method uses accessibility features to interact with the dock", + " * The method will match partial application names (e.g., \"Chrome\" matches \"Google Chrome\")", + " * If the active application is not found in the dock, nothing happens" + ], + "signature" : "DockClick:restoreWindow()", + "type" : "Method", + "returns" : [ + " * None", + "" + ], + "name" : "restoreWindow", + "parameters" : [ + " * None", + "" + ] + }, + { + "desc" : "Binds hotkeys for DockClick", + "stripped_doc" : [ + "Binds hotkeys for DockClick", + "" + ], + "def" : "DockClick:bindHotkeys(mapping)", + "doc" : "Binds hotkeys for DockClick\n\nParameters:\n * mapping - A table containing hotkey modifier\/key details for the following items:\n * restore - Restore the window of the currently active application (default: cmd+alt+w)\n\nReturns:\n * The DockClick object\n\nNotes:\n * If no mapping is provided, the default hotkey (cmd+alt+w) is used\n * Example usage:\n ```lua\n spoon.DockClick:bindHotkeys({\n restore = {{\"cmd\", \"alt\"}, \"w\"}\n })\n ```", + "notes" : [ + " * If no mapping is provided, the default hotkey (cmd+alt+w) is used", + " * Example usage:", + " ```lua", + " spoon.DockClick:bindHotkeys({", + " restore = {{\"cmd\", \"alt\"}, \"w\"}", + " })", + " ```" + ], + "signature" : "DockClick:bindHotkeys(mapping)", + "type" : "Method", + "returns" : [ + " * The DockClick object", + "" + ], + "name" : "bindHotkeys", + "parameters" : [ + " * mapping - A table containing hotkey modifier\/key details for the following items:", + " * restore - Restore the window of the currently active application (default: cmd+alt+w)", + "" + ] + }, + { + "desc" : "Initializes the DockClick spoon", + "stripped_doc" : [ + "Initializes the DockClick spoon", + "" + ], + "def" : "DockClick:init()", + "doc" : "Initializes the DockClick spoon\n\nParameters:\n * None\n\nReturns:\n * The DockClick object\n\nNotes:\n * Currently this method does nothing, but is reserved for future use", + "notes" : [ + " * Currently this method does nothing, but is reserved for future use" + ], + "signature" : "DockClick:init()", + "type" : "Method", + "returns" : [ + " * The DockClick object", + "" + ], + "name" : "init", + "parameters" : [ + " * None", + "" + ] + } + ], + "Command" : [ + + ], + "items" : [ + { + "desc" : "Binds hotkeys for DockClick", + "stripped_doc" : [ + "Binds hotkeys for DockClick", + "" + ], + "def" : "DockClick:bindHotkeys(mapping)", + "doc" : "Binds hotkeys for DockClick\n\nParameters:\n * mapping - A table containing hotkey modifier\/key details for the following items:\n * restore - Restore the window of the currently active application (default: cmd+alt+w)\n\nReturns:\n * The DockClick object\n\nNotes:\n * If no mapping is provided, the default hotkey (cmd+alt+w) is used\n * Example usage:\n ```lua\n spoon.DockClick:bindHotkeys({\n restore = {{\"cmd\", \"alt\"}, \"w\"}\n })\n ```", + "notes" : [ + " * If no mapping is provided, the default hotkey (cmd+alt+w) is used", + " * Example usage:", + " ```lua", + " spoon.DockClick:bindHotkeys({", + " restore = {{\"cmd\", \"alt\"}, \"w\"}", + " })", + " ```" + ], + "signature" : "DockClick:bindHotkeys(mapping)", + "type" : "Method", + "returns" : [ + " * The DockClick object", + "" + ], + "name" : "bindHotkeys", + "parameters" : [ + " * mapping - A table containing hotkey modifier\/key details for the following items:", + " * restore - Restore the window of the currently active application (default: cmd+alt+w)", + "" + ] + }, + { + "desc" : "Initializes the DockClick spoon", + "stripped_doc" : [ + "Initializes the DockClick spoon", + "" + ], + "def" : "DockClick:init()", + "doc" : "Initializes the DockClick spoon\n\nParameters:\n * None\n\nReturns:\n * The DockClick object\n\nNotes:\n * Currently this method does nothing, but is reserved for future use", + "notes" : [ + " * Currently this method does nothing, but is reserved for future use" + ], + "signature" : "DockClick:init()", + "type" : "Method", + "returns" : [ + " * The DockClick object", + "" + ], + "name" : "init", + "parameters" : [ + " * None", + "" + ] + }, + { + "desc" : "Finds and clicks the currently active application in the dock", + "stripped_doc" : [ + "Finds and clicks the currently active application in the dock", + "" + ], + "def" : "DockClick:restoreWindow()", + "doc" : "Finds and clicks the currently active application in the dock\n\nParameters:\n * None\n\nReturns:\n * None\n\nNotes:\n * This method uses accessibility features to interact with the dock\n * The method will match partial application names (e.g., \"Chrome\" matches \"Google Chrome\")\n * If the active application is not found in the dock, nothing happens", + "notes" : [ + " * This method uses accessibility features to interact with the dock", + " * The method will match partial application names (e.g., \"Chrome\" matches \"Google Chrome\")", + " * If the active application is not found in the dock, nothing happens" + ], + "signature" : "DockClick:restoreWindow()", + "type" : "Method", + "returns" : [ + " * None", + "" + ], + "name" : "restoreWindow", + "parameters" : [ + " * None", + "" + ] + } + ], + "doc" : "Emulates clicking the currently active application in the dock to avoid hunting for app icons.\n\nThis spoon is useful for quickly restoring the primary window of the frontmost application\nby simulating a click on its dock icon. This is particularly helpful when you want to bring\nback a closed or hidden window.\n\nDownload: [https:\/\/github.com\/Hammerspoon\/Spoons\/raw\/master\/Spoons\/DockClick.spoon.zip](https:\/\/github.com\/Hammerspoon\/Spoons\/raw\/master\/Spoons\/DockClick.spoon.zip)", + "name" : "DockClick" + } +] diff --git a/Source/DockClick.spoon/init.lua b/Source/DockClick.spoon/init.lua new file mode 100644 index 00000000..75f50991 --- /dev/null +++ b/Source/DockClick.spoon/init.lua @@ -0,0 +1,172 @@ +--- === DockClick === +--- +--- Emulates clicking the currently active application in the dock to avoid hunting for app icons. +--- +--- This spoon is useful for quickly restoring the primary window of the frontmost application +--- by simulating a click on its dock icon. This is particularly helpful when you want to bring +--- back a closed or hidden window. +--- +--- Download: [https://github.com/Hammerspoon/Spoons/raw/master/Spoons/DockClick.spoon.zip](https://github.com/Hammerspoon/Spoons/raw/master/Spoons/DockClick.spoon.zip) + +local obj = {} +obj.__index = obj + +-- Metadata +obj.name = "DockClick" +obj.version = "1.0" +obj.author = "Silas Burger" +obj.homepage = "https://github.com/Hammerspoon/Spoons" +obj.license = "MIT - https://opensource.org/licenses/MIT" + +--- DockClick:restoreWindow() +--- Method +--- Finds and clicks the currently active application in the dock +--- +--- Parameters: +--- * None +--- +--- Returns: +--- * None +--- +--- Notes: +--- * This method uses accessibility features to interact with the dock +--- * The method will match partial application names (e.g., "Chrome" matches "Google Chrome") +--- * If the active application is not found in the dock, nothing happens +function obj:restoreWindow() + -- Get the Dock element + local axuielement = require("hs.axuielement") + local dockElement = axuielement.applicationElement("com.apple.dock") + + -- Function to get the currently active application + local function getFrontmostApp() + local frontApp = hs.application.frontmostApplication() + if obj.logger then + obj.logger.d("Currently active application: " .. frontApp:name()) + end + return frontApp:name() + end + + -- Function to get the list of application names in the Dock + local function getDockAppNames() + local appNames = {} + + -- Loop through the children of the Dock element + for _, child in ipairs(dockElement[1]) do + if child.AXSubrole == "AXApplicationDockItem" then + table.insert(appNames, child.AXTitle) -- App name is stored in AXTitle + end + end + return appNames + end + + -- Helper function to remove trailing numbers and spaces + local function trimTrailingNumbers(str) + return string.gsub(str, "[%s%d]+$", "") + end + + -- Comparison function with partial string match + local function findAppInDock(activeAppName, dockApps) + -- Convert to lowercase + local activeAppName = trimTrailingNumbers(activeAppName:lower()) + + for _, appName in ipairs(dockApps) do + local dockAppName = trimTrailingNumbers(appName:lower()) + + -- Partial string matching: check if the simplified active app name is part of any dock app name + if dockAppName:find(activeAppName, 1, true) then + if obj.logger then + obj.logger.d("Matched dock item: " .. appName) + end + return appName + end + end + return nil + end + + -- Press dock child based on name + local function pressDockChild(appName) + for _, child in ipairs(dockElement[1]) do + if child.AXSubrole == "AXApplicationDockItem" then + if child.AXTitle == appName then + child:performAction("AXPress") -- Simulates a click on the Dock item + if obj.logger then + obj.logger.d("Pressed dock item: " .. appName) + end + return + end + end + end + end + + -- Get the Dock applications + local dockAppNames = getDockAppNames() + + -- Check if the active application is in the Dock + local currentlyActiveApp = getFrontmostApp() + local dockAppName = findAppInDock(currentlyActiveApp, dockAppNames) + if dockAppName then + if obj.logger then + obj.logger.d("The active app is in the Dock.") + end + pressDockChild(dockAppName) + else + if obj.logger then + obj.logger.d("The active app is NOT in the Dock.") + end + end +end + +--- DockClick:bindHotkeys(mapping) +--- Method +--- Binds hotkeys for DockClick +--- +--- Parameters: +--- * mapping - A table containing hotkey modifier/key details for the following items: +--- * restore - Restore the window of the currently active application (default: cmd+alt+w) +--- +--- Returns: +--- * The DockClick object +--- +--- Notes: +--- * If no mapping is provided, the default hotkey (cmd+alt+w) is used +--- * Example usage: +--- ```lua +--- spoon.DockClick:bindHotkeys({ +--- restore = {{"cmd", "alt"}, "w"} +--- }) +--- ``` +function obj:bindHotkeys(mapping) + if mapping and mapping.restore then + -- Use the provided mapping + local def = { + restore = hs.fnutils.partial(self.restoreWindow, self), + } + hs.spoons.bindHotkeysToSpec(def, mapping) + else + -- Bind default hotkey (cmd + alt + w) if no mapping is provided + hs.hotkey.bind({ "cmd", "alt" }, "w", function() + self:restoreWindow() + end) + end + return self +end + +--- DockClick:init() +--- Method +--- Initializes the DockClick spoon +--- +--- Parameters: +--- * None +--- +--- Returns: +--- * The DockClick object +--- +--- Notes: +--- * Currently this method does nothing, but is reserved for future use +function obj:init() + -- Optional: Initialize a logger for debugging + -- obj.logger = hs.logger.new('DockClick', 'debug') + return self +end + +return obj From cbd954431288688b4cbb4b6bd42901a47e93279c Mon Sep 17 00:00:00 2001 From: Silas Burger Date: Sat, 7 Jun 2025 22:13:09 +0200 Subject: [PATCH 2/2] Renamed method Signed-off-by: Silas Burger --- Source/DockClick.spoon/docs.json | 54 ++++++++++++++++---------------- Source/DockClick.spoon/init.lua | 14 ++++----- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/Source/DockClick.spoon/docs.json b/Source/DockClick.spoon/docs.json index 2c494d53..22394a99 100644 --- a/Source/DockClick.spoon/docs.json +++ b/Source/DockClick.spoon/docs.json @@ -33,20 +33,20 @@ "Finds and clicks the currently active application in the dock", "" ], - "def" : "DockClick:restoreWindow()", + "def" : "DockClick:clickFrontmost()", "doc" : "Finds and clicks the currently active application in the dock\n\nParameters:\n * None\n\nReturns:\n * None\n\nNotes:\n * This method uses accessibility features to interact with the dock\n * The method will match partial application names (e.g., \"Chrome\" matches \"Google Chrome\")\n * If the active application is not found in the dock, nothing happens", "notes" : [ " * This method uses accessibility features to interact with the dock", " * The method will match partial application names (e.g., \"Chrome\" matches \"Google Chrome\")", " * If the active application is not found in the dock, nothing happens" ], - "signature" : "DockClick:restoreWindow()", + "signature" : "DockClick:clickFrontmost()", "type" : "Method", "returns" : [ " * None", "" ], - "name" : "restoreWindow", + "name" : "clickFrontmost", "parameters" : [ " * None", "" @@ -59,13 +59,13 @@ "" ], "def" : "DockClick:bindHotkeys(mapping)", - "doc" : "Binds hotkeys for DockClick\n\nParameters:\n * mapping - A table containing hotkey modifier\/key details for the following items:\n * restore - Restore the window of the currently active application (default: cmd+alt+w)\n\nReturns:\n * The DockClick object\n\nNotes:\n * If no mapping is provided, the default hotkey (cmd+alt+w) is used\n * Example usage:\n ```lua\n spoon.DockClick:bindHotkeys({\n restore = {{\"cmd\", \"alt\"}, \"w\"}\n })\n ```", + "doc" : "Binds hotkeys for DockClick\n\nParameters:\n * mapping - A table containing hotkey modifier\/key details for the following items:\n * click - Restore the window of the currently active application by clicking its icon in the dock (default: cmd+alt+w)\n\nReturns:\n * The DockClick object\n\nNotes:\n * If no mapping is provided, the default hotkey (cmd+alt+w) is used\n * Example usage:\n ```lua\n spoon.DockClick:bindHotkeys({\n click = {{\"cmd\", \"alt\"}, \"w\"}\n })\n ```", "notes" : [ " * If no mapping is provided, the default hotkey (cmd+alt+w) is used", " * Example usage:", " ```lua", " spoon.DockClick:bindHotkeys({", - " restore = {{\"cmd\", \"alt\"}, \"w\"}", + " click = {{\"cmd\", \"alt\"}, \"w\"}", " })", " ```" ], @@ -78,7 +78,7 @@ "name" : "bindHotkeys", "parameters" : [ " * mapping - A table containing hotkey modifier\/key details for the following items:", - " * restore - Restore the window of the currently active application (default: cmd+alt+w)", + " * click - Restore the window of the currently active application by clicking its icon in the dock (default: cmd+alt+w)", "" ] }, @@ -117,13 +117,13 @@ "" ], "def" : "DockClick:bindHotkeys(mapping)", - "doc" : "Binds hotkeys for DockClick\n\nParameters:\n * mapping - A table containing hotkey modifier\/key details for the following items:\n * restore - Restore the window of the currently active application (default: cmd+alt+w)\n\nReturns:\n * The DockClick object\n\nNotes:\n * If no mapping is provided, the default hotkey (cmd+alt+w) is used\n * Example usage:\n ```lua\n spoon.DockClick:bindHotkeys({\n restore = {{\"cmd\", \"alt\"}, \"w\"}\n })\n ```", + "doc" : "Binds hotkeys for DockClick\n\nParameters:\n * mapping - A table containing hotkey modifier\/key details for the following items:\n * click - Restore the window of the currently active application by clicking its icon in the dock (default: cmd+alt+w)\n\nReturns:\n * The DockClick object\n\nNotes:\n * If no mapping is provided, the default hotkey (cmd+alt+w) is used\n * Example usage:\n ```lua\n spoon.DockClick:bindHotkeys({\n click = {{\"cmd\", \"alt\"}, \"w\"}\n })\n ```", "notes" : [ " * If no mapping is provided, the default hotkey (cmd+alt+w) is used", " * Example usage:", " ```lua", " spoon.DockClick:bindHotkeys({", - " restore = {{\"cmd\", \"alt\"}, \"w\"}", + " click = {{\"cmd\", \"alt\"}, \"w\"}", " })", " ```" ], @@ -136,53 +136,53 @@ "name" : "bindHotkeys", "parameters" : [ " * mapping - A table containing hotkey modifier\/key details for the following items:", - " * restore - Restore the window of the currently active application (default: cmd+alt+w)", + " * click - Restore the window of the currently active application by clicking its icon in the dock (default: cmd+alt+w)", "" ] }, { - "desc" : "Initializes the DockClick spoon", + "desc" : "Finds and clicks the currently active application in the dock", "stripped_doc" : [ - "Initializes the DockClick spoon", + "Finds and clicks the currently active application in the dock", "" ], - "def" : "DockClick:init()", - "doc" : "Initializes the DockClick spoon\n\nParameters:\n * None\n\nReturns:\n * The DockClick object\n\nNotes:\n * Currently this method does nothing, but is reserved for future use", + "def" : "DockClick:clickFrontmost()", + "doc" : "Finds and clicks the currently active application in the dock\n\nParameters:\n * None\n\nReturns:\n * None\n\nNotes:\n * This method uses accessibility features to interact with the dock\n * The method will match partial application names (e.g., \"Chrome\" matches \"Google Chrome\")\n * If the active application is not found in the dock, nothing happens", "notes" : [ - " * Currently this method does nothing, but is reserved for future use" + " * This method uses accessibility features to interact with the dock", + " * The method will match partial application names (e.g., \"Chrome\" matches \"Google Chrome\")", + " * If the active application is not found in the dock, nothing happens" ], - "signature" : "DockClick:init()", + "signature" : "DockClick:clickFrontmost()", "type" : "Method", "returns" : [ - " * The DockClick object", + " * None", "" ], - "name" : "init", + "name" : "clickFrontmost", "parameters" : [ " * None", "" ] }, { - "desc" : "Finds and clicks the currently active application in the dock", + "desc" : "Initializes the DockClick spoon", "stripped_doc" : [ - "Finds and clicks the currently active application in the dock", + "Initializes the DockClick spoon", "" ], - "def" : "DockClick:restoreWindow()", - "doc" : "Finds and clicks the currently active application in the dock\n\nParameters:\n * None\n\nReturns:\n * None\n\nNotes:\n * This method uses accessibility features to interact with the dock\n * The method will match partial application names (e.g., \"Chrome\" matches \"Google Chrome\")\n * If the active application is not found in the dock, nothing happens", + "def" : "DockClick:init()", + "doc" : "Initializes the DockClick spoon\n\nParameters:\n * None\n\nReturns:\n * The DockClick object\n\nNotes:\n * Currently this method does nothing, but is reserved for future use", "notes" : [ - " * This method uses accessibility features to interact with the dock", - " * The method will match partial application names (e.g., \"Chrome\" matches \"Google Chrome\")", - " * If the active application is not found in the dock, nothing happens" + " * Currently this method does nothing, but is reserved for future use" ], - "signature" : "DockClick:restoreWindow()", + "signature" : "DockClick:init()", "type" : "Method", "returns" : [ - " * None", + " * The DockClick object", "" ], - "name" : "restoreWindow", + "name" : "init", "parameters" : [ " * None", "" diff --git a/Source/DockClick.spoon/init.lua b/Source/DockClick.spoon/init.lua index 75f50991..27469fc8 100644 --- a/Source/DockClick.spoon/init.lua +++ b/Source/DockClick.spoon/init.lua @@ -18,7 +18,7 @@ obj.author = "Silas Burger" obj.homepage = "https://github.com/Hammerspoon/Spoons" obj.license = "MIT - https://opensource.org/licenses/MIT" ---- DockClick:restoreWindow() +--- DockClick:clickFrontmost() --- Method --- Finds and clicks the currently active application in the dock --- @@ -32,7 +32,7 @@ obj.license = "MIT - https://opensource.org/licenses/MIT" --- * This method uses accessibility features to interact with the dock --- * The method will match partial application names (e.g., "Chrome" matches "Google Chrome") --- * If the active application is not found in the dock, nothing happens -function obj:restoreWindow() +function obj:clickFrontmost() -- Get the Dock element local axuielement = require("hs.axuielement") local dockElement = axuielement.applicationElement("com.apple.dock") @@ -122,7 +122,7 @@ end --- --- Parameters: --- * mapping - A table containing hotkey modifier/key details for the following items: ---- * restore - Restore the window of the currently active application (default: cmd+alt+w) +--- * click - Restore the window of the currently active application by clicking its icon in the dock (default: cmd+alt+w) --- --- Returns: --- * The DockClick object @@ -132,20 +132,20 @@ end --- * Example usage: --- ```lua --- spoon.DockClick:bindHotkeys({ ---- restore = {{"cmd", "alt"}, "w"} +--- click = {{"cmd", "alt"}, "w"} --- }) --- ``` function obj:bindHotkeys(mapping) - if mapping and mapping.restore then + if mapping and mapping.click then -- Use the provided mapping local def = { - restore = hs.fnutils.partial(self.restoreWindow, self), + click = hs.fnutils.partial(self.clickFrontmost, self), } hs.spoons.bindHotkeysToSpec(def, mapping) else -- Bind default hotkey (cmd + alt + w) if no mapping is provided hs.hotkey.bind({ "cmd", "alt" }, "w", function() - self:restoreWindow() + self:clickFrontmost() end) end return self