@meshagent/meshagent-react 0.5.7 → 0.5.9
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 +6 -0
- package/dist/cjs/chat.d.ts +2 -0
- package/dist/cjs/chat.js +39 -17
- package/dist/cjs/client-toolkits.d.ts +1 -1
- package/dist/cjs/client-toolkits.js +2 -2
- package/dist/cjs/document-connection-scope.d.ts +5 -2
- package/dist/cjs/document-connection-scope.js +27 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/room-connection-scope.js +11 -8
- package/dist/esm/chat.d.ts +2 -0
- package/dist/esm/chat.js +40 -18
- package/dist/esm/client-toolkits.d.ts +1 -1
- package/dist/esm/client-toolkits.js +2 -2
- package/dist/esm/document-connection-scope.d.ts +5 -2
- package/dist/esm/document-connection-scope.js +26 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/room-connection-scope.js +11 -8
- package/package.json +2 -2
- package/dist/cjs/wail-for-participant.d.ts +0 -3
- package/dist/cjs/wail-for-participant.js +0 -23
- package/dist/esm/wail-for-participant.d.ts +0 -3
- package/dist/esm/wail-for-participant.js +0 -20
package/CHANGELOG.md
CHANGED
package/dist/cjs/chat.d.ts
CHANGED
|
@@ -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(
|
|
86
|
-
for (const participantName of
|
|
87
|
-
|
|
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,
|
|
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,
|
|
140
|
+
const [documentMembers, setDocumentMembers] = (0, react_1.useState)(() => document ? getParticipantNames(document) : []);
|
|
141
|
+
(0, document_connection_scope_2.useDocumentChanged)({
|
|
130
142
|
document,
|
|
131
|
-
onChanged: (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
|
|
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.
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
}, [
|
|
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
|
|
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
|
+
}
|
package/dist/cjs/index.d.ts
CHANGED
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("./
|
|
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
|
|
104
|
-
const [typing,
|
|
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
|
-
|
|
127
|
+
setTyping(Object.keys(typingMap.current).length > 0);
|
|
128
128
|
}, 1000);
|
|
129
129
|
// Update typing state
|
|
130
|
-
|
|
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
|
-
|
|
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
|
-
|
|
141
|
+
delete thinkingMap.current[message.fromParticipantId];
|
|
138
142
|
}
|
|
139
|
-
|
|
140
|
-
setThinking(thinkingSet.current.size > 0);
|
|
143
|
+
setThinking(Object.keys(thinkingMap.current).length > 0);
|
|
141
144
|
}
|
|
142
145
|
}
|
|
143
146
|
},
|
package/dist/esm/chat.d.ts
CHANGED
|
@@ -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,
|
|
1
|
+
import { useCallback, useState, useMemo } from "react";
|
|
2
|
+
import { RemoteParticipant } from "@meshagent/meshagent";
|
|
2
3
|
import { MeshagentFileUpload } from "./file-upload";
|
|
3
|
-
import {
|
|
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(
|
|
80
|
-
for (const participantName of
|
|
81
|
-
|
|
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({
|
|
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) =>
|
|
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
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
}, [
|
|
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
|
|
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
|
+
}
|
package/dist/esm/index.d.ts
CHANGED
package/dist/esm/index.js
CHANGED
|
@@ -93,8 +93,8 @@ export function useRoomConnection(props) {
|
|
|
93
93
|
}
|
|
94
94
|
export function useRoomIndicators({ room, path }) {
|
|
95
95
|
const typingMap = useRef({});
|
|
96
|
-
const
|
|
97
|
-
const [typing,
|
|
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
|
-
|
|
120
|
+
setTyping(Object.keys(typingMap.current).length > 0);
|
|
121
121
|
}, 1000);
|
|
122
122
|
// Update typing state
|
|
123
|
-
|
|
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
|
-
|
|
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
|
-
|
|
134
|
+
delete thinkingMap.current[message.fromParticipantId];
|
|
131
135
|
}
|
|
132
|
-
|
|
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.
|
|
3
|
+
"version": "0.5.9",
|
|
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.
|
|
35
|
+
"@meshagent/meshagent": "^0.5.9",
|
|
36
36
|
"react-dom": "^19.1.0",
|
|
37
37
|
"typescript": "^5.8.3",
|
|
38
38
|
"uuid": "^11.1.0"
|
|
@@ -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,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
|
-
}
|