@meshagent/meshagent-react 0.5.6 → 0.5.8

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 CHANGED
@@ -1,3 +1,9 @@
1
+ ## [0.5.8]
2
+ - Stability
3
+
4
+ ## [0.5.7]
5
+ - Stability
6
+
1
7
  ## [0.5.6]
2
8
  - Stability
3
9
 
@@ -26,6 +26,8 @@ export interface UseMessageChatResult {
26
26
  attachments: FileUpload[];
27
27
  setAttachments: (attachments: FileUpload[]) => void;
28
28
  schemaFileExists: boolean;
29
+ onlineParticipants: Participant[];
30
+ cancelRequest?: () => void;
29
31
  }
30
32
  export declare function fileToAsyncIterable(file: File): AsyncIterable<Uint8Array>;
31
33
  export declare function useChat({ room, path, participants, participantNames, initialMessage, includeLocalParticipant }: UseMessageChatProps): UseMessageChatResult;
package/dist/cjs/chat.js CHANGED
@@ -4,8 +4,10 @@ exports.ChatMessage = void 0;
4
4
  exports.fileToAsyncIterable = fileToAsyncIterable;
5
5
  exports.useChat = useChat;
6
6
  const react_1 = require("react");
7
+ const meshagent_1 = require("@meshagent/meshagent");
7
8
  const file_upload_1 = require("./file-upload");
8
9
  const document_connection_scope_1 = require("./document-connection-scope");
