@ai-matrx/messaging 0.1.1 → 0.1.2
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.
- package/CHANGELOG.md +35 -0
- package/dist/react.cjs +17 -5
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +7 -0
- package/dist/react.d.ts +7 -0
- package/dist/react.js +17 -5
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# Changelog — `@ai-matrx/messaging`
|
|
2
2
|
|
|
3
|
+
## 0.1.2 — 2026-08-31
|
|
4
|
+
|
|
5
|
+
**Typing, presence and message broadcast work.** The root defect was in `@ai-matrx/realtime`
|
|
6
|
+
(every channel got a unique instance topic, and for broadcast/presence the topic IS the room);
|
|
7
|
+
this package found it, and it is fixed in realtime 0.2.0. These are the consumer-side changes
|
|
8
|
+
that fix came with — both of them real defects this package was carrying.
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **One presence publisher per conversation.** One channel carries ONE presence entry per key, so
|
|
13
|
+
`useTypists` and `useOnlineUserIds` now read the roster with `presence: { publish: false }`.
|
|
14
|
+
The composer — which actually knows whether this user is typing — is the single publisher. Two
|
|
15
|
+
publishers overwrote each other on every heartbeat.
|
|
16
|
+
- **One composer per conversation, so REPLY works.** `ConversationView` called `useComposer` and
|
|
17
|
+
also rendered `<Composer>`, which called it again: two independent drafts, two independent
|
|
18
|
+
reply targets — picking "reply" on a message set it on an instance the composer's reply bar
|
|
19
|
+
never read — and two typing publishers. The view now owns the state and renders `ComposerView`;
|
|
20
|
+
the exported `Composer` is unchanged for standalone use.
|
|
21
|
+
- **A broadcast message one field short no longer takes down the thread.** `useMessageAction`
|
|
22
|
+
reads `message.action ?? null` instead of assuming the field is present; an off-shape wire
|
|
23
|
+
payload used to throw `undefined.kind` and unmount the conversation.
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- `src/react/realtime-rooms.test.tsx` — renders the real `ConversationView` against a shared,
|
|
28
|
+
topic-deduping fake Supabase client and proves every holder of the conversation topic lands on
|
|
29
|
+
ONE live channel named as declared (0.1.1 produced five), a remote typist reaches the
|
|
30
|
+
indicator, a remote broadcast reaches the thread, and exactly one holder publishes presence.
|
|
31
|
+
All four fail against `@ai-matrx/realtime` 0.1.1.
|
|
32
|
+
|
|
33
|
+
### Consumer action (C28)
|
|
34
|
+
|
|
35
|
+
None. No public API changed. `@ai-matrx/realtime` >= 0.2.0 is required and is picked up
|
|
36
|
+
automatically by the `"latest"` spec.
|
|
37
|
+
|
|
3
38
|
## 0.1.1 — 2026-08-31
|
|
4
39
|
|
|
5
40
|
Caught in review immediately after 0.1.0 published.
|
package/dist/react.cjs
CHANGED
|
@@ -2125,7 +2125,12 @@ function useTypists(conversationId, participants) {
|
|
|
2125
2125
|
const typing = (0, import_react4.useTyping)(
|
|
2126
2126
|
{
|
|
2127
2127
|
topic: conversationId === null ? "" : conversationTopic(conversationId),
|
|
2128
|
-
identity: { userId: host?.identity.userId ?? "" }
|
|
2128
|
+
identity: { userId: host?.identity.userId ?? "" },
|
|
2129
|
+
// READ-ONLY. This label and `useComposer` sit on the SAME topic, which is
|
|
2130
|
+
// one channel carrying ONE presence entry per key — so exactly one of
|
|
2131
|
+
// them publishes, and it is the composer that actually knows whether this
|
|
2132
|
+
// user is typing. Publishing here would overwrite that on every heartbeat.
|
|
2133
|
+
publish: false
|
|
2129
2134
|
},
|
|
2130
2135
|
{ enabled: conversationId !== null && host !== null }
|
|
2131
2136
|
);
|
|
@@ -2152,7 +2157,10 @@ function useOnlineUserIds(conversationId) {
|
|
|
2152
2157
|
const presence = (0, import_react4.usePresence)(
|
|
2153
2158
|
{
|
|
2154
2159
|
topic: conversationId === null ? "" : conversationTopic(conversationId),
|
|
2155
|
-
|
|
2160
|
+
// READ-ONLY, for the same reason: `useComposer`'s typing presence already
|
|
2161
|
+
// puts this user in the roster under this key with their `userId`, and a
|
|
2162
|
+
// second publisher on one entry just fights it.
|
|
2163
|
+
presence: { publish: false, state: { userId: host?.identity.userId ?? "" } }
|
|
2156
2164
|
},
|
|
2157
2165
|
{ enabled: conversationId !== null && host !== null }
|
|
2158
2166
|
);
|
|
@@ -2233,7 +2241,7 @@ function useMessagingAi(conversationId) {
|
|
|
2233
2241
|
}
|
|
2234
2242
|
function useMessageAction(message) {
|
|
2235
2243
|
const host = useRequiredMessagingHost();
|
|
2236
|
-
const action = message.action;
|
|
2244
|
+
const action = message.action ?? null;
|
|
2237
2245
|
const [receipt, setReceipt] = (0, import_react3.useState)(
|
|
2238
2246
|
() => action === null ? null : host.actions.receiptFor(action.kind, message.id, host.identity.userId)
|
|
2239
2247
|
);
|
|
@@ -2765,7 +2773,7 @@ function ConversationView(props) {
|
|
|
2765
2773
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TypingDots, {}),
|
|
2766
2774
|
typists.label
|
|
2767
2775
|
] }) : null }),
|
|
2768
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2776
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ComposerView, { composer, disabled: false })
|
|
2769
2777
|
] });
|
|
2770
2778
|
}
|
|
2771
2779
|
function MessageGroupView(props) {
|
|
@@ -2898,6 +2906,10 @@ function MessageActionChips(props) {
|
|
|
2898
2906
|
}
|
|
2899
2907
|
function Composer(props) {
|
|
2900
2908
|
const composer = useComposer(props.conversationId);
|
|
2909
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ComposerView, { composer, disabled: props.conversationId === null });
|
|
2910
|
+
}
|
|
2911
|
+
function ComposerView(props) {
|
|
2912
|
+
const composer = props.composer;
|
|
2901
2913
|
const textareaRef = (0, import_react5.useRef)(null);
|
|
2902
2914
|
(0, import_react5.useEffect)(() => {
|
|
2903
2915
|
const node = textareaRef.current;
|
|
@@ -2931,7 +2943,7 @@ function Composer(props) {
|
|
|
2931
2943
|
value: composer.value,
|
|
2932
2944
|
placeholder: "Message",
|
|
2933
2945
|
"aria-label": "Message",
|
|
2934
|
-
disabled: props.
|
|
2946
|
+
disabled: props.disabled,
|
|
2935
2947
|
onChange: (event) => {
|
|
2936
2948
|
composer.setValue(event.target.value);
|
|
2937
2949
|
composer.onKeystroke();
|