- Bundle Sneedchat's 175 site smilies as local assets instead of fetching live over Tor; render them full-size in the chat buffer and emoji picker, with animated (gif/webp) smilies looping via a separate AnimatedImage row since Qt rich-text <img> can only render a static first frame. - Add Discord message reactions: toggleReaction RPC, per-message reaction pills sized to match Discord's own client, a shared reaction/emoji-picker popup anchored and edge-clamped to the message row, and a row-wide hover toolbar (reactions/reply/more) gated to Discord only. - Extend the row hover highlight to every protocol, not just Discord, since it's generic UX polish with nothing protocol-specific about it. - Restyle the message context menu with per-item icons and Discord-style colored entries (e.g. red Delete), using anchored icon+text layout instead of RowLayout/MenuItem auto-sizing for reliable alignment across items. Co-Authored-By: Claude Sonnet 5 <[email protected]>
dms-irc (Dank Chat)
A Dank Material Shell plugin that brings a unified chat frontend to the shell: IRC, Discord, and Sneedchat (SockChat, the Tor-only XenForo chat feature) today, with XMPP/Matrix/Slack support landing protocol by protocol without frontend changes. The architecture is deliberately service-agnostic - the QML side only ever sees a unified account/buffer/message model, never a protocol-specific concept.
Why two parts
Quickshell's QML runtime only exposes Unix-domain sockets, not raw TCP/TLS, so the actual protocol work can't live in QML. Instead:
chatd(chatd-rs/, Rust) speaks each protocol directly and translates it into a small unified JSON model (accounts, buffers, messages) over a Unix socket. It never exposes protocol-specific concepts to the plugin; it speaks the unified model, and each backend module happens to be how that model gets populated for a given service.- The QML plugin (top-level
*.qml,plugin.json) spawns/superviseschatdand renders whatever it reports: a DankBar status pill, a slideout for quick access, and a floating window for a full chat session.
chatd-rs/ chatd - the Rust daemon, one crate per concern:
chatd/src/backend/ one module per protocol (irc, discord, sockchat, ...)
chatd/src/rpc/ the Unix-socket JSON-RPC server
chatd/src/store.rs SQLite-backed scrollback persistence
chatd/src/runtime.rs protocol-agnostic connection/buffer/message state
*.qml the DMS plugin surfaces plugin.json points at directly:
daemon supervisor, bar widget, startup check, settings entry
views/ main chat UI: slideout/popout window, accounts, buffers, messages
settings/ per-protocol settings panels loaded by views/ChatSettings.qml
forms/ per-protocol account-connect forms loaded by views/AccountsPanel.qml
plugin.json DMS plugin manifest
Building chatd
cd chatd-rs
cargo build --release
Enable the plugin in DMS (Settings → Plugins, or dms ipc call plugins enable dankChat). StartupCheck.qml verifies the built binary exists at
chatd-rs/target/release/chatd before allowing activation.
Opening the chat UI
Besides the DankBar pill (ChatBarWidget.qml), ChatDaemon.qml registers an
IpcHandler under the dankChat target for scripting/keybinds:
dms ipc call dankChat toggle # open/close the slideout (or the floating window, if that's what's open)
dms ipc call dankChat popout # open (or bring forward) the floating window directly
Protocol backends
- IRC - TLS with SASL PLAIN, NickServ auto-identify, autojoin.
- Discord - official cross-device QR login (the same one discord.com/app offers), a real-time gateway client, message edit/delete/reactions/replies sync.
- Sneedchat (SockChat) - the Tor-only chat built into Kiwi Farms. Runs over an
embedded Tor client (or an external SOCKS5 proxy), solves the site's own
proof-of-work anti-bot gate, and connects to every configured room simultaneously
(one persistent websocket per room, sharing a single login). Supports message
edit/delete and avatars (fetched through the same Tor session and cached locally,
since the frontend has no route to a
.onionhost on its own). - XMPP/Matrix/Slack - not implemented yet; see the project plan for the milestone list.
Wire protocol
Newline-delimited JSON over a Unix socket at $XDG_RUNTIME_DIR/dms-chat/chatd.sock:
request: {"id": 1, "method": "sendMessage", "params": {...}}
response: {"id": 1, "result": {...}} or {"id": 1, "error": "..."}
push: {"event": "message", "data": {...}}
Core methods: listAccounts, listBuffers, listProtocols, addAccount,
removeAccount, setAccountConnected, joinBuffer, partBuffer, sendMessage,
editMessage, deleteMessage, subscribe/unsubscribe, getBacklog (SQLite-backed,
persists across chatd restarts). Each protocol also has its own account-creation
method (addAccount for IRC, addDiscordAccount, addSockChatAccount, ...).
Push events: message, messageUpdated, messageDeleted, reactionsChanged,
presenceChange, bufferListChange, connectionState, notification, plus
per-protocol login-flow events (discordLoginQr/discordLoginScanned/
discordLoginResult, sockChatLoginStatus/sockChatLoginResult). message and
presenceChange are only delivered to clients that called subscribe for that buffer.
Status
Core loop is verified end to end through the actual DMS-spawned plugin: connect to
real IRC networks over TLS with SASL PLAIN auth, log into Discord via QR and Sneedchat
via username/password (+ optional TOTP 2FA), list/add accounts, join channels, send/
receive messages through a slideout or floating window, mentions flagged as highlights
and pushed as notification events, and scrollback that survives a chatd restart.
Not yet built: tab-complete, multi-account grouping in the buffer list, and XMPP/
Matrix/Slack backends. See the project plan for the full milestone list.