Hammerspoon Integration and Startup Sequence¶
The integration with Hammerspoon is the cornerstone of VimAbl's ability to automatically react to system and application-level events on macOS. It acts as the "glue" that monitors Ableton Live and launches the backend Python server at the right moment, creating a seamless user experience.
This document details the architecture of the Hammerspoon scripts and the step-by-step startup sequence.
Core Components¶
The Hammerspoon logic is modular, broken down into several key Lua scripts located in src/hammerspoon/:
-
app_watcher.lua: The entry point. Its sole responsibility is to watch for macOS applications launching and terminating. It specifically looks for "Live.app". -
project_watcher.lua: Activated by theapp_watcher. This script watches specific filesystem directories for changes to Ableton Live project files (.als). It is designed to be efficient, with a "broad" scanning mode and a "narrow" single-project mode. -
config.lua: A user-configurable file that defines which directories theproject_watchershould monitor. This allows users to add their own project folders. -
websocket_manager.lua: This script manages the lifecycle of the Python WebSocket server. It contains the logic to start, stop, and restart the server process usinguv. -
live_state.lua: A utility script that communicates with Ableton Live to perform actions, most critically, to export the project's state as an XML file.
Startup Sequence Diagram¶
The following diagram illustrates the complete startup process, from launching Ableton Live to the WebSocket server being ready.
sequenceDiagram
participant User
participant Hammerspoon
participant AppWatcher as app_watcher.lua
participant ProjectWatcher as project_watcher.lua
participant WSManager as websocket_manager.lua
participant PythonServer as src/main.py
participant AbletonLive as Live.app
User->>AbletonLive: Launches Ableton Live
Hammerspoon->>AppWatcher: Forwards 'launched' event for Live.app
AppWatcher->>ProjectWatcher: Calls start() after 2s delay
Note over ProjectWatcher: Enters "Broad Mode", watching all configured directories.
ProjectWatcher->>Hammerspoon: Creates path watchers for dirs in config.lua
User->>AbletonLive: Saves project (e.g., MyProject.als)
AbletonLive-->>Hammerspoon: Filesystem change event for .als file
Hammerspoon->>ProjectWatcher: Forwards filesystem event to onFileChanged() callback
ProjectWatcher->>ProjectWatcher: Debounces and verifies .als file change
ProjectWatcher->>WSManager: Calls start(path/to/MyProject.als)
WSManager->>AbletonLive: Requests project XML export (via live_state.lua)
AbletonLive-->>WSManager: Returns path to exported XML file
Note over WSManager: Constructs command: 'uv run python -m src.main ...'
WSManager->>PythonServer: Spawns process with path to XML file
PythonServer->>PythonServer: Parses XML and starts WebSocket server on port 8765
Note over ProjectWatcher: Switches to "Narrow Mode".
ProjectWatcher->>Hammerspoon: Replaces broad watchers with a single watcher for the active project's directory.
Step-by-Step Breakdown¶
-
Hammerspoon Initialization: When Hammerspoon is launched or its configuration is reloaded, the main
init.luascript sets up all the components. Specifically, it callsapp_watcher.setup(), which starts a global application watcher. -
Ableton Launch: The user starts Ableton Live. The
app_watcherdetects the "Live.app" launch event. -
Project Watcher Activation: Upon detecting the launch,
app_watcherwaits for a brief period (2 seconds) to ensure Ableton is loading, and then callsprojectWatcher.start(). -
"Broad Mode" Watch:
projectWatcherbegins in "broad mode". It reads the list of directories fromconfig.projectWatchDirsand creates filesystem watchers for each of their immediate subdirectories. This is an optimization to avoid watching a user's entire home directory, for instance. -
Project Save Event: The user works in Ableton and saves their project (Cmd+S). This action modifies the
.alsfile on the disk. -
Detection and Debouncing: The filesystem watcher detects the change to the
.alsfile and triggers theonFileChangedcallback inproject_watcher.lua. This function confirms the file change is valid and debounces the event to prevent multiple triggers from a single save action. -
Server Launch Trigger: The project watcher then calls
websocket_manager.start(path), passing the full path to the.alsfile that was just saved. -
Project State Export: The
websocket_manager's first action is to ask Ableton Live to export its current state to an XML file. This is handled by thelive_state.exportXML()function, which places the resulting.xmlfile inside a.vimabldirectory within the Ableton project folder. -
Python Process Spawn: With the XML file created,
websocket_managerconstructs the full shell command to launch the Python server (e.g.,uv run python -m src.main /path/to/project.xml --mode=websocket). It then executes this command usinghs.task. -
WebSocket Server Ready: The Python script
src/main.pystarts, parses the XML file into an Abstract Syntax Tree (AST), and launches the WebSocket server, which is now ready to accept connections from the web frontend. -
"Narrow Mode" Switch: After successfully triggering the server start, the
project_watcherswitches to "narrow mode". It stops all the broad directory watchers and creates a single, more efficient watcher that only monitors the directory of the currently active project. This conserves system resources.