10
+ const document_connection_scope_2 = require("./document-connection-scope");
9
11
  class ChatMessage {
10
12
  constructor({ id, text, attachments }) {
11
13
  Object.defineProperty(this, "id", {
@@ -82,12 +84,9 @@ function* getParticipantNames(document) {
82
84
  }
83
85
  }
84
86
  }
85
- function* getOnlineParticipants(room, document) {
86
- for (const participantName of getParticipantNames(document)) {
87
- if (participantName === room.localParticipant?.getAttribute("name")) {
88
- yield room.localParticipant;
89
- }
90
- for (const remoteParticipant of room.messaging.remoteParticipants) {
87
+ function* getOnlineParticipants(roomParticipants, participantNames) {
88
+ for (const participantName of participantNames) {
89
+ for (const remoteParticipant of roomParticipants) {
91
90
  if (remoteParticipant.getAttribute("name") === participantName) {
92
91
  yield remoteParticipant;
93
92
  }
@@ -123,17 +122,35 @@ function fileToAsyncIterable(file) {
123
122
  return (hasNativeStream ? nativeStream : sliceStream)();
124
123
  }
125
124
  function useChat({ room, path, participants, participantNames, initialMessage, includeLocalParticipant }) {
126
- const { document, schemaFileExists } = (0, document_connection_scope_1.useDocumentConnection)({ room, path });
125
+ const { document, schemaFileExists } = (0, document_connection_scope_2.useDocumentConnection)({
126
+ room,
127
+ path,
128
+ onConnected: (doc) => {
129
+ ensureParticipants(doc, room.localParticipant, includeLocalParticipant ?? true, participants ?? [], participantNames ?? []);
130
+ if (initialMessage) {
131
+ sendMessage(initialMessage);
132
+ }
133
+ },
134
+ onError: (error) => {
135
+ console.error("Failed to connect to document:", error);
136
+ }
137
+ });
127
138
  const [messages, setMessages] = (0, react_1.useState)(() => document ? mapMessages(document) : []);
128
139
  const [attachments, setAttachments] = (0, react_1.useState)([]);
129
- (0, document_connection_scope_1.useDocumentChanged)({
140
+ const [documentMembers, setDocumentMembers] = (0, react_1.useState)(() => document ? getParticipantNames(document) : []);
141
+ (0, document_connection_scope_2.useDocumentChanged)({
130
142
  document,
131
- onChanged: (doc) => setMessages(mapMessages(doc)),
143
+ onChanged: (doc) => {
144
+ setMessages(mapMessages(doc));
145
+ setDocumentMembers(getParticipantNames(doc));
146
+ },
132
147
  });
133
148
  const selectAttachments = (0, react_1.useCallback)((files) => {
134
149
  const attachmentsToUpload = files.map((file) => new file_upload_1.MeshagentFileUpload(room, `uploaded-files/${file.name}`, fileToAsyncIterable(file), file.size));
135
150
  setAttachments(attachmentsToUpload);
136
151
  }, [room]);
152
+ const roomParticipants = (0, document_connection_scope_1.useRoomParticipants)(room);
153
+ const onlineParticipants = (0, react_1.useMemo)(() => Array.from(getOnlineParticipants(roomParticipants, documentMembers)), [roomParticipants, documentMembers]);
137
154
  const sendMessage = (0, react_1.useCallback)((message) => {
138
155
  const children = document?.root.getChildren() || [];
139
156
  const thread = children.find((c) => c.tagName === "messages");
@@ -150,7 +167,7 @@ function useChat({ room, path, participants, participantNames, initialMessage, i
150
167
  for (const path of message.attachments) {
151
168
  m.createChildElement("file", { path });
152
169
  }
153
- for (const participant of getOnlineParticipants(room, document)) {
170
+ for (const participant of onlineParticipants) {
154
171
  room.messaging.sendMessage({
155
172
  to: participant,
156
173
  type: "chat",
@@ -161,15 +178,18 @@ function useChat({ room, path, participants, participantNames, initialMessage, i
161
178
  },
162
179
  });
163
180
  }
164
- }, [document, attachments]);
165
- (0, react_1.useEffect)(() => {
166
- if (document) {
167
- ensureParticipants(document, room.localParticipant, includeLocalParticipant ?? true, participants ?? [], participantNames ?? []);
168
- if (initialMessage) {
169
- sendMessage(initialMessage);
181
+ }, [document, path, attachments, onlineParticipants, room]);
182
+ const cancelRequest = (0, react_1.useCallback)(() => {
183
+ for (const participant of onlineParticipants) {
184
+ if (participant instanceof meshagent_1.RemoteParticipant && participant.role === 'agent') {
185
+ room.messaging.sendMessage({
186
+ to: participant,
187
+ type: "cancel",
188
+ message: { path },
189
+ });
170
190
  }
171
191
  }
172
- }, [document]);
192
+ }, [room, path, onlineParticipants]);
173
193
  return {
174
194
  messages,
175
195
  sendMessage,
@@ -177,5 +197,7 @@ function useChat({ room, path, participants, participantNames, initialMessage, i
177
197
  attachments,
178
198
  setAttachments,
179
199
  schemaFileExists,
200
+ onlineParticipants,
201
+ cancelRequest,
180
202
  };
181
203
  }
@@ -3,5 +3,5 @@ interface ClientToolkitsProps {
3
3
  toolkits: RemoteToolkit[];
4
4
  public?: boolean;
5
5
  }
6
- export declare const useClientToolkits: ({ toolkits, public: isPublic, }: ClientToolkitsProps) => void;
6
+ export declare const useClientToolkits: ({ toolkits, public: isPublic }: ClientToolkitsProps) => void;
7
7
  export {};
@@ -2,10 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.useClientToolkits = void 0;
4
4
  const react_1 = require("react");
5
- const useClientToolkits = ({ toolkits, public: isPublic = false, }) => {
5
+ const useClientToolkits = ({ toolkits, public: isPublic = false }) => {
6
6
  (0, react_1.useEffect)(() => {
7
7
  toolkits.forEach(toolkit => toolkit.start({ public_: isPublic }));
8
8
  return () => toolkits.forEach(toolkit => toolkit.stop());
9
- }, []);
9
+ }, [toolkits, isPublic]);
10
10
  };
11
11
  exports.useClientToolkits = useClientToolkits;
@@ -1,7 +1,9 @@
1
- import { MeshDocument, RoomClient } from '@meshagent/meshagent';
1
+ import { MeshDocument, RoomClient, RemoteParticipant } from '@meshagent/meshagent';
2
2
  export interface UseDocumentConnectionProps {
3
3
  room: RoomClient;
4
4
  path: string;
5
+ onConnected?: (document: MeshDocument) => void;
6
+ onError?: (error: unknown) => void;
5
7
  }
6
8
  export interface UseDocumentConnectionResult {
7
9
  document: MeshDocument | null;
@@ -18,10 +20,11 @@ export interface UseDocumentConnectionResult {
18
20
  * @param room An already‑connected RoomClient.
19
21
  * @param path Path to the document inside the room.
20
22
  */
21
- export declare function useDocumentConnection({ room, path }: UseDocumentConnectionProps): UseDocumentConnectionResult;
23
+ export declare function useDocumentConnection({ room, path, onConnected, onError }: UseDocumentConnectionProps): UseDocumentConnectionResult;
22
24
  type onChangedHandler = (document: MeshDocument) => void;
23
25
  export declare function useDocumentChanged({ document, onChanged }: {
24
26
  document: MeshDocument | null;
25
27
  onChanged: onChangedHandler;
26
28
  }): void;
29
+ export declare function useRoomParticipants(room: RoomClient | null): Iterable<RemoteParticipant>;
27
30
  export {};
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.useDocumentConnection = useDocumentConnection;
4
4
  exports.useDocumentChanged = useDocumentChanged;
5
+ exports.useRoomParticipants = useRoomParticipants;
5
6
  const react_1 = require("react");
6
7
  /**
7
8
  * Connects to a Mesh document inside an existing RoomClient and keeps it in sync.
@@ -12,7 +13,7 @@ const react_1 = require("react");
12
13
  * @param room An already‑connected RoomClient.
13
14
  * @param path Path to the document inside the room.
14
15
  */
15
- function useDocumentConnection({ room, path }) {
16
+ function useDocumentConnection({ room, path, onConnected, onError }) {
16
17
  const [schemaFileExists, setSchemaFileExists] = (0, react_1.useState)(null);
17
18
  const [document, setDocument] = (0, react_1.useState)(null);
18
19
  const [error, setError] = (0, react_1.useState)(null);
@@ -41,12 +42,18 @@ function useDocumentConnection({ room, path }) {
41
42
  await new Promise(resolve => setTimeout(resolve, 100));
42
43
  setDocument(doc);
43
44
  setError(null);
45
+ if (onConnected) {
46
+ onConnected(doc);
47
+ }
44
48
  }
45
49
  catch (err) {
46
50
  console.error('Failed to open document:', err);
47
51
  if (cancelled)
48
52
  return;
49
53
  setError(err);
54
+ if (onError) {
55
+ onError(err);
56
+ }
50
57
  // Exponential back‑off: 500 ms, 1 s, 2 s, … up to 60 s.
51
58
  const delay = Math.min(60000, 500 * 2 ** retryCountRef.current);
52
59
  retryCountRef.current += 1;
@@ -84,3 +91,22 @@ function useDocumentChanged({ document, onChanged }) {
84
91
  }
85
92
  }, [document]);
86
93
  }
94
+ function useRoomParticipants(room) {
95
+ const [participants, setParticipants] = (0, react_1.useState)(() => []);
96
+ (0, react_1.useEffect)(() => {
97
+ if (!room || !room.messaging) {
98
+ return;
99
+ }
100
+ const updateParticipants = () => setParticipants(room.messaging.remoteParticipants);
101
+ room.messaging.on('participant_added', updateParticipants);
102
+ room.messaging.on('participant_removed', updateParticipants);
103
+ room.messaging.on('messaging_enabled', updateParticipants);
104
+ updateParticipants();
105
+ return () => {
106
+ room.messaging.off('participant_added', updateParticipants);
107
+ room.messaging.off('participant_removed', updateParticipants);
108
+ room.messaging.on('messaging_enabled', updateParticipants);
109
+ };
110
+ }, [room]);
111
+ return participants;
112
+ }
@@ -3,4 +3,4 @@ export * from './client-toolkits';
3
3
  export * from './document-connection-scope';
4
4
  export * from './file-upload';
5
5
  export * from './room-connection-scope';
6
- export * from './wail-for-participant';
6
+ export * from './subscribe-async-gen';
package/dist/cjs/index.js CHANGED
@@ -19,4 +19,4 @@ __exportStar(require("./client-toolkits"), exports);
19
19
  __exportStar(require("./document-connection-scope"), exports);
20
20
  __exportStar(require("./file-upload"), exports);
21
21
  __exportStar(require("./room-connection-scope"), exports);
22
- __exportStar(require("./wail-for-participant"), exports);
22
+ __exportStar(require("./subscribe-async-gen"), exports);
@@ -100,8 +100,8 @@ function useRoomConnection(props) {
100
100
  }
101
101
  function useRoomIndicators({ room, path }) {
102
102
  const typingMap = (0, react_1.useRef)({});
103
- const thinkingSet = (0, react_1.useRef)(new Set());
104
- const [typing, setState] = (0, react_1.useState)(false);
103
+ const thinkingMap = (0, react_1.useRef)({});
104
+ const [typing, setTyping] = (0, react_1.useState)(false);
105
105
  const [thinking, setThinking] = (0, react_1.useState)(false);
106
106
  (0, react_1.useEffect)(() => {
107
107
  if (!room)
@@ -124,20 +124,23 @@ function useRoomIndicators({ room, path }) {
124
124
  // Set a new timer to remove typing after 1 second
125
125
  typingMap.current[message.fromParticipantId] = setTimeout(() => {
126
126
  delete typingMap.current[message.fromParticipantId];
127
- setState(Object.keys(typingMap.current).length > 0);
127
+ setTyping(Object.keys(typingMap.current).length > 0);
128
128
  }, 1000);
129
129
  // Update typing state
130
- setState(Object.keys(typingMap.current).length > 0);
130
+ setTyping(Object.keys(typingMap.current).length > 0);
131
131
  }
132
132
  else if (message.type === "thinking") {
133
+ clearTimeout(thinkingMap.current[message.fromParticipantId]);
133
134
  if (message.message.thinking) {
134
- thinkingSet.current.add(message.fromParticipantId);
135
+ thinkingMap.current[message.fromParticipantId] = setTimeout(() => {
136
+ delete thinkingMap.current[message.fromParticipantId];
137
+ setThinking(Object.keys(thinkingMap.current).length > 0);
138
+ }, 5000);
135
139
  }
136
140
  else {
137
- thinkingSet.current.delete(message.fromParticipantId);
141
+ delete thinkingMap.current[message.fromParticipantId];
138
142
  }
139
- // Update thinking state
140
- setThinking(thinkingSet.current.size > 0);
143
+ setThinking(Object.keys(thinkingMap.current).length > 0);
141
144
  }
142
145
  }
143
146
  },
@@ -26,6 +26,8 @@ export interface UseMessageChatResult {
26
26
  attachments: FileUpload[];
27
27
  setAttachments: (attachments: FileUpload[]) => void;
28
28
  schemaFileExists: boolean;
29
+ onlineParticipants: Participant[];
30
+ cancelRequest?: () => void;
29
31
  }
30
32
  export declare function fileToAsyncIterable(file: File): AsyncIterable<Uint8Array>;
31
33
  export declare function useChat({ room, path, participants, participantNames, initialMessage, includeLocalParticipant }: UseMessageChatProps): UseMessageChatResult;
package/dist/esm/chat.js CHANGED
@@ -1,6 +1,8 @@
1
- import { useCallback, useState, useEffect } from "react";
1
+ import { useCallback, useState, useMemo } from "react";
2
+ import { RemoteParticipant } from "@meshagent/meshagent";
2
3
  import { MeshagentFileUpload } from "./file-upload";
3
- import { useDocumentConnection, useDocumentChanged, } from "./document-connection-scope";
4
+ import { useRoomParticipants } from "./document-connection-scope";
5
+ import { useDocumentConnection, useDocumentChanged } from "./document-connection-scope";
4
6
  export class ChatMessage {
5
7
  constructor({ id, text, attachments }) {
6
8
  Object.defineProperty(this, "id", {
@@ -76,12 +78,9 @@ function* getParticipantNames(document) {
76
78
  }
77
79
  }
78
80
  }
79
- function* getOnlineParticipants(room, document) {
80
- for (const participantName of getParticipantNames(document)) {
81
- if (participantName === room.localParticipant?.getAttribute("name")) {
82
- yield room.localParticipant;
83
- }
84
- for (const remoteParticipant of room.messaging.remoteParticipants) {
81
+ function* getOnlineParticipants(roomParticipants, participantNames) {
82
+ for (const participantName of participantNames) {
83
+ for (const remoteParticipant of roomParticipants) {
85
84
  if (remoteParticipant.getAttribute("name") === participantName) {
86
85
  yield remoteParticipant;
87
86
  }
@@ -117,17 +116,35 @@ export function fileToAsyncIterable(file) {
117
116
  return (hasNativeStream ? nativeStream : sliceStream)();
118
117
  }
119
118
  export function useChat({ room, path, participants, participantNames, initialMessage, includeLocalParticipant }) {
120
- const { document, schemaFileExists } = useDocumentConnection({ room, path });
119
+ const { document, schemaFileExists } = useDocumentConnection({
120
+ room,
121
+ path,
122
+ onConnected: (doc) => {
123
+ ensureParticipants(doc, room.localParticipant, includeLocalParticipant ?? true, participants ?? [], participantNames ?? []);
124
+ if (initialMessage) {
125
+ sendMessage(initialMessage);
126
+ }
127
+ },
128
+ onError: (error) => {
129
+ console.error("Failed to connect to document:", error);
130
+ }
131
+ });
121
132
  const [messages, setMessages] = useState(() => document ? mapMessages(document) : []);
122
133
  const [attachments, setAttachments] = useState([]);
134
+ const [documentMembers, setDocumentMembers] = useState(() => document ? getParticipantNames(document) : []);
123
135
  useDocumentChanged({
124
136
  document,
125
- onChanged: (doc) => setMessages(mapMessages(doc)),
137
+ onChanged: (doc) => {
138
+ setMessages(mapMessages(doc));
139
+ setDocumentMembers(getParticipantNames(doc));
140
+ },
126
141
  });
127
142
  const selectAttachments = useCallback((files) => {
128
143
  const attachmentsToUpload = files.map((file) => new MeshagentFileUpload(room, `uploaded-files/${file.name}`, fileToAsyncIterable(file), file.size));
129
144
  setAttachments(attachmentsToUpload);
130
145
  }, [room]);
146
+ const roomParticipants = useRoomParticipants(room);
147
+ const onlineParticipants = useMemo(() => Array.from(getOnlineParticipants(roomParticipants, documentMembers)), [roomParticipants, documentMembers]);
131
148
  const sendMessage = useCallback((message) => {
132
149
  const children = document?.root.getChildren() || [];
133
150
  const thread = children.find((c) => c.tagName === "messages");
@@ -144,7 +161,7 @@ export function useChat({ room, path, participants, participantNames, initialMes
144
161
  for (const path of message.attachments) {
145
162
  m.createChildElement("file", { path });
146
163
  }
147
- for (const participant of getOnlineParticipants(room, document)) {
164
+ for (const participant of onlineParticipants) {
148
165
  room.messaging.sendMessage({
149
166
  to: participant,
150
167
  type: "chat",
@@ -155,15 +172,18 @@ export function useChat({ room, path, participants, participantNames, initialMes
155
172
  },
156
173
  });
157
174
  }
158
- }, [document, attachments]);
159
- useEffect(() => {
160
- if (document) {
161
- ensureParticipants(document, room.localParticipant, includeLocalParticipant ?? true, participants ?? [], participantNames ?? []);
162
- if (initialMessage) {
163
- sendMessage(initialMessage);
175
+ }, [document, path, attachments, onlineParticipants, room]);
176
+ const cancelRequest = useCallback(() => {
177
+ for (const participant of onlineParticipants) {
178
+ if (participant instanceof RemoteParticipant && participant.role === 'agent') {
179
+ room.messaging.sendMessage({
180
+ to: participant,
181
+ type: "cancel",
182
+ message: { path },
183
+ });
164
184
  }
165
185
  }
166
- }, [document]);
186
+ }, [room, path, onlineParticipants]);
167
187
  return {
168
188
  messages,
169
189
  sendMessage,
@@ -171,5 +191,7 @@ export function useChat({ room, path, participants, participantNames, initialMes
171
191
  attachments,
172
192
  setAttachments,
173
193
  schemaFileExists,
194
+ onlineParticipants,
195
+ cancelRequest,
174
196
  };
175
197
  }
@@ -3,5 +3,5 @@ interface ClientToolkitsProps {
3
3
  toolkits: RemoteToolkit[];
4
4
  public?: boolean;
5
5
  }
6
- export declare const useClientToolkits: ({ toolkits, public: isPublic, }: ClientToolkitsProps) => void;
6
+ export declare const useClientToolkits: ({ toolkits, public: isPublic }: ClientToolkitsProps) => void;
7
7
  export {};
@@ -1,7 +1,7 @@
1
1
  import { useEffect } from 'react';
2
- export const useClientToolkits = ({ toolkits, public: isPublic = false, }) => {
2
+ export const useClientToolkits = ({ toolkits, public: isPublic = false }) => {
3
3
  useEffect(() => {
4
4
  toolkits.forEach(toolkit => toolkit.start({ public_: isPublic }));
5
5
  return () => toolkits.forEach(toolkit => toolkit.stop());
6
- }, []);
6
+ }, [toolkits, isPublic]);
7
7
  };
@@ -1,7 +1,9 @@
1
- import { MeshDocument, RoomClient } from '@meshagent/meshagent';
1
+ import { MeshDocument, RoomClient, RemoteParticipant } from '@meshagent/meshagent';
2
2
  export interface UseDocumentConnectionProps {
3
3
  room: RoomClient;
4
4
  path: string;
5
+ onConnected?: (document: MeshDocument) => void;
6
+ onError?: (error: unknown) => void;
5
7
  }
6
8
  export interface UseDocumentConnectionResult {
7
9
  document: MeshDocument | null;
@@ -18,10 +20,11 @@ export interface UseDocumentConnectionResult {
18
20
  * @param room An already‑connected RoomClient.
19
21
  * @param path Path to the document inside the room.
20
22
  */
21
- export declare function useDocumentConnection({ room, path }: UseDocumentConnectionProps): UseDocumentConnectionResult;
23
+ export declare function useDocumentConnection({ room, path, onConnected, onError }: UseDocumentConnectionProps): UseDocumentConnectionResult;
22
24
  type onChangedHandler = (document: MeshDocument) => void;
23
25
  export declare function useDocumentChanged({ document, onChanged }: {
24
26
  document: MeshDocument | null;
25
27
  onChanged: onChangedHandler;
26
28
  }): void;
29
+ export declare function useRoomParticipants(room: RoomClient | null): Iterable<RemoteParticipant>;
27
30
  export {};
@@ -8,7 +8,7 @@ import { useEffect, useRef, useState } from 'react';
8
8
  * @param room An already‑connected RoomClient.
9
9
  * @param path Path to the document inside the room.
10
10
  */
11
- export function useDocumentConnection({ room, path }) {
11
+ export function useDocumentConnection({ room, path, onConnected, onError }) {
12
12
  const [schemaFileExists, setSchemaFileExists] = useState(null);
13
13
  const [document, setDocument] = useState(null);
14
14
  const [error, setError] = useState(null);
@@ -37,12 +37,18 @@ export function useDocumentConnection({ room, path }) {
37
37
  await new Promise(resolve => setTimeout(resolve, 100));
38
38
  setDocument(doc);
39
39
  setError(null);
40
+ if (onConnected) {
41
+ onConnected(doc);
42
+ }
40
43
  }
41
44
  catch (err) {
42
45
  console.error('Failed to open document:', err);
43
46
  if (cancelled)
44
47
  return;
45
48
  setError(err);
49
+ if (onError) {
50
+ onError(err);
51
+ }
46
52
  // Exponential back‑off: 500 ms, 1 s, 2 s, … up to 60 s.
47
53
  const delay = Math.min(60000, 500 * 2 ** retryCountRef.current);
48
54
  retryCountRef.current += 1;
@@ -80,3 +86,22 @@ export function useDocumentChanged({ document, onChanged }) {
80
86
  }
81
87
  }, [document]);
82
88
  }
89
+ export function useRoomParticipants(room) {
90
+ const [participants, setParticipants] = useState(() => []);
91
+ useEffect(() => {
92
+ if (!room || !room.messaging) {
93
+ return;
94
+ }
95
+ const updateParticipants = () => setParticipants(room.messaging.remoteParticipants);
96
+ room.messaging.on('participant_added', updateParticipants);
97
+ room.messaging.on('participant_removed', updateParticipants);
98
+ room.messaging.on('messaging_enabled', updateParticipants);
99
+ updateParticipants();
100
+ return () => {
101
+ room.messaging.off('participant_added', updateParticipants);
102
+ room.messaging.off('participant_removed', updateParticipants);
103
+ room.messaging.on('messaging_enabled', updateParticipants);
104
+ };
105
+ }, [room]);
106
+ return participants;
107
+ }
@@ -3,4 +3,4 @@ export * from './client-toolkits';
3
3
  export * from './document-connection-scope';
4
4
  export * from './file-upload';
5
5
  export * from './room-connection-scope';
6
- export * from './wail-for-participant';
6
+ export * from './subscribe-async-gen';
package/dist/esm/index.js CHANGED
@@ -3,4 +3,4 @@ export * from './client-toolkits';
3
3
  export * from './document-connection-scope';
4
4
  export * from './file-upload';
5
5
  export * from './room-connection-scope';
6
- export * from './wail-for-participant';
6
+ export * from './subscribe-async-gen';
@@ -93,8 +93,8 @@ export function useRoomConnection(props) {
93
93
  }
94
94
  export function useRoomIndicators({ room, path }) {
95
95
  const typingMap = useRef({});
96
- const thinkingSet = useRef(new Set());
97
- const [typing, setState] = useState(false);
96
+ const thinkingMap = useRef({});
97
+ const [typing, setTyping] = useState(false);
98
98
  const [thinking, setThinking] = useState(false);
99
99
  useEffect(() => {
100
100
  if (!room)
@@ -117,20 +117,23 @@ export function useRoomIndicators({ room, path }) {
117
117
  // Set a new timer to remove typing after 1 second
118
118
  typingMap.current[message.fromParticipantId] = setTimeout(() => {
119
119
  delete typingMap.current[message.fromParticipantId];
120
- setState(Object.keys(typingMap.current).length > 0);
120
+ setTyping(Object.keys(typingMap.current).length > 0);
121
121
  }, 1000);
122
122
  // Update typing state
123
- setState(Object.keys(typingMap.current).length > 0);
123
+ setTyping(Object.keys(typingMap.current).length > 0);
124
124
  }
125
125
  else if (message.type === "thinking") {
126
+ clearTimeout(thinkingMap.current[message.fromParticipantId]);
126
127
  if (message.message.thinking) {
127
- thinkingSet.current.add(message.fromParticipantId);
128
+ thinkingMap.current[message.fromParticipantId] = setTimeout(() => {
129
+ delete thinkingMap.current[message.fromParticipantId];
130
+ setThinking(Object.keys(thinkingMap.current).length > 0);
131
+ }, 5000);
128
132
  }
129
133
  else {
130
- thinkingSet.current.delete(message.fromParticipantId);
134
+ delete thinkingMap.current[message.fromParticipantId];
131
135
  }
132
- // Update thinking state
133
- setThinking(thinkingSet.current.size > 0);
136
+ setThinking(Object.keys(thinkingMap.current).length > 0);
134
137
  }
135
138
  }
136
139
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meshagent/meshagent-react",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
4
4
  "description": "Meshagent React Client",
5
5
  "homepage": "https://github.com/meshagent/meshagent-react",
6
6
  "scripts": {
@@ -32,7 +32,7 @@
32
32
  },
33
33
  "dependencies": {
34
34
  "react": "^19.1.0",
35
- "@meshagent/meshagent": "^0.5.6",
35
+ "@meshagent/meshagent": "^0.5.8",
36
36
  "react-dom": "^19.1.0",
37
37
  "typescript": "^5.8.3",
38
38
  "uuid": "^11.1.0"
@@ -1,3 +0,0 @@
1
- import { Participant } from '@meshagent/meshagent';
2
- import { UseRoomConnectionResult } from './room-connection-scope';
3
- export declare function useWaitForAgentParticipant(connection: UseRoomConnectionResult | null): Participant | null;
@@ -1,23 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useWaitForAgentParticipant = useWaitForAgentParticipant;
4
- const react_1 = require("react");
5
- function useWaitForAgentParticipant(connection) {
6
- const [participant, setParticipant] = (0, react_1.useState)(null);
7
- (0, react_1.useEffect)(() => {
8
- if (connection == null || !connection.ready) {
9
- return;
10
- }
11
- function onChange() {
12
- const participants = Array.from(connection.client.messaging.remoteParticipants);
13
- const agentParticipant = participants.find(p => p.role === 'agent');
14
- if (agentParticipant) {
15
- setParticipant(agentParticipant);
16
- }
17
- }
18
- connection.client.messaging.on('change', onChange);
19
- onChange();
20
- return () => connection.client.messaging.off('change', onChange);
21
- }, [connection]);
22
- return participant;
23
- }
@@ -1,3 +0,0 @@
1
- import { Participant } from '@meshagent/meshagent';
2
- import { UseRoomConnectionResult } from './room-connection-scope';
3
- export declare function useWaitForAgentParticipant(connection: UseRoomConnectionResult | null): Participant | null;
@@ -1,20 +0,0 @@
1
- import { useState, useEffect } from 'react';
2
- export function useWaitForAgentParticipant(connection) {
3
- const [participant, setParticipant] = useState(null);
4
- useEffect(() => {
5
- if (connection == null || !connection.ready) {
6
- return;
7
- }
8
- function onChange() {
9
- const participants = Array.from(connection.client.messaging.remoteParticipants);
10
- const agentParticipant = participants.find(p => p.role === 'agent');
11
- if (agentParticipant) {
12
- setParticipant(agentParticipant);
13
- }
14
- }
15
- connection.client.messaging.on('change', onChange);
16
- onChange();
17
- return () => connection.client.messaging.off('change', onChange);
18
- }, [connection]);
19
- return participant;
20
- }