Created
December 8, 2025 00:18
-
-
Save quells/25c178eb9d3934a8b5d17e8b6937b8fa to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import json | |
| import os | |
| import sys | |
| from typing import Dict, List, Tuple | |
| def main(): | |
| query = "" | |
| if len(sys.argv) > 1: | |
| query = sys.argv[1].lstrip(".") | |
| apps = list_files_with_fuzzy_match(["/Applications", "/Users/kwells/Applications"], query) | |
| print(format_items(apps)) | |
| def list_files_with_fuzzy_match(directories: List[str], query: str) -> List[Tuple[str, str]]: | |
| results = [] | |
| for directory in directories: | |
| if not os.path.exists(directory): | |
| continue | |
| try: | |
| for entry in os.listdir(directory): | |
| full_path = os.path.join(directory, entry) | |
| if not full_path.endswith(".app"): | |
| continue | |
| score = fuzzy_match_score(entry, query) | |
| if score > 0: # Only include matches | |
| name = entry.split(".")[0] | |
| results.append((name, full_path, score)) | |
| except PermissionError: | |
| return [] | |
| # Sort by score (descending) and then alphabetically | |
| results.sort(key=lambda x: (-x[2], x[0].lower())) | |
| # Return without the score | |
| return [(name, path) for name, path, score in results] | |
| def fuzzy_match_score(text: str, query: str) -> int: | |
| """ | |
| Calculate fuzzy match score between text and query. | |
| Returns: | |
| Score > 0 if match found, 0 if no match. | |
| Higher scores indicate better matches. | |
| """ | |
| if text.startswith("."): | |
| return 0 | |
| if not query: | |
| return 100 # Empty query matches everything | |
| text_lower = text.lower() | |
| query_lower = query.lower() | |
| # Exact match gets highest score | |
| if query_lower == text_lower: | |
| return 1000 | |
| # Starts with query gets high score | |
| if text_lower.startswith(query_lower): | |
| return 500 | |
| # Check if query is substring | |
| if query_lower in text_lower: | |
| return 300 | |
| # Fuzzy match: all characters in order | |
| text_idx = 0 | |
| query_idx = 0 | |
| consecutive_matches = 0 | |
| max_consecutive = 0 | |
| while text_idx < len(text_lower) and query_idx < len(query_lower): | |
| if text_lower[text_idx] == query_lower[query_idx]: | |
| query_idx += 1 | |
| consecutive_matches += 1 | |
| max_consecutive = max(max_consecutive, consecutive_matches) | |
| else: | |
| consecutive_matches = 0 | |
| text_idx += 1 | |
| # If all query characters found in order | |
| if query_idx == len(query_lower): | |
| # Score based on how close together the matches were | |
| return 100 + max_consecutive * 10 | |
| return 0 # No match | |
| def format_items(rows: List[Tuple[str, str]]) -> str: | |
| items = [format_item(row) for row in rows] | |
| return json.dumps({"items": items}) | |
| def format_item(row: Tuple[str, str]) -> Dict[str, str]: | |
| (name, loc) = row | |
| return { | |
| "title": name, | |
| "subtitle": loc, | |
| "arg": loc, | |
| "icon": { | |
| "path": f"{loc}/Contents/Resources/AppIcon.icns" | |
| } | |
| } | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment