TreeMap
A free, open-source disk-space visualizer. Squarified treemap, duplicate hunting, disk-full forecasting, and cleanup that only ever moves files to the Trash.
Clipboard history and voice dictation for Mac, Windows and Linux. Free and local by default, synced with your Clipboard+ account when you want.
ProductivityPython · ShellGitHub @tommyqhoangOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Clipboard+ for desktop: free clipboard history and voice dictation for macOS, Windows and Linux. Local and private by default; optional sync with your Clipboard+ account.
Taken from the repository README
Loading the full README.
Read the full README ↗Formerly Clipboard+ and Dictation, and before that Whisper Dictation & Clipboard+. Download page: https://clipboardplus.apercallc.com/desktop.html · Source: https://github.com/tommyqhoang/ClipboardPlus-and-Dictation
Free desktop app for Linux, macOS, and Windows with two features you can use separately or together:
The same icon, menu and workflow on every platform: the app lives in the macOS menu bar, the Windows system tray, or the Linux top bar and starts at login. Press Win+Shift+D (Super+Shift+D on Linux, ⇧⌘D on a Mac) in any app, talk, press it again, and notifications tell you it is recording, transcribing, and then ready to paste.
Linux remains the original platform. macOS and Windows support is newer. Installers exist for all three (see Quick install) but are not code-signed yet, so each asks for one one-time OS security confirmation on first launch. See desktop setup for dependencies, permissions, and validation limits.
| Platform | Installed launcher | Microphone | Clipboard (dictation) | Clipboard history capture |
|---|---|---|---|---|
| Linux/Wayland | Top bar icon + GNOME shortcut | ALSA/PipeWire via arecord | wl-copy | wl-paste --watch where the desktop allows it, otherwise X11 selection events (also under XWayland) |
| Linux/X11 | Top bar icon + GNOME shortcut | ALSA/PipeWire via arecord | xclip | X11 selection events |
| macOS | Menu bar (/Applications/Clipboard+.app) | FFmpeg AVFoundation | pbcopy | Pasteboard change count, polled twice a second |
| Windows x64 | System tray + Start Menu | FFmpeg DirectShow | Windows clipboard | Clipboard sequence number, polled four times a second |
Local Whisper is the default. You can select your own compatible local model, use a local transcription server, or explicitly enable an external transcription API. External services may charge; no subscription is required for local use.
The setup flow and Settings → Privacy include a single switch, Share anonymous
crash reports and usage statistics. It is off by default. Nothing is sent unless
you explicitly turn it on, and you can turn it off at any time.
DO_NOT_TRACK=1 or DICTATION_TELEMETRY=0 disables reporting for a launch.
When enabled, usage reports contain a random per-installation identifier, a fixed event name, approved feature choices, and bounded counts or durations. Crash reports contain the exception type and frames from Clipboard+ only. Clipboard history, transcripts, audio, file names and paths, email addresses, API keys, and arbitrary error text are never sent. The app does not wait for reporting and quietly drops it when offline. Test runs and CI always disable reporting.
Desktop usage reports reach the Clipboard+ API, which validates the fixed schema and forwards it to Google Analytics only when its server-side Measurement Protocol secret is configured. That secret is never included in the app or its installer.
Download the installer for your OS from the latest release:
Clipboard+-macOS-arm64.dmg for Apple silicon or
Clipboard+-macOS-x86_64.dmg for Intel. Open the matching DMG, quit a running
copy and double-click Install Clipboard+ — it copies the app into Applications,
clears macOS's one-time block for unsigned apps, and launches it. (The app
isn't notarized yet; without this step macOS 15+ may claim it "is damaged
and can't be opened." If you instead drag the app yourself, run
xattr -dr com.apple.quarantine /Applications/Clipboard+.app once in
Terminal before opening it.) Signed and notarized builds skip this step
once the maintainer's Apple credentials are configured for a release.Clipboard+-Setup.exe — run it; if SmartScreen shows a
notice, choose "More info" then "Run anyway" (unsigned unless the release
was built with signing credentials). x86_64 only; Windows on ARM runs it
through emulation, untested.
Installs to your user folder, no admin needed.Clipboard+-x86_64.AppImage — chmod +x it (or check "Allow
executing file as program" in your file manager), then double-click. No
package manager, no sudo. Needs FUSE to mount itself (present on most
desktops already); if it fails to start with a FUSE error, run it with
--appimage-extract-and-run instead. x86_64 only; there is no ARM64 Linux
build, use the source install there.
One system requirement: dictation records with arecord from your
distribution's alsa-utils package (not bundled; already present on most
desktops). If it is missing, the AppImage prints and shows a notice at
startup naming the package (for example sudo apt install alsa-utils);
clipboard history works without it.No Python and no terminal needed. Everything else ships inside the download
(Python runtime and whisper.cpp, plus ffmpeg on macOS and Windows); the only outside pieces are the
Linux arecord above and the operating system itself. Licenses of the
bundled components are in THIRD-PARTY-NOTICES.md. Prefer a one-line install script instead
(or want to build from a Git checkout)? See
Build from source.
Once you open Clipboard+ for the first time, the first-run screens help the user:
/audio/transcriptions endpoint). Audio sent
to your own service may incur charges.The walkthrough does not begin recording. The microphone starts only when you press the shortcut (or Test next to the microphone, which listens for three seconds and keeps nothing).
"overlay": false in config.json for plain notifications instead.)transcription-key, readable only by your user account. An environment
variable named by api_key_env still takes precedence.Platform details:
~/Library/LaunchAgents/com.apercallc.clipboardplusdesktop.menubar.plist.RegisterHotKey.
Open at login is the ClipboardPlus value under
HKCU\Software\Microsoft\Windows\CurrentVersion\Run.~/.local/bin/dictate-toggle --via-shortcut; changing it
from the menu updates GNOME. On other desktops, bind that command yourself.
The top bar icon needs AppIndicator support (built into Ubuntu; an extension
on stock GNOME). Open at login is ~/.config/autostart/clipboardplus.desktop.The window has Clipboard, Dictation and Settings tabs (only for the features you turned on). The Clipboard tab lists what you copied, newest first, with a preview or thumbnail, how long ago, and where it came from (Desktop, Dictation or Cloud). Search it, filter by All / Favorites / Images / Text, star an item, click a row to copy it back, or delete it. Clear history deletes text, links and copied images; with a Clipboard+ account connected it clears everywhere by default (or this device only), and it keeps favorites unless you say otherwise. Cleared items are not brought back by the next sync. Dictation transcripts join the history too.
A small background service, started and restarted by the tray or menu bar app, does the capturing; the window and dictation only read the shared database.
clipboard/: a SQLite database
(clips.db) and image files, readable only by you. It is not encrypted, so
anything you copy that is not marked secret is stored in plain form.x-kde-passwordManagerHint target on Linux, org.nspasteboard.ConcealedType /
TransientType / AutoGeneratedType on macOS, and
ExcludeClipboardContentFromMonitorProcessing / CanIncludeInClipboardHistory = 0
on Windows. A password manager that does not set these cannot be recognised.sk-…, ghp_…, AKIA…, -----BEGIN PRIVATE KEY-----) is skipped the
same way, so it is never stored or synced.Connect an account in Settings (or during setup) and the history is mirrored with your Clipboard+ account, so the website and the browser extension show the same items without duplicates.
The same keys everywhere, using each system's own key (cmd in the code):
| Platform | Dictation shortcut | Clipboard history shortcut |
|---|---|---|
| Linux | Super+Shift+D (desktop keybinding) | Super+Shift+F (desktop keybinding) |
| Windows | Win+Shift+D | Win+Shift+F |
| macOS | ⇧⌘D | ⇧⌘F |
Ctrl+Shift+D was the default before. It did not reach the app everywhere (browsers, terminals and other apps take it first), so it is now a one-click preset instead, and anyone who already chose a shortcut keeps theirs. Super/Win/⌘ plus Shift plus a letter is not a system shortcut on macOS, Windows, GNOME or KDE by default (an app can still use it: browsers and editors use ⇧⌘D and ⇧⌘F on a Mac, and the shortcut here takes precedence while it is on). The app refuses combinations the system owns (such as Win+D, ⌘Space or ⌥⌘D, which shows the Dock) and, on GNOME, reads the desktop's own shortcut list to warn about a clash before you pick.
Test it. Settings → Keyboard shortcuts → Test it (also after Save in the
recorder, on Home, and in the first-run tutorial) asks you to press the shortcut and
says whether it was heard. If not, it says the likely cause and offers the next
shortcut that can work, one click each, ending with the exact steps to set it by hand.
On GNOME, KDE, Sway and Hyprland the desktop runs
dictate-toggle --via-shortcut (or app.py --clipboard --via-shortcut); the marker makes
the command record that it was pressed. A binding without the marker still works, it just
cannot report.
Free dictation. Your model. Your words.
Not automatically faster or more accurate: results depend on model, hardware, microphone, accent, and vocabulary. Larger models can help at the cost of speed and memory. Built-in dictation has tighter system integration and may be better for immediate text insertion; this app currently copies text for you to paste. Apple offers on-device dictation in supported configurations, and Windows Voice Access can work offline. Offline operation is therefore not unique to this app.
Does it shorten or rephrase? The engine has an experimental, opt-in text rewrite adapter, but it is intentionally not part of the first-run experience or required workflow. The desktop app currently prioritizes faithful transcription. Any future rewrite UI should keep the original, show changes for review, and never send text to an external provider without clear consent.
The downloadable installers above are the recommended path for most people. These alternatives are for contributors, Linux power users, or anyone who'd rather review the install script than run a downloaded binary.
Windows automatic installation is x64 only and still needs real-machine acceptance testing.
macOS or Linux (Debian/Ubuntu, Fedora, Arch, openSUSE), in Terminal:
bash -c "$(curl -fsSL https://raw.githubusercontent.com/tommyqhoang/ClipboardPlus-and-Dictation/main/bootstrap.sh)"
Windows x64, in PowerShell:
& ([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing https://raw.githubusercontent.com/tommyqhoang/ClipboardPlus-and-Dictation/main/bootstrap.ps1).Content))
These commands execute downloaded code: review the bootstrap script first if you prefer. Administrator permission may be requested by dependency installers; do not run the entire installation as root. macOS uses Homebrew (installing it if missing); Linux uses apt, dnf, pacman or zypper and installs only what is missing; Windows requires Microsoft's App Installer/WinGet and installs Python, FFmpeg, the C++ runtime and a checksum-pinned Whisper build. The latest released source snapshot is downloaded automatically. Windows installs the checksum-pinned Whisper 1.8.7 binary; Linux pins its source-build fallback to the matching release and commit. Git is only needed for that Linux fallback. Dependencies and model downloads need internet access.
Unlike the downloadable installers, this path installs the menu bar/tray
component (PyObjC and Pillow on macOS; pystray and Pillow elsewhere, plus
python-xlib on Linux) into ~/.local/share/whisper-dictation/venv
(Windows: under the app folder in %LOCALAPPDATA%), so the system Python
is not modified. No shell profile or system PATH is silently changed.
For reproducible deployment, set DICTATION_REF to a release tag (Windows: pass
-Ref). Only tags that publish a checksum are accepted; the quick commands install the
latest release. App snapshots are HTTPS downloads checked against that checksum, not
signed application releases.
./install.sh installs the source in this checkout, including local changes; it
does not select or download a release. The one-line commands install the latest
published release source, while the macOS, Windows, and Linux downloads are
prebuilt from that same tagged app version. Their dependency setup differs by
platform and package type, but they use the same app workflows after launch.
Requires Python 3.10+, ALSA/PipeWire, and a Wayland desktop.
./install.sh
The installer detects your package manager (apt, dnf, pacman or zypper), installs only the dependencies that are missing (asking for administrator access only then), downloads the base English model,
builds whisper.cpp if necessary, adds Clipboard+ to the application menu,
and requests the optional GNOME shortcut Super+Shift+D. Open the app to complete
the graphical walkthrough. Check the shortcut setup output; headless installations
skip GNOME registration, and with no desktop session (over SSH, or in a container)
the installer says so and the app opens at your next login instead. Use
--no-packages when dependencies are already installed.
For a server-only setup, ./install.sh --http skips the local model and Whisper
build. Configure the endpoint before recording. Existing settings are never
overwritten by installation or --init-config.
Other Wayland desktops can bind ~/.local/bin/dictate-toggle themselves.
For example, Sway: bindsym $mod+Shift+d exec ~/.local/bin/dictate-toggle --via-shortcut.
GNOME users change the shortcut from the top bar icon's Shortcut menu. The
app re-applies its saved shortcut each time it starts, so
DICTATION_BINDING='<Super><Shift>v' ./install.sh --no-packages only lasts when
the top bar app is not used.
Edit ~/.config/dictation/config.json (or $XDG_CONFIG_HOME/dictation/config.json).
Settings are read on each session, including sessions launched by the desktop
shortcut. This avoids relying on terminal environment variables reaching GNOME.
On macOS, settings default to
~/Library/Application Support/WhisperDictation/config.json; on Windows,
%LOCALAPPDATA%\WhisperDictation\Config\config.json.
dictate-toggle --status prints the active configuration path.
Example local settings:
{
"backend": "local",
"model": "/absolute/path/to/ggml-small.en.bin",
"language": "en",
"device": "default",
"threads": 4,
"prompt": "Vocabulary: PostgreSQL, Kubernetes, Wayland.",
"voice_commands": true,
"live": false
}
The model must be compatible with whisper.cpp: an arbitrary chat-model name or
API key is not a local speech model. Install another model using
DICTATION_MODEL_NAME=ggml-small.en.bin ./install.sh --no-packages, or point
model at your own file. An explicit missing model produces an error rather
than silently using a different model.
Downloads are staged in a resumable partial file and checked for a GGML header
before activation. The built-in English and multilingual base models are verified
against pinned SHA-256 digests. For another model, set DICTATION_MODEL_SHA256
to a trusted digest; otherwise the installer clearly warns that only the GGML
header was checked. Model files selected in the app are also rejected early when
they do not have a whisper.cpp GGML header.
Larger models trade memory and speed for potential accuracy gains; measure them
on your microphone, accent, and vocabulary. Use a multilingual model with
"language": "auto" for automatic language detection. Vocabulary prompts can
help names and specialized terms, but are hints, not guarantees.
An optional vad_model path enables whisper.cpp's voice activity detection.
This requires a whisper-cli version supporting --vad --vad-model and its
compatible VAD model. Exact digital silence is skipped without inference.
Ordinary spoken words such as “music” are preserved.
With voice_commands enabled, “new line” and “new paragraph” become line breaks.
Disable it when you want those phrases transcribed literally.
The HTTP adapter sends WAV audio as multipart form data and expects
{"text": "..."} JSON. It supports whisper.cpp's /inference endpoint and
compatible file-transcription endpoints; it does not support arbitrary chat APIs.
Local server settings:
{
"backend": "http",
"endpoint": "http://127.0.0.1:8080/inference",
"language": "en",
"live": true
}
Run your own whisper.cpp server, for example:
whisper-server -m /path/to/model.bin --host 127.0.0.1 --port 8080.
A source install builds both whisper-cli and whisper-server; packaged whisper.cpp
builds may not include the server, so build that target yourself then. A server retains the model in memory
between requests, avoiding repeated CLI model startup.
For an external compatible provider:
{
"backend": "http",
"endpoint": "https://your-provider.example/v1/audio/transcriptions",
"api_model": "your-provider-speech-model",
"api_key_env": "DICTATION_API_KEY",
"allow_remote": true,
"live": false
}
Enter the key in Settings; it is kept in a private transcription-key file, not in
config.json. The environment variable named by api_key_env still takes precedence
when set in the desktop session or a private launcher (terminal exports only apply to
commands launched from that terminal). Keys are never written to command-line
arguments or application logs. Remote endpoints require HTTPS and explicit
allow_remote consent. Redirects are rejected so credentials and audio cannot
be forwarded unexpectedly. Failed requests are not automatically retried or
silently sent to another provider.
Set live: true, start recording, and run dictate-toggle --watch in a terminal.
Optionally set preview_notifications: true to show draft text in desktop
notifications (which may appear on the lock screen).
This is periodic near-live transcription, not token streaming. It transcribes
the latest live_window seconds (default 30) every live_interval seconds
(default 5), waiting for each result before scheduling another. Drafts are
provisional and never change the clipboard. After stopping, the entire recording
is transcribed once for the final result.
Use a local resident server for repeated previews when possible. The CLI loads its model per request. Remote preview repeatedly uploads overlapping audio and can increase charges. A running preview must finish or time out before final transcription or cancellation completes; recording itself stops promptly.
Normal recovery actions are buttons in the app. The commands below are optional diagnostics for maintainers and automated environments; users do not need them for everyday dictation.
dictate-toggle --status # JSON phase and recovery status
dictate-toggle --cancel # stop and discard the active recording
dictate-toggle --transcribe # retry retained audio
dictate-toggle --copy-last # recover from clipboard failure without inference
dictate-toggle --discard # explicitly delete retained audio
dictate-toggle --devices # list ALSA input names
dictate-toggle --doctor # validate settings and required files/executables
Recordings are bounded to max_seconds (default 300, maximum 600), 16 kHz mono
16-bit PCM. The recorder belongs to a supervised session; no process is signalled
using a stale PID file. OS locks prevent concurrent sessions from overwriting
audio, including the background-worker startup window.
Transcripts are atomically saved in ~/.cache/dictation/last.txt. Current audio
is dictation.pcm; failed sessions retain it for retry. Successful sessions
delete audio unless keep_audio: true. Retained audio must be retried or
explicitly discarded before starting another recording. Silence preserves the
previous transcript and clipboard.
Unix runtime files, audio, settings and the clipboard history use private permissions. Windows uses
per-user LocalAppData directories and their inherited access controls; do not
override the data directories to a shared location. Transcript text and
provider responses are not written to logs; user-facing errors are in
--status. Draft text is removed when the session ends. A hard process kill may
leave retained audio/drafts; these remain private files until explicitly removed
or the next session cleans the draft. Historical logs from older releases are
not automatically deleted.
Environment overrides still work: DICTATION_MODEL, DICTATION_LANGUAGE,
DICTATION_WHISPER_BIN, DICTATION_WHISPER_THREADS, DICTATION_DEVICE,
DICTATION_WL_COPY, DICTATION_KEEP_AUDIO, and corresponding
DICTATION_<SETTING> names. Boolean overrides accept 0/1 or true/false.
XDG config/cache/runtime paths are respected.
Uninstall with ./uninstall.sh (macOS/Linux) or .\uninstall.ps1 (Windows,
PowerShell) after the active session finishes. It removes the installed
command/module, the Start Menu shortcut or GNOME binding, the macOS app bundle,
and stops the clipboard service, retaining models, settings, transcripts,
recoverable audio and the clipboard history by default. Add --purge
(-Purge on Windows) to also delete all of that plus the account key and
telemetry id; without a flag on a terminal it asks. See also
Removing a packaged install.
Your history, settings, account key and models are kept unless you choose otherwise below (Delete all clipboard data in the app erases history first).
--purge to also delete data). It quits the app, unloads and deletes
its login item (the LaunchAgent com.apercallc.clipboardplusdesktop.menubar
in ~/Library/LaunchAgents) and removes Clipboard+.app. Manually: turn
off "Open at Login" in the app, then drag the app to the Trash../Clipboard+-x86_64.AppImage --uninstall (add
--purge to also delete data, --delete-appimage to delete the file). It
removes the GNOME shortcuts (Dictation and Clipboard history), the autostart
entry ~/.config/autostart/clipboardplus.desktop and the menu entry, none
of which disappear by deleting the AppImage alone. Without a working
AppImage, ./uninstall.sh from a checkout does the same integration cleanup../uninstall.sh [--purge] (macOS/Linux) or
.\uninstall.ps1 [-Purge] (Windows). --purge also deletes the clipboard
database, the Clipboard+ account key file, the telemetry id, recordings and
downloaded models; without it they are all kept.MIT, see LICENSE. Bundled third-party software (whisper.cpp under MIT, static ffmpeg under GPL with a source offer) is listed in THIRD-PARTY-NOTICES.md. Report vulnerabilities per SECURITY.md; contributions per CONTRIBUTING.md.
bin/dictate-toggle is a small launcher for the standard-library Python runtime
in lib/dictation.py. lib/desktop.py owns OS-specific commands, paths, and locks.
lib/app.py provides the graphical workflow and lib/app_service.py keeps its
setup and action logic separate from Tk rendering.
The clipboard history is a separate headless process (lib/clipservice.py) that the
tray starts and restarts: lib/clipwatch*.py are the per-OS watchers behind one
Watcher interface, lib/clipstore.py is the only code that touches the SQLite
database and image files, lib/clipsync.py is the two-way account sync (pure logic
over the store and a cloud client, tested offline), lib/clipboardplus.py is the
account/API client, lib/clipui.py the Tk pages, and lib/clipcontrol.py the shared
tray/menu-bar supervision. The window and dictation reach the service through the
database, clip-status.json and small runtime signal files.
lib/rewriting.py owns opt-in text-model requests and review/copy locking;
lib/workflow.py provides the read-only session monitor and safe terminal display.
Each recording starts one detached supervisor, which owns
the recorder, control state, optional preview task, and final transcription.
There is no always-running dictation daemon (only the clipboard service runs
continuously, and only while Clipboard history is on). Local CLI and HTTP transcription
share normalization, recovery, and clipboard delivery.
sh tests/check.sh # everything CI checks, in about a minute
sh tests/check.sh --full # also Python 3.10 and a bare install on 5 distributions (Docker)
git config core.hooksPath .githooks # run tests/check.sh before every push
tests/check.sh mirrors .github/workflows/quality.yml: ruff, mypy --strict for
Linux, macOS and Windows (--platform), shellcheck and shfmt (from Docker when they
are not installed), and the tests with the 90% coverage floor under Xvfb.
The app icon is generated, not hand-edited: run python3 tools/make_icon.py (any
OS, needs Pillow) to rebuild the matching native icon files in lib/ and assets/.
Tests use a subprocess recorder and transcription fixture plus an actual local HTTP server. They do not establish speech accuracy, hardware latency, or live GNOME/Wayland behavior. Those require microphone and desktop testing on Linux.
Run the complete isolated Linux gate with:
docker build -f tests/Dockerfile -t wayland-dictation-quality .
docker run --rm wayland-dictation-quality
tests/benchmark.py optionally compares a real CLI and resident server using
your model and an existing 16 kHz mono WAV. It performs no downloads.
The server adapter follows the
whisper.cpp server contract.