macOS menubar AI Prompt Assistant
Have you ever wanted to create your own macOS menubar application? In this blog post, we'll walk through the development of the [AI Prompt Assistant](https://github.com/Raspiska-Ltd/ai-prompt-assistant) - a practical utility that lives in your menubar and helps improve your interactions with AI services.
AI Prompt Assistant
Introduction
Have you ever wanted to create your own macOS menubar application? In this blog post, we'll walk through the development of the AI Prompt Assistant - a practical utility that lives in your menubar and helps improve your interactions with AI services.
This project is perfect for intermediate Python developers looking to expand their skills into desktop application development. We'll cover from initial planning to final packaging, with plenty of code examples and explanations along the way.
The AI Prompt Assistant is a handy tool that sits in your macOS menubar, allowing you to quickly rephrase your prompts for better results when using services like ChatGPT. It's a real-world example of how Python can be used to create practical desktop utilities that enhance your daily workflow.
Note 1: This project is not a production-ready application. It is a proof of concept and is not intended for use in a production environment. Note 2: I know icon is not good, I'm just that lazy -_-
Technologies Used
Before diving into the development process, let's explore the key technologies that make this project possible:
-
Python - Our core programming language, chosen for its readability and extensive library ecosystem. Python 3.8+ provides all the features we need for this project.
-
PyQt6 - A comprehensive set of Python bindings for Qt, which we use to create beautiful, native-looking dialog interfaces. PyQt gives us access to Qt's powerful widgets while allowing us to stay in the Python ecosystem.
-
rumps (Ridiculously Uncomplicated Mac OS X Python Statusbar apps) - A wonderful library that makes creating macOS menubar applications surprisingly simple. It handles all the complexities of the statusbar API for us.
-
keyring - A cross-platform library for secure password storage. We use this to safely store the user's API key in the system's secure storage (Keychain on macOS).
-
pynput - For global keyboard shortcut detection, allowing our app to be summoned from anywhere with a customizable key combination.
-
py2app - A setuptools command that allows you to build standalone macOS applications from Python scripts. Essential for distributing our application to users who don't have Python installed.
The Development Process
Let's walk through how we built this application step by step. This approach can be applied to many types of desktop applications, not just our specific use case.
Day 1: Planning and Project Structure
Every successful software project starts with good planning. We began by creating a comprehensive development plan in plan.md
. This might seem like an extra step, but having a clear roadmap saves tremendous time later in the development process.
Our plan document outlined:
- Project objectives and features - What exactly are we building and why?
- Development phases and timeline - Breaking the work into manageable chunks
- Technical specifications - What technologies we'll use and how they'll fit together
- API requirements - What external services we'll need to interact with
Example Prompt for Day 1
I need to create a macOS menubar application that helps users optimize their AI prompts. The app should have these features: - Live in the menubar with a custom icon - Open a dialog with a keyboard shortcut - Allow multi-line text input - Connect to OpenAI API to rephrase prompts - Store prompt history - Support multiple languages Please help me create: 1. A detailed project plan (plan.md) with development phases 2. A project structure with all necessary files 3. A README.md with installation and usage instructions
The planning phase was crucial as it provided a roadmap for the entire project. We created a detailed structure that broke down the work into manageable phases:
- Phase 1: Basic Structure
- Phase 2: Core Functionality
- Phase 3: Advanced Features
- Phase 4: Testing and Packaging
Simultaneously, we created a README.md
file to document the application's features, installation process, and usage instructions. This helped maintain focus on the end-user experience throughout development.
Here's a snippet from our plan.md file showing how we organized our development phases:
## Development Phases ### Phase 1: Basic Structure (Day 1) - Set up project structure - Create menubar app with icon - Create keyboard shortcut registration - Build input dialog ### Phase 2: Core Functionality (Day 2) - Implement prompt rephrasing API client - Connect input dialog to API client - Create history manager for storing past inputs - Implement settings UI
This structured approach helped us stay organized and track our progress throughout the development process.
Day 2: Core Implementation
With our plan in place, we started implementing the core functionality:
- Menubar Integration: We created a menubar app using the rumps library
- Input Dialog: We developed a clean, multi-line input dialog using PyQt6
- Settings Management: We implemented a JSON-based settings manager
- History Tracking: We created an SQLite-based history manager
Let's look at some key code snippets to understand how these components work together.
First, here's how we set up the menubar application using rumps:
class MenuBarApp(rumps.App): def __init__(self): super().__init__("AI Prompt Assistant", icon="resources/icons/menubar_icon.png") self.menu = ["Open Input", "Settings", "Check for Updates", "Quit"] self.keyboard_handler = KeyboardShortcutHandler(self.open_input) self.settings_manager = SettingsManager() self.history_manager = HistoryManager() self.input_dialog = InputDialog(self.process_input) @rumps.clicked("Open Input") def open_input(self, _): self.input_dialog.run()
For the input dialog, we used PyQt6 to create a clean, centered dialog with a multi-line text input:
class InputDialog(QDialog): def __init__(self, callback): super().__init__() self.callback = callback self.is_initialized = False self.setWindowTitle("AI Prompt Assistant") self.setFixedSize(500, 300) self.center_on_screen() self.setup_ui()
This approach of separating concerns into different classes makes the code more maintainable and easier to understand.
Example Prompt for Day 2
Now I need to implement the core functionality for the AI Prompt Assistant. Please help me with: 1. Creating the MenuBarApp class using rumps with: - A menu with Open Input, Settings, Check for Updates, and Quit options - A handler for the keyboard shortcut - Integration with settings and history managers 2. Implementing the InputDialog class using PyQt6 with: - A centered multi-line text input field - Submit and Cancel buttons - Character counter - Proper focus handling 3. Creating a SettingsManager class that: - Stores settings in a JSON file - Has methods to get and set settings - Handles default settings Please provide the code for these components and explain how they work together.
Day 3: Advanced Features
On the third day, we enhanced the application with more sophisticated features:
- Multilingual Support: We added localization for five languages (English, Spanish, German, Swedish, Turkish)
- API Integration: We implemented the OpenAI API client for prompt rephrasing
- Keyboard Shortcuts: We added customizable global keyboard shortcuts
- Character Counter: We implemented a dynamic character and word counter with visual feedback
- Settings Dialog: We created a comprehensive settings interface
The localization system was particularly interesting to implement. We stored all UI strings in JSON files for each language:
{ "menu": { "open_input": "Open Input", "settings": "Settings", "check_updates": "Check for Updates", "quit": "Quit", "about": "About" }, "input_dialog": { "title": "AI Prompt Assistant", "placeholder": "Enter your prompt here...", "submit": "Submit" } }
Then we created a language selector in the settings dialog that allows users to switch languages on the fly:
def create_language_tab(self): tab = QWidget() layout = QVBoxLayout() language_group = QGroupBox("Language") language_layout = QVBoxLayout() self.language_combo = QComboBox() self.language_combo.addItem("English", "en") self.language_combo.addItem("Español", "es") self.language_combo.addItem("Deutsch", "de") self.language_combo.addItem("Svenska", "sv") self.language_combo.addItem("Türkçe", "tr") # Set current language current_lang = self.settings_manager.get_setting("language") index = self.language_combo.findData(current_lang) if index >= 0: self.language_combo.setCurrentIndex(index)
This approach makes it easy to add new languages in the future without changing the core application code.
Example Prompt for Day 3
For day 3 of development, I need to implement advanced features for the AI Prompt Assistant: 1. Add localization support with: - JSON files for English, Spanish, German, Swedish, and Turkish - A language selector in the settings dialog - Dynamic text updating when language changes 2. Implement the OpenAI API client: - Create methods to call the API for prompt rephrasing - Add error handling for API calls - Implement API key management 3. Enhance the input dialog with: - A character and word counter that changes color based on length - Support for keyboard shortcuts (Enter/Cmd+Enter) - Immediate focus on the text field when opened Please provide the necessary code and explain how these features work.
Day 4: Polishing and Packaging
The final day focused on polishing the application and preparing it for distribution:
- API Key Management: We added secure storage using the keyring library
- About Dialog: We created an About dialog with app information
- Error Handling: We implemented comprehensive error handling
- Packaging: We set up py2app configuration for creating a standalone app
- Documentation: We finalized installation guides and release documentation
Secure API key storage was an important feature. We used the keyring library to store the API key in the macOS Keychain:
import keyring def save_api_key(service_name, api_key): keyring.set_password("ai_prompt_assistant", service_name, api_key) def get_api_key(service_name): return keyring.get_password("ai_prompt_assistant", service_name)
For packaging, we created a setup.py file for py2app:
from setuptools import setup APP = ['main.py'] DATA_FILES = [ ('resources/icons', ['resources/icons/menubar_icon.png']), ('resources/localization', ['resources/localization/en.json', 'resources/localization/es.json', 'resources/localization/de.json', 'resources/localization/sv.json', 'resources/localization/tr.json']) ] OPTIONS = { 'argv_emulation': True, 'plist': { 'LSUIElement': True, 'CFBundleName': 'AI Prompt Assistant', 'CFBundleDisplayName': 'AI Prompt Assistant', 'CFBundleIdentifier': 'com.raspiska.aipromptassistant', 'CFBundleVersion': '1.0.0', 'CFBundleShortVersionString': '1.0.0' }, 'packages': ['rumps', 'PyQt6', 'requests', 'keyring', 'pynput'], } setup( app=APP, data_files=DATA_FILES, options={'py2app': OPTIONS}, setup_requires=['py2app'], install_requires=['rumps', 'PyQt6', 'requests', 'keyring', 'pynput'], )
We also created shell scripts to automate the build and packaging process.
Example Prompt for Day 4
For the final day of development, I need to polish the application and prepare it for distribution: 1. Implement secure API key storage using the keyring library: - Create functions to save and retrieve API keys from the macOS Keychain - Add API key validation and testing 2. Create an About dialog with: - Application name and version - Copyright information - Website link - Localized strings for all supported languages 3. Set up packaging with py2app: - Create a setup.py file with all necessary configuration - Include all resource files (icons, localization) - Set proper macOS bundle information 4. Create build scripts: - A build_app.sh script to create the standalone app - A create_dmg.sh script to package the app for distribution Please provide the code and explain the packaging process.
Lessons Learned
Developing this macOS menubar application taught us several valuable lessons that can be applied to any desktop application project:
-
Structured Planning: Having a clear plan was essential. Breaking the project into phases with specific tasks made the development process much smoother.
-
Iterative Development: We updated our
plan.md
at the end of each development phase, marking completed tasks and adjusting priorities. This helped us stay on track and adapt to challenges as they arose. -
Separation of Concerns: Dividing the application into distinct components (menubar, dialogs, settings, history, API client) made the code more maintainable and easier to debug.
-
User Experience Focus: Constantly thinking about the end-user experience guided our design decisions, from keyboard shortcuts to dialog layouts.
-
Documentation First: Creating documentation before implementation helped clarify requirements and expectations, reducing rework later in the process.
Conclusion
The AI Prompt Assistant project demonstrates how Python developers can create useful macOS utilities with relatively little code. By leveraging libraries like rumps, PyQt6, and keyring, we were able to build a fully functional, localized macOS application in just four days.
This project serves as a practical example of:
- Creating menubar applications with rumps
- Building native-looking dialogs with PyQt6
- Implementing secure credential storage with keyring
- Managing application settings and user preferences
- Packaging Python applications for distribution
We hope this walkthrough inspires you to create your own macOS utilities. The complete source code is available on GitHub, and we encourage you to explore it, learn from it, and perhaps even contribute to it!
This project was developed as an educational resource for Python developers interested in macOS application development. The application is functional and demonstrates practical techniques for creating desktop utilities.
Technologies Used
Backend
Other
Related Projects
Jenkins Shared Library for Build Notifications
A lightweight, customizable solution for sending build notifications directly to your team's Slack channels.
Terminal Multiplexers: tmux and GNU Screen
A comprehensive guide to terminal multiplexing with tmux and GNU Screen for efficient terminal workflows.
Have a project in mind?
Let's work together to bring your ideas to life. Our team of experts is ready to help you build something amazing.