From 18e1877039e9a03faaa58b5fd0c8ec298b8e491a Mon Sep 17 00:00:00 2001 From: Amin Zibayi <43344360+AminZibayi@users.noreply.github.com> Date: Fri, 27 Jun 2025 23:45:02 +0000 Subject: [PATCH] feat: add regex-based title cleaning rules configuration and implementation --- README.md | 28 +++++++++++++++++++++++++ aw_watcher_window/config.py | 28 +++++++++++++++++++++++-- aw_watcher_window/main.py | 23 ++++++++++++++++++++- example-config.toml | 41 +++++++++++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 example-config.toml diff --git a/README.md b/README.md index 41de1a35..9f9bfa1e 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,34 @@ that it might not get the exact version of the dependencies due to not reading t In order for this watcher to be available in the UI, you'll need to have a Away From Computer (afk) watcher running alongside it. +## Title Cleaning Rules + +You can configure regex-based title cleaning rules to modify window titles before they are sent to the server. This is useful for removing application-specific suffixes, file paths, or other unwanted information from window titles. + +### Configuration + +Add title cleaning rules to your `~/.config/activitywatch/aw-watcher-window.toml` file: + +```toml +# Title cleaning rules - apply regex replacements to window titles for specific apps +[[title_cleaning_rules.rules]] +app = "firefox" +pattern = "( - Mozilla Firefox)$" +replacement = "" + +[[title_cleaning_rules.rules]] +app = "code" +pattern = " - Visual Studio Code$" +replacement = "" +``` + +Each rule consists of: +- `app`: The application name (case-insensitive match) +- `pattern`: Regular expression pattern to match in the window title +- `replacement`: String to replace matches with (can be empty to remove) + +See `example-config.toml` for more examples. + ### Note to macOS users To log current window title the terminal needs access to macOS accessibility API. diff --git a/aw_watcher_window/config.py b/aw_watcher_window/config.py index 81eabc28..7ff9e97d 100644 --- a/aw_watcher_window/config.py +++ b/aw_watcher_window/config.py @@ -8,15 +8,39 @@ exclude_titles = [] poll_time = 1.0 strategy_macos = "swift" + +# Title cleaning rules - apply regex replacements to window titles for specific apps +# [[title_cleaning_rules.rules]] +# app = "firefox" +# pattern = "( - Mozilla Firefox)$" +# replacement = "" """.strip() def load_config(): - return load_config_toml("aw-watcher-window", default_config)["aw-watcher-window"] + return load_config_toml("aw-watcher-window", default_config) + + +def get_title_cleaning_rules(): + """Load title cleaning rules from configuration""" + config = load_config_toml("aw-watcher-window", default_config) + rules = [] + + # Check if title_cleaning_rules section exists + if "title_cleaning_rules" in config and "rules" in config["title_cleaning_rules"]: + for rule in config["title_cleaning_rules"]["rules"]: + if "app" in rule and "pattern" in rule: + rules.append({ + "app": rule["app"], + "pattern": rule["pattern"], + "replacement": rule.get("replacement", "") + }) + + return rules def parse_args(): - config = load_config() + config = load_config()["aw-watcher-window"] default_poll_time = config["poll_time"] default_exclude_title = config["exclude_title"] diff --git a/aw_watcher_window/main.py b/aw_watcher_window/main.py index b00f6780..6f7d2ca3 100644 --- a/aw_watcher_window/main.py +++ b/aw_watcher_window/main.py @@ -11,7 +11,7 @@ from aw_core.log import setup_logging from aw_core.models import Event -from .config import parse_args +from .config import parse_args, get_title_cleaning_rules from .exceptions import FatalError from .lib import get_current_window from .macos_permissions import background_ensure_permissions @@ -40,6 +40,23 @@ def try_compile_title_regex(title): exit(1) +def clean_window_title(app, title): + """Apply regex replacement rules to window title based on app name""" + try: + rules = get_title_cleaning_rules() + for rule in rules: + if rule["app"].lower() == app.lower(): + try: + title = re.sub(rule["pattern"], rule["replacement"], title) + logger.debug(f"Applied title cleaning rule for {app}: {rule['pattern']} -> {rule['replacement']}") + except re.error: + logger.error(f"Invalid regex pattern in title cleaning rule for {app}: {rule['pattern']}") + return title + except Exception as e: + logger.error(f"Error applying title cleaning rules: {e}") + return title + + def main(): args = parse_args() @@ -144,6 +161,10 @@ def heartbeat_loop( if current_window is None: logger.debug("Unable to fetch window, trying again on next poll") else: + # Apply title cleaning rules before other processing + if "app" in current_window and "title" in current_window: + current_window["title"] = clean_window_title(current_window["app"], current_window["title"]) + for pattern in exclude_titles: if pattern.search(current_window["title"]): current_window["title"] = "excluded" diff --git a/example-config.toml b/example-config.toml new file mode 100644 index 00000000..49632e6f --- /dev/null +++ b/example-config.toml @@ -0,0 +1,41 @@ +# Example configuration file for aw-watcher-window with title cleaning rules +# Save this as ~/.config/activitywatch/aw-watcher-window.toml + +[aw-watcher-window] +exclude_title = false +exclude_titles = [] +poll_time = 1.0 +strategy_macos = "swift" + +# Title cleaning rules - apply regex replacements to window titles for specific apps +[[title_cleaning_rules.rules]] +app = "firefox" +pattern = "( - Mozilla Firefox)$" +replacement = "" + +[[title_cleaning_rules.rules]] +app = "google-chrome" +pattern = "( - Google Chrome)$" +replacement = "" + +[[title_cleaning_rules.rules]] +app = "code" +pattern = " - Visual Studio Code$" +replacement = "" + +[[title_cleaning_rules.rules]] +app = "sublime_text" +pattern = " \\(.*\\) - Sublime Text$" +replacement = "" + +# Example: Remove file paths from terminal titles +[[title_cleaning_rules.rules]] +app = "gnome-terminal" +pattern = "^.*: (.*)$" +replacement = "\\1" + +# Example: Clean browser tab information +[[title_cleaning_rules.rules]] +app = "firefox" +pattern = "^(.*) — Mozilla Firefox$" +replacement = "\\1"