@genex-ai/cli-demo 1.2.4 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -180,9 +180,10 @@ room.on("host", () => {/* host migrated — the new host takes over writing */})
180
180
  de-duped) — but for a value you render every frame, read `room.shared.get(...)` in your loop (as
181
181
  `updateHud` does) rather than wiring render state through the event.
182
182
 
183
- ## Custom events (shots, emotes, chat)
183
+ ## Custom events (shots, emotes)
184
184
 
185
- For one-off actions that aren't continuous state, use `send`. The shooter judges the hit **locally**
185
+ For one-off actions that aren't continuous state, use `send`. (Text chat is NOT one of these —
186
+ it has its own channel, `room.chat`; see below.) The shooter judges the hit **locally**
186
187
  against what it sees (favor-the-shooter) and announces it; the victim applies its own damage.
187
188
 
188
189
  **`send` reaches only OTHER clients — it never echoes back to you.** So apply your own action's
@@ -210,6 +211,47 @@ if (!t) return;
210
211
  const px = t.stateRaw.x, pz = t.stateRaw.z; // test against the raw latest
211
212
  ```
212
213
 
214
+ ## Text chat
215
+
216
+ Chat has its own channel — **never build it on `send`**. The relay stamps the sender from the
217
+ verified session, sanitizes the text, and replays the recent history to late joiners; `send` gives
218
+ you none of that and lets a client post under any name it likes.
219
+
220
+ Install the overlay with `npx genex controller chat` (writes `src/controllers/chat/chat-overlay.ts`),
221
+ then wire it:
222
+
223
+ ```ts
224
+ import { ChatOverlay } from "./controllers/chat/chat-overlay.ts";
225
+
226
+ const chat = new ChatOverlay({
227
+ room,
228
+ onTypingChange: (typing) => { keyboard.enabled = !typing; }, // ← REQUIRED, see below
229
+ });
230
+ ```
231
+
232
+ **The one bug everybody ships: the game reads the keyboard while the player types.** Someone
233
+ types "was that a wall?" and their character walks, reverses and jumps. Either pass
234
+ `onTypingChange` (above) or check `chat.isTyping` in your input code. There is no third option.
235
+
236
+ Rolling your own UI instead? Three rules:
237
+
238
+ ```ts
239
+ room.chat.send("on my way"); // your OWN line comes back via on() — don't also append it
240
+ room.chat.on((m) => {
241
+ el.textContent = `${m.name}: ${m.text}`; // textContent, NEVER innerHTML — remote input
242
+ });
243
+ room.chat.history; // render on mount: a late joiner sees the conversation
244
+ ```
245
+
246
+ - **Your own lines echo back** (unlike `send`), in server order with the server's timestamp.
247
+ Appending locally on submit shows every message twice.
248
+ - **`m.name` is relay-verified** — safe to display, and a player cannot post under another name.
249
+ - **`m.text` is sanitized but not markup-escaped** — control and bidi characters are stripped by
250
+ the relay, but you must still render it as text.
251
+
252
+ Chat has its own rate budget (~2 lines/second per player), separate from gameplay traffic: a chat
253
+ flood cannot starve movement, and a busy game cannot starve chat.
254
+
213
255
  ## Persistence helper
214
256
 
215
257
  ```ts