Skip to content
manosPublic

About

My Hammerspoon Spoons

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

12 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Spoons

Small Hammerspoon tools for Safari and everyday macOS tasks.

  • AirPodsMeet — Mute or unmute a Safari Google Meet call with your AirPods, with spoken confirmation.
  • KSheet — See the current app's menu shortcuts with ⌃⌥⌘K; Escape closes the sheet.
  • MissionControlSend — Shift-click a window in Mission Control to send it to the next desktop.
  • PasteToPicker — Attach a clipboard image through a file picker with ⌃⌥⌘V.
  • PersonalSafari — Shift-click links into your chosen personal Safari window, even on another desktop.
  • SafariTabGroups — Search and switch Safari tab groups with ⌃⌥⌘L.
  • TorrentToQbit — Download a .torrent, choose a category, and send it to your qBittorrent server.

AirPodsMeet — mute Meet without reaching for your Mac

You're walking around during a Google Meet call in Safari and want to mute or unmute from your AirPods. Safari rejects that control, so AirPodsMeet detects the rejected request and presses Meet's microphone button for you, even while Safari is behind another app.

Keep the call in Safari's selected tab and press the normal AirPods mute control. Once Meet confirms the change, a bundled Lily voice clip says “Muted” or “Unmuted.” The clips play at 95% speed after a configurable 0.6-second pause. No ElevenLabs account, API key, or voice download is needed to use the Spoon.

The macOS rejection tones still play; this Spoon reacts after the rejection. Detection relies on macOS log messages and English Meet controls, and requires the call to remain in the selected Safari tab.

Control: AirPods mute gesture · Download · Setup and details

KSheet — remember the current app's shortcuts

You want to use the keyboard, but cannot remember the shortcut for a command in the app you're using. Press ⌃⌥⌘K to see its native menu shortcuts together, organized by menu. Press Escape, or the same shortcut again, to close the sheet.

This is a fork of ashfinal's KSheet, retaining the upstream credits and adding Escape dismissal and an offline layout. It shows shortcuts exposed in the app's menus, rather than every hidden command or website shortcut.

Shortcut: ⌃⌥⌘K · Download · Setup and details

MissionControlSend — move windows to the next desktop

You're sorting windows between desktops in Mission Control and want to send one away without dragging it into the desktop strip. Open Mission Control with Control–↑, Shift-click a window thumbnail, then release Shift. The window moves to the next ordinary desktop on that display, wrapping from the last desktop to the first.

The Spoon briefly moves the pointer to perform the drag, follows the desktop thumbnail through its animation, and verifies that the window reached its destination. Real mouse or keyboard input cancels the operation. Full-screen spaces are skipped, and ambiguous windows are left alone.

This is experimental and has been tested on macOS 27.2.1. It depends on macOS's Mission Control accessibility controls, which can change between versions. A fixed destination desktop can also be configured.

Gesture: Shift-click in Mission Control · Download · Setup and details

PasteToPicker — attach a screenshot without saving it yourself

You have an image on the clipboard, but a website only offers Choose File. Normally you would save the image, find it in the upload dialog, and click Open. PasteToPicker handles those steps from the keyboard.

For example, copy a screenshot with Control–Shift–Command–4, click a website's upload button, then press ⌃⌥⌘V in the native file picker and release the keys. The Spoon saves a temporary PNG, enters its path, and presses the picker's Open/Upload button. The dialog closes and the image is handed to the app.

It works with clipboard images and native macOS Open dialogs, including Safari upload pickers. It does not paste text or operate custom website file choosers. Accepting the picker may start an upload immediately; a separate Send or Publish button in the app remains yours to press.

Shortcut: ⌃⌥⌘V · Download · Setup and details

PersonalSafari — keep personal links in your personal window

You keep work and personal browsing in separate Safari windows, perhaps on different desktops. A personal link arrives in a chat or appears while you're browsing for work, and opening it normally puts it in the wrong window.

Focus the Safari window you want to use for personal browsing and press ⌃⌥⌘P once. Then Shift-click a link to add it as a background tab in that window. Your current window and desktop stay in place, so you can read the link later without interrupting what you're doing.

Accessible links can be intercepted directly in Safari and other apps. The optional default-browser setup also handles external links from apps that do not expose them to Accessibility. Ordinary clicks keep their normal routing. Select the destination again after closing it or restarting Safari.

Choose destination: ⌃⌥⌘P · Send a link: Shift-click · Download · Setup and details

SafariTabGroups — jump to a group by typing its name

You organize Safari into groups such as Research, Shopping, and Travel, but switching means opening the sidebar or hunting through a menu. SafariTabGroups puts those group names in a searchable keyboard chooser.

While in Safari, press ⌃⌥⌘L, type part of a group name, and press Enter. Use ↑/↓ to move through matches or Escape to cancel. The selected group opens in the window where you invoked the shortcut.

The list follows that window's available groups and profile, includes its ungrouped tabs, and marks the current group. It searches group names, not individual tab titles or browsing history.

Shortcut: ⌃⌥⌘L · Download · Setup and details

TorrentToQbit — send downloaded torrents to your server

You download a .torrent on your Mac, but qBittorrent runs on another machine. Instead of opening its Web UI and uploading the file manually, TorrentToQbit watches Downloads and shows a searchable list of your server's categories when a new torrent file is ready.

