The project's README from github.com/crmne/zapfast. As one markdown file: README.md.
WhatsApp, native and fast. ZapFast is a WhatsApp client written in Rust with egui. It uses whatsapp-rust for the WhatsApp Web protocol. It runs on Linux, macOS, and Windows, links to your phone as a companion device, and has no browser engine. In our Linux test, it opens in under a second and uses about 200 MB of idle RAM, compared with 1.13 GB for WhatsApp Web and its Chromium processes. See the measurements.
Want Spotify just as fast and native? Spotifast is ZapFast's sibling: the same native interface, for Spotify. Both are built on fastframe, the shared foundation for native Rust apps built with egui.
https://github.com/user-attachments/assets/2bf86b54-45fc-4add-8de7-c426c3cdad9b
See zapfast.rocks for downloads and guides.
Angel
finds Ángel.
The filters stay on one row and scroll horizontally in narrow sidebars.
Unnamed groups use a shared participant summary for their title and subtitle.
It names each saved contact by its whole first name as saved on the phone (the
first word of the name when none is known), repeated names appear as Andrea ×3, and your
own entry is shown as You. The sender before a group's last message goes by
the same name. Contacts synced by an older version are read once more from the
phone after updating, so their first names are known too.
Incomplete group metadata preserves known names and retries with backoff;
an empty cached subject remains eligible for recovery.
Typing indicators show other participants, excluding your own linked devices.
Newsletter channels are read-only; publishing channel posts is not supported.
Channels show their own pictures, read from the channel's details on WhatsApp.LC_TIME) on Linux. Select in a
message's menu, or Ctrl-click (Command-click on macOS) on a message, starts
a selection: click more messages to add or remove them, Shift-click to add
everything up to the one you click, or drag across messages to add every
one you pass (the list scrolls when you hold the pointer at its top or
bottom edge). A drag that starts beside the bubbles, off the text, starts a
selection too; a drag over the text outside a selection still selects the
text to copy. Then Forward… sends them together,
in their original order, or Escape cancels. A batch goes out one message at
a time, each starting once the one before it reached WhatsApp, so a picture
cannot overtake the text that came before it.system-ui font on Linux), or the bundled Inter
if you choose it under Settings > Appearance > Font, and emoji look as they
do in the system's other apps: Apple Color Emoji on macOS, Segoe UI Emoji on
Windows, and the desktop's emoji font on Linux. The bundled Noto Color Emoji
draws whatever those lack (country flags on Windows, or every emoji on a
Linux desktop without an emoji font). Emoji-only messages are larger.:name to autocomplete an emoji without leaving the composer,
or @ in a group to mention a member.
Reply, react with any emoji, edit, forward, delete, and check when a message was sent,
delivered, or read. Replies can be text, attachments, voice messages,
stickers, or GIFs. A reply never goes out without its quote: if the
original is no longer available on this computer, nothing is sent, the text
or attachments return to the composer, and a voice message waits above it
to be sent again or discarded. Cancel the reply to send without a quote.
Quotes carry a bar and name in the quoted person's colour; clicking one
scrolls back to the original, which flashes briefly, as a search result
does. The same right-click menu copies a message's ID, which
helps when looking one up for a bug report.
Opening a message's context menu outlines that message until the menu closes.
The full reaction picker stays beside the menu and adds a target preview.
The conversation stays still while you choose; the emoji
grid can scroll. Quick reactions learn from usage on this computer, independently
of inserted emoji. These preferences do not sync from the phone.
Hovering a message also shows a small smiley control beside it; clicking it
opens the full reaction picker for that message, so right-click is never required.
Deleting a message asks first and says which copies go:
deleting for everyone leaves "This message was deleted" in the chat, while
deleting for yourself removes the message from this computer only. Neither
can be undone, because the archive here is the only copy..bin. A photo, video,
or voice message sent to be viewed once shows as a view-once placeholder:
WhatsApp opens it only on your phone, as it does in WhatsApp Web.signal.art link or
.wastickers file, or make your own packs from any sticker with a
right-click. Open WhatsApp sticker packs shared in a chat and add them, or
send any of your packs as one.
Turn any picture into a sticker: crop it square, keep its transparent
background, and tag it with emojis.
Animated packs remain animated. Packs are stored as WebP files on your
computer.~) fill in, across
chats, replies, mentions, and notifications.chat.whatsapp.com invite
link shows the group's name, size, and description, and joins it (or sends a
join request when admins approve members) without leaving ZapFast.socks5h:// and http:// proxies resolve WhatsApp's host themselves, and
socks5:// hands the proxy the first address this computer resolves.Ctrl+Q, or disable this behavior in Settings. The window
reopens where you left it; on Windows and X11, one that would open on no
connected monitor (for example on a display that is now unplugged) moves to
the middle of the primary monitor.~/.config/autostart/zapfast.desktop on Linux, a LaunchAgent in
~/Library/LaunchAgents on macOS, or a Run entry for your user on Windows,
and removes it when turned off. zapfast --start-hidden does the same by hand;
it opens the window anyway when no tray is available. The Flatpak does not
offer this setting yet.99+ above 99.
Windows must be using its regular taskbar icon size for overlays to appear. As
in WhatsApp, the count is of chats, not of the messages in them or of toasts
kept in Windows notification history: archived, muted, and locked chats are
left out, and a chat marked unread counts. Reading a chat lowers the count,
and zero removes the overlay.[time, date] Name: format. Contact names and numbers are also
selectable, with Brazilian numbers shown as (DDD) XXXX-XXXX or
(DDD) XXXXX-XXXX.Ctrl+K or Ctrl+Shift+F searches your chats,
where ↑/↓ selects a matching chat and Enter opens it ready for typing;
Ctrl+F searches the open chat as in WhatsApp (↑/↓ walk the results
and Enter jumps to one; with no chat open it searches your chats, and in
Settings it searches the settings), Alt+↑/↓ or WhatsApp's
Ctrl+Shift+[/Ctrl+Shift+] switches chats and
keeps the active chat visible in the list, ↑ in an empty input edits your
previous message, PgUp/PgDn scroll the open chat by about a page,
Home/End jump to the top or newest message of the open chat (when the
input is empty), Esc cancels the current action, Ctrl+L focuses the
message input, Ctrl+N opens New chat, Ctrl+B collapses or expands the
chat list, Ctrl+Shift+L locks ZapFast when an app lock password is set,
and ? (outside text fields) or
Ctrl+/ opens Keyboard shortcuts (use Command instead of Ctrl on macOS).
The × at the left of the shortcut hints
hides the bar; bring it back with Show shortcut hints under the message
box in the Keyboard shortcuts dialog.Ctrl+B, or the button beside
New chat) leaves a narrow column of avatars.
It shows the same chats as the full list under the current filter, with
unread badges (dimmed for muted chats); hovering names a chat, clicking opens
it, and Ctrl+B brings the full list back.On macOS with Homebrew: brew install --cask crmne/tap/zapfast.
ZapFast was previously called FastsApp. Version 0.13.0 introduces the new package and executable names. On Arch Linux:
yay -S zapfast-bin # the released build, ready made
yay -S zapfast # the release, built from source
yay -S zapfast-git # built from the latest commit
With Nix, install the package directly from its flake:
nix profile install github:crmne/zapfast
NixOS configurations can add the repository as a flake input and include
inputs.zapfast.packages.${pkgs.system}.default in
environment.systemPackages.
Builds for every release are on the releases page:
| Platform | File |
|---|---|
| Linux x86_64 and arm64 | zapfast-vX.Y.Z-<target>.tar.gz, with the desktop file and icon in packaging/ |
| Linux x86_64 and arm64, one file | zapfast-X.Y.Z-x86_64.AppImage or -aarch64.AppImage: make it executable and run it |
| Windows x64 and arm64 | zapfast-vX.Y.Z-<target>-setup.exe (no administrator rights needed), or the .zip |
| macOS, universal | zapfast-vX.Y.Z-macos-universal.dmg |
On macOS, the rounded Dock icon matches the app bundle. Native menus provide
Settings, editing, search, view controls, and window commands. The traffic
lights share the chat header, leaving more room for conversations in a normal
window. Settings is also available with ⌘,.
The macOS release process signs the app with Developer ID, submits the DMG to Apple's notarization service, and staples and validates its ticket before publishing. Open the DMG and drag ZapFast to Applications. When upgrading from FastsApp on macOS, quit the old app and remove its application bundle after installing ZapFast.
Releases before 0.13.0 keep their original FastsApp filenames.
Flatpak packaging lives in packaging/flatpak/, following Spotifast's source
manifest and release-bundle setup. Future releases will attach an x86_64
.flatpak bundle; install a downloaded bundle with flatpak install --user FILE
and run flatpak run rocks.zapfast.ZapFast. Flathub publication is pending;
ZapFast is not yet listed there. See PACKAGING.md for local builds
and preparing a Flathub submission. File selection uses desktop portals;
the sandbox has no general access to your home directory.
The archive key is a random 256-bit secret in Secret Service on Linux, Keychain
on macOS, or Windows Credential Manager. Linux needs a working Secret Service
provider (for example GNOME Keyring or KeePassXC with Secret Service enabled).
If the keyring is locked or unavailable, unlock it and click Retry; ZapFast keeps
its archive intact and waits before connecting. It never saves a replacement
plaintext archive. Back up both the archive and its OS keyring key: copying only
archive.db to another computer is insufficient.
A missing key is different from a locked keyring. If ZapFast says the key is
missing, restore the original OS credential store or use the original profile
location. Do not delete the archive or create replacement credentials: neither
can decrypt the existing archive. If the original key cannot come back,
Start over… on that screen renames the unreadable archive to
archive-unreadable-<date>.db beside it, forgets the linked session, and
shows the linking screen: linking again brings recent history back from your
phone. If the saved key has an invalid length, ZapFast replaces it only after
the unreadable archive has been moved aside; retrying leaves it unchanged while
the archive is still present. This cannot recover a key already lost by the
OS credential store. Remove the old ZapFast entry under Linked devices on the
phone afterwards. For help, report the OS, app version, whether
the profile was moved/restored, and the error text with personal paths removed.
Never attach the archive, keys, or full logs from older releases.
Only archive.db and its SQLite journal/WAL are encrypted. Device credentials in
session.db, downloaded media, profile pictures, favorite sticker files and settings
remain ordinary files. Use full-disk encryption for those files, swap, backups and
remnants of the old plaintext archive. Migration removes the original only after
verifying its encrypted copy; deletion cannot guarantee erasure from SSDs or
snapshots. Keyring unlocking also does not protect against software running as you
while your login is unlocked.
ZapFast needs Rust, a C/C++ toolchain, CMake and Perl (for bundled OpenSSL). rust-toolchain.toml pins the exact version. On Linux,
it also needs GUI development packages:
# Debian and Ubuntu
sudo apt install libxkbcommon-dev libwayland-dev libgl1-mesa-dev libasound2-dev cmake perl
# Arch
sudo pacman -S libxkbcommon wayland mesa alsa-lib cmake perl
Then:
cargo install --path .
zapfast
With Nix, nix develop provides the pinned Rust toolchain and all native build
dependencies. From the checkout, use nix build .#zapfast to build the package
or nix run .#zapfast to run it.
cargo install puts the binary on your PATH, but it does not add a launcher
entry. On Linux, a source build can have the entry the packages install:
cargo build --release --locked
packaging/install-user.sh
The script installs the binary, the icon, and a desktop entry under
~/.local (or the prefix you pass), with Exec= set to the installed binary's
full path, since a graphical session often lacks ~/.local/bin on PATH. It
is Linux-only; on macOS and Windows use a packaged release or run the binary
directly.
whatsapp-rust is pinned to a Git commit because version 0.7.0 on crates.io
enables a simd feature that needs nightly Rust. The pinned commit builds on
stable Rust and includes the upstream fixes for missing app-state snapshots and
conflicts that make no progress. ZapFast does not reset your session to recover
a collection.
On first start, scan the QR code from WhatsApp under Linked devices, Link a device. To link without the camera, click Link with phone number instead, enter your number with its country code, then enter the shown code on your phone.
WhatsApp then sends your recent history. This can take a few minutes. A banner shows the progress. New messages arrive live, and your phone does not need to stay on the same network.
Right-click a chat or message to open its menu. Double-click beside a message,
or on its edge, to reply to it (a double-click on its text still selects the
word). Open Settings from the gear or with Ctrl+,, and close them the same
way. The pencil opens New chat, with Message yourself and
+ Add contact at the top, followed by searchable contacts. Add contact also
lets you message a new number without saving it. Also save to your phone's
contacts in that dialog adds the contact to your phone's address book too, as
the phone asks; the next contact starts from your last choice. You
can also open a group member's contact card. Renaming a contact opens with its
first and last name as saved, each of which may hold several words; a name
saved without a separate first name opens whole in the first-name field. Saved
names sync through WhatsApp to your phone and linked devices.
Lock chat in a chat's right-click menu moves the chat into a locked folder: it disappears from the chat list, search, and the unread badge, and its messages never raise a desktop notification. The lock state syncs with your phone and other linked devices.
Choose Locked beside the other chat filters, type your local code, and press Enter or choose Open locked chats. The tab appears when locked chats exist, without a count or names before opening. If no local code exists, it offers to set one up. The local code is separate from your phone's code and is a visibility control, not an extra encryption layer. Search inside the open tab filters its chats. Leaving it, changing or clearing the code in Settings, or closing the window hides the locked chats and closes any open locked conversation. Typing the code into ordinary search remains an alternative way in. Revealed locked chats are currently read-only: sending messages and forwarding into them remain disabled.
After linking or upgrading, chats wait up to ten seconds for WhatsApp's lock state before appearing. Chats already known to be locked stay hidden. If the lock state cannot be confirmed in time, the chats appear with a notice that chats locked on the phone may show until they sync, and recovery keeps retrying in the background. The recovered state is saved in the encrypted archive for offline use.
Protocol logs omit private payloads and raw error details, including verbose logging. Panic logs record the source location without the panic payload. Pairing signature failures and rate limits retain a diagnostic category.
Offline previews for these states use --demo --demo-page channel,
--demo --demo-page locked, --demo --demo-page locked-open, and
--demo --demo-page keyring. The open locked-folder preview uses demo-code.
Use --demo-page locked-prompt, locked-setup, new-chat, unnamed-group,
or react-picker for the new dialogs, shared group summaries, and reactions.
group-info, group-info-rename, group-info-saving, and group-info-locked
show a group's info with its name and photo editable, being renamed, saving,
and locked to admins. meta-ai shows a Meta AI reply with code and a table.
The protocol dependency includes the upstream WhatsApp Business pairing fix. Device-store migration waits until an updated window is acknowledged, preserving startup rollback; an unused legacy column is retained for 0.14 compatibility.
Like WhatsApp Web's screen lock, Settings > Privacy > App lock hides
ZapFast behind a password. It is off until you choose Set password… and
type a password of at least six characters twice. ZapFast then starts locked
and locks again after 1 minute, 15 minutes (the default), or 1 hour without
input in its window, a choice under Lock after. Time spent hidden in the
tray counts as time without input. Lock ZapFast in the tray menu and
Ctrl+Shift+L lock it at once; the tray entry is there while a password is
set. Change password… and Turn off… ask for the
current password first.
While locked the window shows only the lock screen: no chats, names, pictures, or messages, and shortcuts, pasting, and dropped files do nothing. Messages keep arriving but stay unread, and their desktop notifications say only "New message" from ZapFast, without the chat, the sender, the text, or a picture, with the message sound but no per-chat or mention sound, since those would tell who wrote. Clicking one opens the message after you unlock. Locking withdraws the notifications still on the desktop on Linux. The unread count on the taskbar stays, since a number names no one and says nothing that a "New message" notification does not. Voice messages and videos stop, and a recording in progress is discarded.
Wrong passwords make the next try wait, from one second after the third up to half a minute. Forgot password? Unlink this computer on the lock screen is the only way back in without it: after you confirm, it unlinks this computer as Unlink this computer does, which deletes the chats stored here, and the lock lifts once WhatsApp has unlinked it. Link again with a new code. Any unlink, including one from your phone, turns the app lock off.
The app lock keeps people using this computer out of your chats. It encrypts
nothing beyond what the archive already is, and someone who can edit your
files can remove it from settings.json. ZapFast stores only a salted
PBKDF2-HMAC-SHA256 verifier of the password (600,000 rounds), in
settings.json beside the locked-chats code, because the lock screen must
appear before the archive opens. It is independent of the locked-chats code:
unlocking one never opens the other, and locking ZapFast closes the locked
tab. Preview it with --demo --demo-page app-lock (the password is
demo-password), app-lock-wrong, app-lock-forgot, app-lock-settings,
or app-lock-setup.
Business messages keep their image, formatted text, timestamp, and options together in one bubble. Reply buttons immediately send the selected response, quoting the original message so the business can recognize your choice. Simple list buttons open a centered dialog with sections, descriptions, and a full-row selection target; choosing an item sends that response. Hover highlights the full action row, following the card edges. Link buttons open your browser, and copy-code buttons copy the offered code locally.
Carousels retain separate cards and images in a horizontally scrollable strip. Each card can open web links or copy codes; reply, calling, and shopping actions that require an unsupported carousel envelope stay unavailable.
Reply buttons require a connection and a writable conversation. They pause while sending, and become available again if the send fails. Actions with a phone icon are unavailable in ZapFast; use WhatsApp Web or your phone. Hovering explains which restriction applies. Replies from other devices retain their quotes too.
| Text and reply options | Image and website link |
|---|---|
![]() |
![]() |

