DBDelve
Dive deep into your data. A fast, native, multi-engine database client written in Rust. Free and open source, no telemetry.
翻译准备中
An open-source, native desktop WhatsApp client for macOS, Windows and Linux, in Rust and GPUI.
翻译准备中
正在加载完整 README。
查看完整 README ↗An open-source, native desktop WhatsApp client for macOS, Windows and Linux. It starts in 0.57 s, opens a chat in 13 ms and holds 120 MB of memory with a conversation open.
| Measured | Median |
|---|---|
| Start: from the process being launched to the chat list drawn | 0.57 s |
| Open a chat: from the click to the conversation's frame painted | 13 ms |
| Search: from a keystroke to the results' frame painted | 8 ms |
| One frame while scrolling a conversation | 3.8 ms |
| Memory with a conversation open (what Activity Monitor calls Memory) | 120 MB |
| Processor, left alone for a minute | 0.08 % of one core |
Download, macOS on Apple Silicon (.dmg, 0.1.0) | 22.5 MB |
Measured on 2026-10-04 on a MacBook Pro (M1 Pro, 16 GB, macOS 26.6.2), release build, on the built-in demo data. The three frame times are the application's own work, from the input event to the frame laid out and painted; the display shows it at its next refresh. How we measured has the ranges, the method, what the figures leave out, and the script that takes them again on your machine: docs/media/measure.sh.
Why: the window is native and drawn on the GPU by GPUI, with no browser engine inside; it reads a local SQLite database (encrypted with SQLCipher) and never waits for the network; and it is one process.
The demo history is small, so the local store was also timed over 100,000 messages: the query that opens a chat takes 0.9 ms, and a search takes 0.6 ms for a rare word and 82 ms for the most common one.
Four clips of the application on the demo data, at the speed they were recorded. The application saved its own frames, 20 a second; on a screen it draws at the display's rate. Each picture links to the MP4.
The screenshots follow your theme and show the built-in demo data: nobody in them exists.
Watch the video (42 seconds, no sound).
It is written in Rust, with the UI in GPUI, the framework behind the Zed editor. It is local-first: the window only reads from a database on your computer, and a background engine keeps that database in sync. Opening a chat, scrolling back and searching are local queries.
How the client reaches WhatsApp is pluggable. A provider is an adapter around one backend: a hosted API, a self-hosted gateway, a protocol library. It is one trait, Provider, in crates/client-provider. The reference adapter is for wuapi. A provider only translates: storage, search, retries, the outbox and the window are the client's, and the window never calls a provider.
client-provider, async-trait and futures. crates/provider-example is one file to copy.id, capabilities, list_accounts, list_chats, fetch_messages, send, mark_read, download_media and subscribe. The other 60 are optional and have defaults.Capabilities, and an optional method is only called when its flag is on; the window hides what a flag does not turn on (no reaction picker without reactions, no attach button without media_upload).ProviderKind and its name to --provider in crates/app/src/cli.rs, and construct it in crates/app/src/providers.rs. The example's lines are the ones marked provider-example.crates/provider-example/tests/contract.rs: the account and chat listings, a send repeated, history paged newest first, a refusal that is not retried, an event pushed.The smallest provider that compiles, for a backend with nothing in it (a doc-test of provider-example, so it is built with the workspace):
use async_trait::async_trait;
use client_provider::*;
use futures::StreamExt as _;
pub struct Minimal;
#[async_trait]
impl Provider for Minimal {
fn id(&self) -> &'static str {
"minimal"
}
// What the backend can do. The UI hides the rest.
fn capabilities(&self) -> Capabilities {
Capabilities { chat_list: true, replies: true, ..Capabilities::none() }
}
async fn list_accounts(&self) -> ProviderResult<Vec<Account>> {
Ok(Vec::new())
}
async fn list_chats(&self, _: &AccountId, _: Option<Cursor>) -> ProviderResult<Page<Chat>> {
Ok(Page::last(Vec::new()))
}
async fn fetch_messages(
&self, _: &AccountId, _: &ChatId, _: Option<Cursor>, _limit: u32,
) -> ProviderResult<Page<Message>> {
Ok(Page::last(Vec::new())) // newest first
}
// The same client_id must always answer the same message id.
async fn send(&self, message: OutgoingMessage) -> ProviderResult<SendReceipt> {
let message_id = MessageId::new(message.client_id.as_str());
Ok(SendReceipt { message_id, status: DeliveryStatus::Sent, timestamp: None })
}
async fn mark_read(&self, _: &AccountId, _: &ChatId, _: Option<&MessageId>) -> ProviderResult<()> {
Ok(())
}
async fn download_media(&self, _: &AccountId, _: &MediaRef) -> ProviderResult<MediaData> {
Err(ProviderError::Unsupported("media"))
}
// Live updates: a stream of events, pushed or polled for.
async fn subscribe(&self) -> ProviderResult<EventStream> {
Ok(futures::stream::pending().boxed())
}
}
That is the example provider in the window: an in-memory backend that says back what it is sent.
cargo run -p wuapi-inbox --features provider-example -- --provider example
以上为 README 的开头部分。其余内容请阅读原文。 查看完整 README ↗