Brings Facebook-style chat to every page. The topnav Messages icon now opens a Chats panel (people search, recent conversations with unread dots, a New message button, and a link to the full page). Picking a person opens a small chat window docked at the bottom right, so you can keep chatting while you browse.
Up to three windows sit side by side (fewer on narrower screens); older ones collapse to avatar heads.
A window minimizes to a round avatar head that counts unread messages; clicking the head reopens it with fresh history.
Open chats survive page navigation and reloads within the tab (sessionStorage).
New message opens its own small window with a "To:" search; picking someone turns it into their chat.
A profile's Send Message button, the live "sent you a message" toast, and a message notification on /notifications all open the docked window instead of leaving the page. The notification is marked read by the conversation load, as before.
Phones (768px and below) and the /messages page itself are unchanged: every trigger falls back to its normal link.
The message input now has the same layout everywhere (page, windows, embed): [+] [ text … 😊 ] [➤]. + reveals the attachment and GIF buttons, which tuck away again while you type; a paste, drop or pick opens them so the pending count stays visible. This gives the text field nearly the full width on small phones.
How it is built. Each window is the existing dp-chat in a new mode="window", so sending, live delivery, AI frames, typing, read receipts, delete, report, attachments and older history all come from the one AppChat implementation. All windows share a single WebSocket through chat/ChatSocketHub.js. chat/ConversationItem.js is now the one conversation-row builder for both /messages and the panel. A window loads history through the same GET /messages?with_uid= JSON the page uses, before subscribing, so the reconnect sync starts from the newest loaded message. No server changes: the dock uses only /messages/conversations, /messages/search, GET /messages?with_uid= and WS /messages/ws. New z-index token --z-chat-dock (950). The Chats panel also counts as "something open" for Devii's Escape and double-click triggers.
Fix included. Clickable toasts were never clickable: .dp-toast-host sets pointer-events: none and .dp-toast-link never turned it back on, so clicking a notification toast did nothing on any page. Linked toasts now accept clicks; plain toasts still let clicks through.
Tests. New browser tests in tests/e2e/messages.py cover: the icon opening the panel and a docked window; sending and receiving live in a window; minimize to a head with an unread count that survives navigation; the compose window; no dock on /messages; the mobile icon opening the page; and the + button revealing and hiding the tools. Updated: the message-notification click test (now asserts the docked window, plus a new phone-width variant that asserts navigation), the paste-to-attach test, and the two GIF-in-chat tests (they tap + first).
Validation:
In the project's Docker image (Python 3.13, headless Chromium): pytest tests/unit tests/api tests/e2e/messages.py tests/e2e/notifications tests/e2e/giphy.py tests/e2e/profile tests/e2e/feed.py tests/e2e/reports -> 4,861 passed, 8 skipped, 4 failed in 30 minutes.
All four failures pass when rerun on their own, on this branch and on unchanged origin/master920a679d, so they are order-dependent in a long run rather than regressions (three of them are API tests, and this PR changes no server code):
The last one checks the send button is disabled right after a click; the lock releases on the server echo, so a fast echo can clear it before the assertion runs.
The remaining e2e directories were not run locally; CI runs the full suite.
from devplacepy.main import app imports clean; every changed JS module passes node --check; CSS braces balance; no em-dashes.
Brings Facebook-style chat to every page. The topnav Messages icon now opens a **Chats** panel (people search, recent conversations with unread dots, a New message button, and a link to the full page). Picking a person opens a small chat window docked at the bottom right, so you can keep chatting while you browse.
- Up to three windows sit side by side (fewer on narrower screens); older ones collapse to avatar heads.
- A window minimizes to a round avatar head that counts unread messages; clicking the head reopens it with fresh history.
- Open chats survive page navigation and reloads within the tab (`sessionStorage`).
- **New message** opens its own small window with a "To:" search; picking someone turns it into their chat.
- A profile's **Send Message** button, the live "sent you a message" toast, and a message notification on `/notifications` all open the docked window instead of leaving the page. The notification is marked read by the conversation load, as before.
- Phones (768px and below) and the `/messages` page itself are unchanged: every trigger falls back to its normal link.
The message input now has the same layout everywhere (page, windows, embed): `[+] [ text … 😊 ] [➤]`. **+** reveals the attachment and GIF buttons, which tuck away again while you type; a paste, drop or pick opens them so the pending count stays visible. This gives the text field nearly the full width on small phones.
**How it is built.** Each window is the existing `dp-chat` in a new `mode="window"`, so sending, live delivery, AI frames, typing, read receipts, delete, report, attachments and older history all come from the one `AppChat` implementation. All windows share a single WebSocket through `chat/ChatSocketHub.js`. `chat/ConversationItem.js` is now the one conversation-row builder for both `/messages` and the panel. A window loads history through the same `GET /messages?with_uid=` JSON the page uses, before subscribing, so the reconnect `sync` starts from the newest loaded message. **No server changes**: the dock uses only `/messages/conversations`, `/messages/search`, `GET /messages?with_uid=` and `WS /messages/ws`. New z-index token `--z-chat-dock` (950). The Chats panel also counts as "something open" for Devii's Escape and double-click triggers.
**Fix included.** Clickable toasts were never clickable: `.dp-toast-host` sets `pointer-events: none` and `.dp-toast-link` never turned it back on, so clicking a notification toast did nothing on any page. Linked toasts now accept clicks; plain toasts still let clicks through.
**Tests.** New browser tests in `tests/e2e/messages.py` cover: the icon opening the panel and a docked window; sending and receiving live in a window; minimize to a head with an unread count that survives navigation; the compose window; no dock on `/messages`; the mobile icon opening the page; and the **+** button revealing and hiding the tools. Updated: the message-notification click test (now asserts the docked window, plus a new phone-width variant that asserts navigation), the paste-to-attach test, and the two GIF-in-chat tests (they tap **+** first).
Validation:
- In the project's Docker image (Python 3.13, headless Chromium): `pytest tests/unit tests/api tests/e2e/messages.py tests/e2e/notifications tests/e2e/giphy.py tests/e2e/profile tests/e2e/feed.py tests/e2e/reports` -> **4,861 passed, 8 skipped, 4 failed** in 30 minutes.
- All four failures pass when rerun on their own, on this branch **and** on unchanged `origin/master` `920a679d`, so they are order-dependent in a long run rather than regressions (three of them are API tests, and this PR changes no server code):
```
tests/api/admin/containers/manage.py::test_primary_admin_sees_and_can_manage_others_private_instance
tests/api/posts/featured.py::test_post_page_featured_topics_pick_varies
tests/api/sync/export.py::test_a_post_exports_in_the_inbound_shape_and_no_private_user_field_leaks
tests/e2e/messages.py::test_send_button_locks_during_send_and_prevents_duplicate
```
The last one checks the send button is disabled right after a click; the lock releases on the server echo, so a fast echo can clear it before the assertion runs.
- The remaining e2e directories were not run locally; CI runs the full suite.
- `from devplacepy.main import app` imports clean; every changed JS module passes `node --check`; CSS braces balance; no em-dashes.
Docs updated: `README.md`, `services/messaging/CLAUDE.md`, `static/js/CLAUDE.md`, `static/css/CLAUDE.md` (stacking table).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The topnav Messages icon opens a Chats panel (people search, recent
conversations, New message, link to the full page). Picking a person
opens a small chat window docked bottom-right; windows minimize to
avatar heads with unread counts and survive navigation within the tab.
Profile Send Message, the live message toast and message notifications
on /notifications open the same window. Phones and /messages keep the
full page.
Each window is dp-chat in mode="window", sharing one WebSocket through
chat/ChatSocketHub.js; chat/ConversationItem.js is the shared row
builder. No server changes.
The message input becomes [+] [text + emoji] [send] everywhere: + reveals
the attach and GIF buttons, which tuck away while typing.
Also fixes linked toasts never receiving clicks (pointer-events).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
retoor
merged commit 078d0657ff into master2026-11-07 18:10:35 +01:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Brings Facebook-style chat to every page. The topnav Messages icon now opens a Chats panel (people search, recent conversations with unread dots, a New message button, and a link to the full page). Picking a person opens a small chat window docked at the bottom right, so you can keep chatting while you browse.
sessionStorage)./notificationsall open the docked window instead of leaving the page. The notification is marked read by the conversation load, as before./messagespage itself are unchanged: every trigger falls back to its normal link.The message input now has the same layout everywhere (page, windows, embed):
[+] [ text … 😊 ] [➤]. + reveals the attachment and GIF buttons, which tuck away again while you type; a paste, drop or pick opens them so the pending count stays visible. This gives the text field nearly the full width on small phones.How it is built. Each window is the existing
dp-chatin a newmode="window", so sending, live delivery, AI frames, typing, read receipts, delete, report, attachments and older history all come from the oneAppChatimplementation. All windows share a single WebSocket throughchat/ChatSocketHub.js.chat/ConversationItem.jsis now the one conversation-row builder for both/messagesand the panel. A window loads history through the sameGET /messages?with_uid=JSON the page uses, before subscribing, so the reconnectsyncstarts from the newest loaded message. No server changes: the dock uses only/messages/conversations,/messages/search,GET /messages?with_uid=andWS /messages/ws. New z-index token--z-chat-dock(950). The Chats panel also counts as "something open" for Devii's Escape and double-click triggers.Fix included. Clickable toasts were never clickable:
.dp-toast-hostsetspointer-events: noneand.dp-toast-linknever turned it back on, so clicking a notification toast did nothing on any page. Linked toasts now accept clicks; plain toasts still let clicks through.Tests. New browser tests in
tests/e2e/messages.pycover: the icon opening the panel and a docked window; sending and receiving live in a window; minimize to a head with an unread count that survives navigation; the compose window; no dock on/messages; the mobile icon opening the page; and the + button revealing and hiding the tools. Updated: the message-notification click test (now asserts the docked window, plus a new phone-width variant that asserts navigation), the paste-to-attach test, and the two GIF-in-chat tests (they tap + first).Validation:
pytest tests/unit tests/api tests/e2e/messages.py tests/e2e/notifications tests/e2e/giphy.py tests/e2e/profile tests/e2e/feed.py tests/e2e/reports-> 4,861 passed, 8 skipped, 4 failed in 30 minutes.origin/master920a679d, so they are order-dependent in a long run rather than regressions (three of them are API tests, and this PR changes no server code):The last one checks the send button is disabled right after a click; the lock releases on the server echo, so a fast echo can clear it before the assertion runs.
from devplacepy.main import appimports clean; every changed JS module passesnode --check; CSS braces balance; no em-dashes.Docs updated:
README.md,services/messaging/CLAUDE.md,static/js/CLAUDE.md,static/css/CLAUDE.md(stacking table).🤖 Generated with Claude Code