| Carousel cards | Poll participant details |
|---|---|
![]() |
![]() |
These screenshots use synthetic offline chats. See the usage guide for download behavior and the remaining limitations.
The search field at the top of Settings narrows the page to the settings
whose name or description contains what you type, ignoring case and accents,
and hides sections with nothing left. A match on a section's name keeps the
whole section. Translated settings are found in the interface language and in
English. Ctrl+F on the Settings page focuses the field, and Esc clears it.
Settings > Appearance > Language chooses the interface language. Auto
follows the first of the operating system's preferred languages that ZapFast
has a translation for, and falls back to English when it has none. Brazilian
Portuguese, German, Spanish, Italian, French, Russian, Simplified Chinese,
and Turkish cover the chat list, search, composer, shortcut hints, Settings,
and dates. Translations are compiled from gettext PO
files at build time, with no runtime parsing or network access. Message
contents, contact names, logs, and protocol errors are never translated, and
copied messages keep WhatsApp's [time, date] Name: format.
Settings > System > Proxy sends the WhatsApp connection, media, profile
pictures, GIF search, Signal sticker imports, and update checks through a proxy. It accepts
socks5h://host:port (the proxy resolves names, as Tor expects),
socks5://host:port, and http://host:port, each with an optional
user:password@. A bare host:port is an HTTP proxy. Changing it reconnects
at once. When the field is empty, ZapFast uses ALL_PROXY or HTTPS_PROXY
from the environment and honors NO_PROXY.
| What | Linux | Notes |
|---|---|---|
| Settings | ~/.config/zapfast/settings.json |
JSON, safe to edit; the app lock password and the locked-chats code are kept only as salted verifiers |
| Device keys | ~/.local/state/zapfast/session.db |
Owned by whatsapp-rust; deleting it unlinks |
| Messages | ~/.local/state/zapfast/archive.db |
SQLCipher-encrypted SQLite, unlocked by the OS keyring; raw messages retain attachment keys |
| Attachments, avatars | ~/.cache/zapfast/ |
Safe to delete; Settings > Files > Change… sends new downloads to another folder, leaving earlier ones in place |
| Favorite stickers and packs | ~/.local/state/zapfast/stickers/ |
Plain WebP files; each pack is a folder |
| Wallpaper image | ~/.local/state/zapfast/wallpaper.jpg |
Copy of the chosen picture, or .png, .webp, .gif; deleted by Remove image |
| Log of the last run | ~/.local/state/zapfast/zapfast.log |
--verbose for more; Settings > Files > Log > Open shows it in its folder when no app opens it |
macOS and Windows use the standard platform directories selected by the
directories crate. On first start, ZapFast moves settings, the linked session,
message archive, favorite stickers, caches, and window state from fastsapp
(or the earlier fastwhatsapp) paths. Existing ZapFast directories take
precedence and are never overwritten. Quit FastsApp before starting ZapFast;
if an older copy is still running, the new launch brings its window forward.
Your phone may keep showing the old linked-device name until you link again.
On Linux and macOS, ZapFast restricts its configuration, state, and cache
directories to the current user (0700), including existing installations.
Startup stops if those directories cannot be created or secured, before opening
logs or databases. Windows uses the permissions inherited from your user profile.
Settings → Appearance → Theme uses the same picker as Spotifast, with
Follow system, Light, Dark, and its Catppuccin, Catppuccin Latte, Nord, Ristretto,
Tokyo Night, Rose Pine, Rose Pine Moon, and Rose Pine Dawn palettes.
Choose Open themes folder below the picker to add
JSON palettes beside settings.json; How to make a theme opens
the guide with every colour name. The
bundled palettes are written into the themes folder once, as ordinary files
to read or change; ZapFast never rewrites them, and a deleted one stays
deleted. For example:
{"base":"dark","colors":{"accent":"#89b4fa","bubble_out":"#293954"}}
Unspecified colors inherit the light or dark base. Spotifast palettes also work:
chat backgrounds, bubbles, and links derive from their interface colors when not
specified. Color names match Palette
in src/theme.rs; use #RRGGBB or #RRGGBBAA. The last accepted palette is cached
in settings, so a missing or damaged theme file does not reset your appearance.
Linux watches the themes folder for changes without periodic repaints. On other
platforms, use zapfast reload-themes after editing. The command also works while
the window is closed and never launches a stopped app.
Settings → Appearance → Wallpaper offers Theme first, then WhatsApp's light and dark wallpaper colours, with a live preview that shows exactly what the chat will. Theme, the default, uses the active palette's chat colour, so a local or Omarchy theme colours the conversation too and a theme change shows at once. Settings from earlier versions that still had the old default (Beige, or Black in dark mode) move to Theme once; a colour you chose stays. Add doodles controls only the SVG layer, so disabling it leaves the selected background colour in place; the doodles switch between dark and light lines to stay visible on any colour. Light and dark selections are stored independently, and the embedded SVG is rendered at its native size and repeated across the conversation without stretching.
Choose image… on the same page uses a picture of your own instead, in light
and dark mode alike, filling the conversation and cropped from the centre
without stretching. ZapFast keeps its own copy as wallpaper.jpg (or .png,
.webp, .gif) in its state directory, so the original can move; a picture
larger than 2560 pixels on its long side is scaled down first. The image
replaces the colour and doodles, which return with Remove image, which also
deletes the copy. If the copy goes missing or cannot be read, the colour shows
instead.
On Omarchy, Follow system and Omarchy read the active desktop palette and follow its changes in native, portable, and source builds, even without installed hooks. Other desktops keep their normal light/dark system preference. Native packages additionally register a missing per-user template and theme hook on first launch; existing user files are preserved. Flatpak uses the desktop's light/dark preference and does not read host theme files or install desktop hooks.
ZapFast checks GitHub once a day when Check for updates is enabled.
Click Update in the banner to download and verify a newer release, then
Restart to update when convenient. Download updates automatically is
optional and off by default; it downloads in the background and still waits for
you to restart. Downloads contact GitHub's API and release-asset hosts and are
checked against the release's SHA-256 checksums. Before downloading a package,
the updater verifies the checksum manifest's Ed25519 publisher signature using
its embedded public key. Missing or invalid signatures stop the update.
The updater keeps a backup and restores it if the updated app cannot start;
its helper writes what it did to helper.log in the update's staging folder
beside the app.
Release builds also carry GitHub provenance attestations, independently
verifiable with gh attestation verify FILE -R crmne/zapfast.
See update signing for key custody and recovery.
The in-app updater supports marked portable downloads, the Windows installer,
and the macOS app in Applications. Keep zapfast-portable.txt beside a portable
executable. AUR, DEB, RPM, Flatpak, Cargo and Homebrew installations use their
package manager, and an AppImage is replaced by downloading the new one. Older portable downloads without the marker need one manual
upgrade. No account or additional service is needed.
cargo run --features demo -- --demo # sample chats, no connection
cargo run --features demo -- --demo-page login # or settings, pair, info, light, …
cargo run --features demo -- --demo-shot shot.png --demo-page chat,light
cargo run --features demo -- --demo-tour # Space starts/replays a 41-second tour
cargo run --features demo -- --demo-tour --demo-tour-script whats-new # what 0.16 added
cargo run --features demo -- --demo-hover 900,400 # holds a fake pointer there
cargo test --all-features # includes a headless layout of every screen
cargo clippy --all-targets --all-features -- -D warnings
To include a default GIPHY key for GIF search, set it at build time. A key in Settings overrides it:
ZAPFAST_GIPHY_KEY=your-key cargo build --release
The earlier FASTSAPP_GIPHY_KEY build variable remains supported as a fallback.
AGENTS.md describes the architecture and the rules for changes.
CI checks the complete lockfile against RustSec advisories with cargo audit.
Candidate-specific manual checks and results are tracked in the release PR.
The demo feature uses offline sample chats in a fresh temporary directory.
It does not open your linked account, read your message archive, connect to
WhatsApp, or register a tray icon. You can run it alongside your regular app.
cargo build --locked --features demo
./target/debug/zapfast --demo-tour --demo-size 1280x800
The ZapFast Demo window waits for Space. The 41-second tour starts with
search, switches chats with keyboard shortcuts, scrolls, right-clicks a message
and selects Reply, types quickly, completes emoji and mentions, searches the GIF
picker and sends a still sticker, opens group information and the shortcut list,
and changes themes through Settings. It uses the normal mouse and keyboard handlers;
a local responder handles outgoing messages with no WhatsApp connection.
The demo's profile pictures, photos, video clips, GIF-search thumbnails, and captioned stickers
are stock media compiled into demo builds only, all CC0 or in the public domain
(assets/demo/SOURCES.md lists each source); demo GIF search uses these local
fixtures, and the remaining stickers come from the bundled Noto emoji font. The tour makes no
sound and holds its final frame. Space rebuilds the sample and replays.
For an automatic start, add --demo-tour-delay 5000 (milliseconds).
Use --demo instead of --demo-tour to explore the sample chats yourself.
--demo-tour-script whats-new plays an 86-second tour of what ZapFast 0.16
added instead: the composer's plus menu and poll dialog, searching a chat and
narrowing it to a day, the photo preview, videos and round video messages
playing in place, sticker shelves and sticker search, message info in a group,
the Favorites and label chips and a chat's menu, recording a voice message and
choosing a playback speed, the chat list folded to avatars, hover controls,
Ctrl-click and Shift-click selection with Forward, and Settings (languages,
search, and the light theme). --demo-tour-script launch is the default.
Demo runs never open the microphone: recording plays back a synthetic tone.
Use --demo-page rtl-self for a self-chat of mixed Hebrew, Arabic, and
English lines.
Use --demo-page composer-tools to preview the WhatsApp-style composer pill
and its attachment and poll menu. typing, mention, and
emoji-complete preview the multiline field and inline suggestions.
Use --demo-page chat-menu to preview the compact chat context menu,
--demo-page chat-header-menu for the menu at the top of an open chat, and
--demo-page chat,voice,voice-menu for a voice message's menu with its speeds.
--demo-page video shows a video and round video messages,
--demo-page video-expanded the video over the whole window, and
video-playing or note-playing starts one of them, silently.
For deterministic theme screenshots, --demo-page settings,omarchy and
--demo-page settings,omarchy-light preview following dark and light Omarchy
palettes without changing the desktop theme.
Use --demo-page shared-contact for an offline shared-contact card with synthetic
vCard data, or --demo-page interactive for text and button messages, or
--demo-page interactive-media for messages with an image, and
--demo-page interactive-list for a list message,
--demo-page interactive-list-dialog for its grouped choice dialog, --demo-page carousel
for a scrolling strip or --demo-page carousel-pair for two cards, and --demo-page poll-empty, poll-voted, or poll-results
for voting states. Use --demo-page interactive-actions for reply, list, copy-code, and unavailable
form actions. Add ,light to
preview any of these in the light theme. Capture the app's own frame without desktop
content:
./target/debug/zapfast --demo --demo-page interactive-media --demo-shot interactive.png
./target/debug/zapfast --demo --demo-page interactive-media,light --demo-shot interactive-light.png
On Omarchy, run omarchy screenrecord, select the demo window, then press Space
in ZapFast. Recording has no audio unless you explicitly enable desktop or
microphone audio. Stop with omarchy screenrecord --stop-recording after the
tour finishes. The default capture records a fixed rectangle, so keep the demo
window visible and stationary until recording stops.
To annotate the video with a visible pointer, click rings, and outlined shortcut
labels, add --demo-tour-events tour.json when launching the tour. After
recording, run:
python3 scripts/render-demo.py recording.mp4 tour.json launch.mp4 --start 0.8
Set --start to the recording time (in seconds) when you pressed Space. The
export trims the setup footage, adds a caption band below the app, and produces
a silent H.264 MP4. It requires ffmpeg with libass support and ffprobe.
--scale 1.5 keeps 1.5 pixels per point, for example 1920 pixels across from a
1280-point window recorded at 2x; the default is one pixel per point.
These annotations are added during video export, not drawn by the app. The trace contains only pointer coordinates and shortcut labels, not typed text.
Instead of recording the screen, the tour can save its own frames. With
--demo-tour-frames DIR, it starts at once, plays on a virtual clock (steady
frame times even when a frame is slow to draw), writes every frame as a PNG
at the window's pixel size, and quits when the tour ends. --demo-fps sets the
rate (30 by default). The window still has to be shown somewhere; a virtual
output keeps it off your screens. Then assemble and annotate the frames:
cargo build --release --locked --features demo
./target/release/zapfast --demo-tour --demo-tour-script whats-new \
--demo-size 1280x800 --demo-tour-frames frames --demo-tour-events tour.json
ffmpeg -framerate 30 -i frames/frame-%05d.png -c:v libx264 -crf 12 -pix_fmt yuv420p raw.mp4
python3 scripts/render-demo.py raw.mp4 tour.json whats-new.mp4 --scale 1.5
ZapFast is an unofficial client and is not affiliated with WhatsApp or Meta. Using an unofficial client may be against WhatsApp's terms of service and could get an account suspended. Use it at your own risk.
Release packaging uses the native-packages gem. macOS release builds automatically sign and notarize when the Apple CI credentials are configured. native-packages.yaml declares packages and downstream repositories; native recipes and installation assets live in packaging/; see PACKAGING.md for local commands and CI behavior.
MIT. Inter and Noto Color Emoji are under the SIL Open Font License; the icons
and the chat wallpaper doodles are from Lucide (ISC).
The notification sounds are Pidgin's, under the GPL-2.0
(see assets/sounds/).