Type to filter, use ↑/↓ to choose a category, and press Enter to add it. Escape cancels. The Spoon checks for duplicates, verifies that the torrent reached qBittorrent, and keeps the local file. Your server's category settings and completion automations still apply.

Keep the server address and Web UI login in a separate private JSON file outside the Spoon; the package contains only reusable code and example settings. Existing files are ignored when the watcher starts, and magnet links are not handled.

Trigger: New .torrent in Downloads · Download · Setup and details

Requirements

macOS, Hammerspoon, and Accessibility permission are required. PersonalSafari also requires permission to control Safari. These are independently distributed Spoons, not part of the official Hammerspoon collection.

Install

Download a ZIP above or from Releases, unzip it, then double-click the .spoon folder to install it with Hammerspoon. Add only the Spoons you want to your ~/.hammerspoon/init.lua:

hs.loadSpoon("AirPodsMeet"):start()

hs.loadSpoon("MissionControlSend"):start()

hs.loadSpoon("KSheet")
spoon.KSheet:bindHotkeys({
  toggle = {{"ctrl", "alt", "cmd"}, "K"},
})

hs.loadSpoon("SafariTabGroups")
spoon.SafariTabGroups:bindHotkeys({
  show = {{"ctrl", "alt", "cmd"}, "L"},
})

hs.loadSpoon("PersonalSafari")
spoon.PersonalSafari:bindHotkeys({
  selectWindow = {{"ctrl", "alt", "cmd"}, "P"},
}):start()

hs.loadSpoon("PasteToPicker")
spoon.PasteToPicker:bindHotkeys({
  pasteImageToPicker = {{"ctrl", "alt", "cmd"}, "V"},
})

-- First create the private config described in TorrentToQbit's setup guide.
hs.loadSpoon("TorrentToQbit"):start()

See PersonalSafari's README for its one-time target-window selection and optional default-browser setup. To keep Hammerspoon out of the Dock and app switcher, add hs.dockIcon(false) to your own configuration; this affects Hammerspoon globally.

SpoonInstall

If you already use SpoonInstall, register this repository and install the desired Spoon:

hs.loadSpoon("SpoonInstall")
spoon.SpoonInstall.repos.manos = {
  url = "https://github.com/manos/Spoons",
  desc = "Safari, clipboard, and download utilities",
  branch = "main",
}
spoon.SpoonInstall:andUse("SafariTabGroups", {
  repo = "manos",
  hotkeys = {show = {{"ctrl", "alt", "cmd"}, "L"}},
})

Use either manual setup or SpoonInstall for a given Spoon. SpoonInstall's andUse installs missing packages; it is not an automatic updater for already installed versions.

Privacy

This repository contains reusable source, synthetic tests, documentation, and installable archives. It does not include personal Hammerspoon configuration, settings, browser profiles, browsing history, clipboard images, diagnostic captures, or credentials.

The Spoons contain no analytics or telemetry. PersonalSafari reads clicked link addresses to open them in Safari and stores the selected window/process identity and previous browser handler in local Hammerspoon settings. SafariTabGroups reads group names from Safari's native menu while the chooser is in use. PasteToPicker writes the clipboard image to a local temporary PNG and automatically accepts the file picker, which may start an upload through the destination app; see its README for retention and file-permission details. Opening a web link or submitting a file through the destination application has that application's normal network behavior.

AirPodsMeet listens to a narrowly filtered local system-log stream and inspects Safari's accessible call controls after a matching gesture. It does not capture microphone audio or save logs, meeting addresses, or page contents. Its two bundled voice clips were synthesized from the fixed words “Muted” and “Unmuted”; playback is entirely local.

TorrentToQbit reads new torrent files from the configured download folder and sends their metadata to your configured qBittorrent server over HTTPS. Its host and login stay in a separate private config file; session cookies stay in memory. Successful torrent hashes and timestamps are stored locally to avoid repeat uploads. Neither the config nor your downloaded files are included in the source or ZIP.

KSheet reads the current app's native menu shortcuts and displays them locally. This fork loads no external scripts or styles and does not store or transmit menu contents. Its upstream attribution link is an ordinary website link.

MissionControlSend reads local window identities, titles, and desktop layout to identify the clicked thumbnail and verify the move. It sends no data over the network and does not save window contents or titles.

Development

Sources live under Source/, installable ZIPs under Spoons/, and the SpoonInstall index at docs/docs.json.

Run all Lua checks (Lua 5.3 or newer):

./scripts/check.sh

Rebuild ZIPs and catalog using Python 3's standard library:

python3 -m venv .venv
.venv/bin/python scripts/package.py

Archives use an explicit file allowlist and fixed timestamps. They omit macOS extended attributes and other local metadata. Tests use synthetic data and do not control real applications. Accessibility identifiers can change between macOS/Safari versions; automated checks do not replace testing in a real native picker or Safari window.

Code and documentation are MIT licensed. AirPodsMeet's bundled Lily clips were generated on ElevenLabs' free plan and are for noncommercial use with attribution; see the audio terms. Replace or remove those clips for uses their terms do not cover; missing clips fall back to the system voice.

About

My Hammerspoon Spoons

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages