diff --git a/client/public/version.js b/client/public/version.js index 66c3b5b..ddcd473 100644 --- a/client/public/version.js +++ b/client/public/version.js @@ -1,5 +1,5 @@ // Maintainer-controlled web client version. // Format: YYYY.MM.DD Rn (example: 2026.02.20 R2) -window.CHGRID_WEB_VERSION = "2026.02.27 R283"; +window.CHGRID_WEB_VERSION = "2026.02.27 R284"; // Optional display timezone for timestamps. Falls back to America/Detroit if unset/invalid. window.CHGRID_TIME_ZONE = "America/Detroit"; diff --git a/docs/protocol-notes.md b/docs/protocol-notes.md index 407b20d..979245f 100644 --- a/docs/protocol-notes.md +++ b/docs/protocol-notes.md @@ -48,6 +48,7 @@ This is a behavior guide for packet semantics beyond raw schemas. - `item_upsert` is full-state replacement for one item, not partial patch. - `item_upsert.item.display` is server-owned display text for readonly/system properties (for example: `createdBy`, `updatedBy`, `createdAt`, `updatedAt`, `capabilities`, `useSound`, `emitSound`). - `item_action_result` messages are intended for direct screen-reader/user status feedback. +- Successful `item_pickup` and `item_drop` also emit system chat lines to other users in the room. - Piano runtime control no longer depends on parsing `item_action_result.message` text. - `item_piano_status` carries machine-readable piano events (`use_mode_entered`, record/playback transitions). - `item_use_sound` contains absolute item world coordinates (`x`, `y`) and sound path. diff --git a/server/app/server.py b/server/app/server.py index 4c71f3f..5b8e44e 100644 --- a/server/app/server.py +++ b/server/app/server.py @@ -1753,6 +1753,15 @@ class SignalingServer: item.updatedByName = actor_name await self._broadcast_item(item) self._request_state_save() + item_text = f"{item.title} ({self._item_type_label(item)})" + await self._broadcast( + BroadcastChatMessagePacket( + type="chat_message", + message=f"{client.nickname} picked up {item_text}.", + system=True, + ), + exclude=client.websocket, + ) await self._send_item_result(client, True, "pickup", f"Picked up {item.title}.", item.id) return @@ -1776,6 +1785,15 @@ class SignalingServer: item.updatedByName = actor_name await self._broadcast_item(item) self._request_state_save() + item_text = f"{item.title} ({self._item_type_label(item)})" + await self._broadcast( + BroadcastChatMessagePacket( + type="chat_message", + message=f"{client.nickname} dropped {item_text} at {item.x}, {item.y}.", + system=True, + ), + exclude=client.websocket, + ) await self._send_item_result(client, True, "drop", f"Dropped {item.title}.", item.id) return