Draft a message anywhere, press a hotkey, and your chosen LLM rewrites it in place.
A lightweight, private, cross-platform menu-bar / tray app for polishing text in any application.
Quick start · Features · How it works · Configuration · FAQ · Contributing
Every app has a text box, but none of them have your favorite LLM and your favorite prompt one keystroke away. AI Text Refiner lives quietly in your tray and bridges that gap: select (or write) some text in Slack, Gmail, VS Code, a browser — anywhere — hit a global hotkey, and the refined version is pasted straight back, no copy-paste-juggling required.
- 🔒 Private — your API key is encrypted at rest with your OS keychain, and text goes only to the provider you choose. No middleman servers.
- ⚡ Universal — works in any app via clipboard + synthetic keystrokes; no per-app plugins.
- 🎛️ Yours — your provider, your model, your prompt, your hotkeys.
# 1. Clone & install
git clone https://github.com/Flowdesktech/ai-text-refiner.git
cd ai-text-refiner
npm install
# 2. Launch the tray app + settings window
npm run devThen in the settings window:
- Pick a provider (OpenAI / Anthropic / Gemini), paste your API key, choose a model.
- Tweak the refine prompt — keep the
{{text}}placeholder where the draft is inserted. - Record your hotkeys (defaults:
Ctrl/⌘ + Alt + Rfor the selection,Ctrl/⌘ + Alt + Afor the whole field). - Hit Run test to confirm your key works — no hotkey needed.
Now select a draft in any app and press your hotkey. ✨
| 🌐 Multi-provider | OpenAI, Anthropic (Claude), and Google Gemini — switch any time |
| 🤖 Per-provider models | Curated model dropdowns (GPT-5.5, Claude Opus 4.8, Gemini 3.5 Pro, …) plus custom entry |
| ✍️ Custom prompt | Editable instruction template with a {{text}} placeholder |
| ⌨️ Two global hotkeys | One for the current selection, one to select-all the focused field |
| 🎯 In-place replacement | Works in any app — editors, Slack, browsers, mail clients |
| 🌀 Inline progress | An animated "Refining…" spinner right in the text field while you wait |
| 🔐 Encrypted keys | API keys sealed with Electron safeStorage (OS keychain) |
| 📋 Clipboard-safe | Saves and restores your original clipboard around each run |
| 🚀 Launch on startup | Optional auto-start, runs hidden in the tray |
| 🖥️ Cross-platform | Windows, macOS, and Linux (X11) |
External apps don't expose their text fields to other programs, so AI Text Refiner uses a clipboard + synthetic-keystroke bridge that works everywhere without per-app integration.
flowchart LR
A["Draft text in any app"] --> B["Press global hotkey"]
B --> C["Save clipboard, send Ctrl/Cmd+C<br/>(optionally Ctrl/Cmd+A first)"]
C --> D["Read captured text"]
D --> E["Send text + your prompt<br/>to the chosen provider"]
E --> F["Write result to clipboard,<br/>send Ctrl/Cmd+V"]
F --> G["Restore original clipboard"]
To avoid your still-held hotkey modifiers corrupting the synthetic copy/paste, the app waits a beat and explicitly releases modifier keys before sending keystrokes.
All settings live in the in-app window and save automatically.
| Setting | Description |
|---|---|
| Provider + API key + model | Key stored encrypted via OS keychain (safeStorage) |
| Refine prompt | Instruction template containing the {{text}} placeholder |
| Refine-selection hotkey | Copies the current selection, then refines it |
| Refine-entire-field hotkey | Sends Ctrl/⌘+A first to grab the whole field, then refines |
| Inline progress | Animated "Refining…" placeholder shown in the field while waiting |
| Restore clipboard | Put your original clipboard contents back after pasting |
| Launch on startup | Start automatically when you log in (Windows/macOS) |
| Provider | Endpoint |
|---|---|
| OpenAI | POST https://api.openai.com/v1/chat/completions |
| Anthropic | POST https://api.anthropic.com/v1/messages (anthropic-version header) |
| Gemini | POST https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent |
Each receives prompt.replace("{{text}}", draft) and returns the refined string.
| OS | Status | Notes |
|---|---|---|
| Windows | ✅ Works out of the box | — |
| macOS | ✅ Needs permission | Grant Accessibility for synthetic keystrokes — the app detects this and links you to System Settings |
| Linux (X11) | ✅ Works | — |
| Linux (Wayland) | Wayland blocks synthetic input; the app detects it and shows a clear warning (use the Test panel / manual paste) |
npm run pack:win # NSIS installer (.exe)
npm run pack:mac # dmg (configure signing/notarization for distribution)
npm run pack:linux # AppImageBuilt via electron-builder; see electron-builder.yml.
Pushing a version tag builds installers for all three platforms and publishes a GitHub
Release with the artifacts attached (see .github/workflows/release.yml):
npm version patch # bumps package.json + creates a vX.Y.Z tag
git push --follow-tags # triggers the Build & Release workflowOptional code signing — set the repository variable ENABLE_CODE_SIGNING=true and provide
the relevant secrets to produce signed/notarized builds:
| Platform | Secrets |
|---|---|
| Windows | WINDOWS_CERTIFICATE, WINDOWS_CERTIFICATE_PASSWORD |
| macOS | APPLE_CERTIFICATE, APPLE_CERTIFICATE_PASSWORD, APPLE_ID, APPLE_APP_SPECIFIC_PASSWORD, APPLE_TEAM_ID |
src/
├─ main/ Electron main process
│ ├─ index.ts app lifecycle, tray, global hotkeys, refine orchestration
│ ├─ automation.ts clipboard save/restore + nut-js keystroke sequences
│ ├─ providers.ts unified refine() → OpenAI / Anthropic / Gemini
│ └─ settings.ts electron-store schema + safeStorage key encryption
├─ preload/ typed IPC bridge (window.refiner)
├─ renderer/ React settings UI + status overlay
└─ shared/ shared types & defaults
Tech stack: Electron · electron-vite · React · TypeScript · @nut-tree-fork/nut-js
(keystrokes) · electron-store (settings) · safeStorage (encryption) · electron-builder.
Is my text or API key sent anywhere besides the LLM provider?
No. Your text is sent only to the provider you configure, using your own API key. The key is encrypted at rest with your OS keychain and never leaves your machine except in requests to that provider.
Nothing gets pasted when I press the hotkey.
Make sure (1) the hotkey shows "Active" in settings (no conflict), (2) you've saved an API key, and (3) on macOS you've granted Accessibility permission. For "Refine selected text" you must have text selected; otherwise use the "Refine entire field" hotkey.
The inline spinner behaves oddly in my editor.
Some rich/code editors handle synthetic Shift+Left selection differently. Turn off
"Show animated Refining… progress in the text field" in settings to fall back to the corner overlay.
Which models are supported?
Any chat model from the three providers. The dropdowns list current flagships, and a Custom… option lets you type any model id.
- Multiple saved prompt presets (tone: formal / casual / concise)
- Streaming responses into the field
- Local model support (Ollama / LM Studio)
- Per-app prompt overrides
- Signed & notarized release binaries
Have an idea? Open an issue. 💡
Contributions are welcome! Please read CONTRIBUTING.md and our Code of Conduct. Found a security issue? See SECURITY.md.
npm run dev # run in development
npm run typecheck # type-check main + renderer
npm run format # format with PrettierIf this saves you keystrokes, please star the repo — it genuinely helps others find it.
MIT © Your Name