Add: Chats -> Everything, Everywhere, All At Once - #3169
Conversation
…ings section - Add docs/CHAT-PLUGINS.md defining the bridge contract: a chat plugin ships an executable that translates its service into newline-delimited JSON on stdin/stdout - Add core/internal/chat: a shared SQLite message store (keyset pagination, FTS5 search, delivery-status ratchet, myLastTs), a size-capped attachment cache, and the notification suppression policy - Add core/internal/server/chat: supervises bridge processes with restart backoff, ingests their events into the store, and serves chat.* methods plus a "chat" subscription - Add a "chat" plugin type: plugin-schema.json gains a bridge field, PluginService accepts component-less chat manifests and delegates their enable toggle to the backend - Add Settings -> Chats with notification, storage and per-provider sections; each provider's own settings component loads inline and is pushed down to its bridge - Add a provider-agnostic chat window under Modals/Chats, with affordances gated on each provider's declared capabilities - Add ChatService and ChatCycleService singletons, a "chats" IPC target, and keybind actions - Add EchoChatExample, a working reference bridge, plus a chat plugin guide and Python template in the plugin-dev skill - Add modernc.org/sqlite, the first SQL driver in core
- chat.search, chat.chats and chat.history encoded a nil Go slice as JSON null when there were no results, so callers had to guard before reading .length - Found by driving the running backend from the CLI; the Go tests missed it because they assert on values rather than on the encoded wire format
- Fixed ChatService failing to load the shell: `property var messages` already generates a messagesChanged signal, and redeclaring it is a duplicate-signal error that aborted the whole QML config load - Stripped a stray NUL byte from a comment in the same file, which made it read as binary to grep and other tooling - Settings -> Chats now shows one container per installed chat plugin instead of a shared list, holding that provider's state, sign-in, notification policy and plugin settings - Sign-in now renders the provider's QR inline in Settings; previously the code only existed in the chat window, so the button appeared to do nothing - Notification settings moved from global to per provider, with a do-not-disturb that silences a provider without discarding its other choices; the global values remain as the defaults a new provider inherits - Added an optional `warning` field to plugin.json, shown prominently in the provider's container, so a caveat is declared by the plugin rather than hardcoded in the shell - Added JSON tags to NotifyPrefs; without them the policy reached QML with capitalised keys the UI could not read
- Fixed ChatService failing to load the shell: `property var messages` already generates a messagesChanged signal, and redeclaring it is a duplicate-signal error that aborted the whole QML config load - Stripped a stray NUL byte from a comment in the same file, which made it read as binary to grep and other tooling - Settings -> Chats now shows one container per installed chat plugin instead of a shared list, holding that provider's state, sign-in, notification policy and plugin settings - Sign-in now renders the provider's QR inline in Settings; previously the code only existed in the chat window, so the button appeared to do nothing - Fixed the QR rendering as a blank white panel: the UI preferred the themed variant, which is a white code on a transparent background, drawn onto the white plate that exists for scanner contrast - The renderer now produces a single black-on-white image instead of two variants, removing the choice that was being made wrongly - Fixed concurrent QR renders failing with a rename error: cleanup deleted every file in the directory including another render's half-written temp file, so rendering is now serialised and cleanup is scoped to one provider and skips temp files - Notification settings moved from global to per provider, with a do-not-disturb that silences a provider without discarding its other choices; the global values remain as the defaults a new provider inherits - Added an optional `warning` field to plugin.json, shown prominently in the provider's container, so a caveat is declared by the plugin rather than hardcoded in the shell - Added JSON tags to NotifyPrefs; without them the policy reached QML with capitalised keys the UI could not read
- Added a generic `handles` list to a chat: the other identifiers it answers to, such as a phone number, an email address or a username, declared by the bridge - Resolution now matches handles and never parses an id, which is what actually broke phone search: WhatsApp addresses contacts by LID, so there is no phone number in the id at all - The WhatsApp bridge publishes each contact's number as a handle, looked up through the session's own LID mapping - Added AllChats and a chats(all) mode so conversations a provider knows about but that have no messages yet are findable; 94% of a synced WhatsApp account was invisible to search because it filtered on last_ts - The conversation list still shows only chats with activity, so it does not fill with address-book entries - Added a chat popout, message search, forward/copy/delete, Alt+K/J navigation, a shortcut overlay, reverse rotation, attachment staging and sender avatars - Removed a stray NUL byte from ChatCycleService, which had replaced a space in the rotation key
- Messages were rendered newest-first and scrolled to the wrong end: the list was inverted while the model is chronological, so it now runs top to bottom, parks at the newest, and pages older messages in from the top - Alt+K and Alt+J now work, as window shortcuts rather than key handlers, which the composer was swallowing; they move a bordered selection that is deliberately separate from real focus, so typing always reaches the text field - On the selected message: Ctrl+C copies text or the attachment as a file via wl-copy, Alt+R replies, Alt+F forwards, Delete removes it locally and Shift+Delete for everyone, both behind a confirmation - Added chat.deleteLocal, so a message can be removed from this device on any provider, not only where the service supports revoking - Message actions no longer vanish when reaching for them: hover is tracked across the bubble and its action row together - Removed the attach button, which opened a file dialog behind the popout; attachments are pasted with Ctrl+V instead, written from the clipboard and staged with a preview - A sent image now renders its own preview: the file the user picked is cached against the message rather than waiting for the provider to echo it back - The popout is twice as wide and half again as tall, and the runner opens it rather than the full window - Opening a message search result now clears the search box instead of leaving the results over the conversation - The conversation header shows the contact's handles and provider beside their name - Added chat tags, declared by providers and derived by the host, plus filter rules matching a tag, name, handle or provider to hide or show conversations; archived chats, statuses and channels are no longer silently dropped but filtered where the user can see it
- The composer now keeps focus permanently: the popout opens straight into it, and closing an overlay hands focus back, so Escape and every shortcut keep working - Enter always sends; Shift+Enter opens the selected message's attachment or link, and the newline binding is gone - Ctrl+Shift+C copies the selected message, leaving Ctrl+C to the text field for whatever is highlighted there - Text now copies through wl-copy like attachments do, so copy and paste behave the same for both - Escape closes an overlay before the window: help, delete confirmations and the forward picker take focus and the modal leaves the key alone while one is up - Enter confirms a delete, with Cancel focused so a reflexive press is the safe one - Replies were arriving quoting nothing: the host now passes what is being replied to, and the bridge attributes the quote to whoever wrote it rather than to the chat - Message actions no longer vanish while reaching for them; hover survives the gap between the bubble and its buttons - Links are clickable in message bodies, and a provider-supplied link preview is shown as a card - Replaced the filter rule builder with a toggle per tag in settings, covering both the conversation list and the runner - Notifications can be silenced per tag per provider, so a service's statuses or channels can be quiet without silencing the provider - Added scripts/chat-healthcheck.sh, which builds, tests, lints, and exercises the running or a sandboxed backend end to end
- Pasting media never worked: the text field's inner input consumes Ctrl+V before anything wrapping it is told, so the key handler was never reached. Paste is now intercepted and handled outright, staging an image or inserting text at the cursor - A pasted or typed file path followed by a space is attached instead of left as text, and only once the file is confirmed to exist so a sentence starting with a slash stays a sentence - Per-tag notification toggles no longer repeat the named settings above them: archived, muted, unread, group and direct each already have their own control, so only a provider's own categories are listed - Made the earlier keyboard, focus, escape, reply, hover, link and tag work, plus scripts/chat-healthcheck.sh which exercises the backend end to end
- Sending several files recorded a single message while the provider sent one per file, so the conversation showed one attachment when three had gone out; each now gets its own row, with the caption and any reply on the first - Pasting a file copied from a file manager now attaches it: the clipboard offers it as text/uri-list, which the previous pass skipped in favour of the file's name as text - Several files copied at once are all staged, and percent-escaped paths are decoded - Conversation filtering moved out of the shell's settings into each provider's own, since which categories exist is a property of the service; the shell only reads the resulting hiddenTags list, and the runner and conversation list share it - Added a test that three files produce three messages, using a bridge that returns distinct ids as a real provider does
- Resolve core/go.mod: drop the stale indirect entry for dlclark/regexp2/v2, which master promoted to a direct require, and keep the chat system's new dustin/go-humanize indirect. - Run go mod tidy, which also promotes modernc.org/sqlite from indirect to a direct require since internal/chat imports it directly. - Resolve quickshell/Services/PluginService.qml: keep master's _loadPluginTranslations call and run it before the chat plugin early-return, so a chat plugin still registers translations for its settings page.
- Resolve core/go.mod and core/go.sum: keep modernc.org/sqlite and take upstream's tailscale.com v1.102.0 upgrade. - Resolve core/internal/server/server.go: keep both new manager vars, chatManager and notifyActionsManager. - Move the Chats settings tab from index 46 to 47, since upstream's new Dank Island tab claimed 46; update the sidebar entry, the SettingsContent loader and the translation extractor's tab maps, then regenerate settings_search_index.json. - Fix internal/chat/notify.go for upstream's notify.Send, which now also returns the notification id.
- Update the dank-qml-common submodule from fbbdddc to 13b7f2e, the commit recorded by upstream master; the merge kept the older pointer, so DankTextField lacked hidePlaceholderOnFocus. - That missing property made CalendarOverviewCard fail to build, which cascaded up through OverviewTab, HomeExpanded, DankIslandSurface and DankIsland until ShellCore itself failed to load, leaving the shell with no bar and only the theme IPC target registered.
- Restore README.md to master's version, dropping the appended chats section. - Add docs/CHATS.md covering how chats work from a user's side: IPC commands, in-conversation keybinds, attachments, capability-gated UI, tag filters, notifications, storage locations and troubleshooting. - Cross-link docs/CHATS.md and docs/CHAT-PLUGINS.md so the using and authoring halves point at each other.
|
Signal chat added https://github.com/by-architect/DMS-Plugins/tree/main/signalChat |
|
We're moving away from DMS being a monolith in general with moves to strip out the dms greeter, calendar, and eventually file browser, process monitor, etc. This looks more like a standalone app that should be a plugin, with eventually the potential to join the dms suite with the others (dcal, dsearch) after maturation. So I don't really want to push something this large into dms core, but I'll leave it open as a draft for discussion |
|
I designed the chat system to be adapted to a plugin. The plugin system is insufficient for using the chats. It's a large structure to add to the main repository. I'll leave it up to you. For those interested, I will add the changes to the personnel branch. |
Description
A pluggable chat system that provides a unified UI for chat backends like WhatsApp, Signal, Telegram, Mail, etc.
Screenshots / video
Chat:

Chat Runner:

Settings

Commands
For more: https://github.com/by-architect/Phoenix/blob/chats-9/docs/CHATS.md
Plugin Implementation Todo
First plugin, "WhatsApp", available here: https://github.com/by-architect/DMS-Plugins/tree/main/whatsappChat
Signal chat plugin is on the way: https://github.com/by-architect/DMS-Plugins/tree/main/signalChat
Also see chatRunner: https://github.com/by-architect/DMS-Plugins/tree/main/chatRunner
claude -p 'prompt'-based Claude chattingFor plugin implementation: https://github.com/by-architect/Phoenix/blob/chats-9/docs/CHAT-PLUGINS.md