SalastilandClaude Sonnet 5 73ff641e6a Bundle Sneedchat smilies client-side; add Discord reactions and reaction/reply UI polish
- 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]>
2026-08-12 18:41:39 -04:00

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/supervises chatd and 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 .onion host 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.

S
Description
No description provided
Readme
2.2 MiB
Languages
Rust 57.7%
QML 42.3%