@scalemule/chat 0.0.1

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.
@@ -0,0 +1,108 @@
1
+ 'use strict';
2
+
3
+ var chunkNFOVLPF2_cjs = require('./chunk-NFOVLPF2.cjs');
4
+
5
+ // src/element.ts
6
+ var ScaleMuleChatElement = class extends HTMLElement {
7
+ constructor() {
8
+ super();
9
+ this.client = null;
10
+ this.unsub = null;
11
+ this.shadow = this.attachShadow({ mode: "open" });
12
+ }
13
+ static get observedAttributes() {
14
+ return ["api-key", "conversation-id", "api-base-url", "embed-token"];
15
+ }
16
+ connectedCallback() {
17
+ this.initialize();
18
+ }
19
+ disconnectedCallback() {
20
+ this.cleanup();
21
+ }
22
+ attributeChangedCallback() {
23
+ this.cleanup();
24
+ this.initialize();
25
+ }
26
+ initialize() {
27
+ const apiKey = this.getAttribute("api-key") ?? void 0;
28
+ const conversationId = this.getAttribute("conversation-id");
29
+ const apiBaseUrl = this.getAttribute("api-base-url") ?? void 0;
30
+ const embedToken = this.getAttribute("embed-token") ?? void 0;
31
+ if (!apiKey && !embedToken) return;
32
+ const config = {
33
+ apiKey,
34
+ embedToken,
35
+ apiBaseUrl
36
+ };
37
+ this.client = new chunkNFOVLPF2_cjs.ChatClient(config);
38
+ this.shadow.innerHTML = `
39
+ <style>
40
+ :host { display: block; width: 100%; height: 100%; }
41
+ .chat-container { width: 100%; height: 100%; display: flex; flex-direction: column; font-family: system-ui, sans-serif; }
42
+ .messages { flex: 1; overflow-y: auto; padding: 8px; }
43
+ .message { margin: 4px 0; padding: 6px 10px; background: #f0f0f0; border-radius: 8px; max-width: 80%; }
44
+ .input-area { display: flex; padding: 8px; border-top: 1px solid #e0e0e0; }
45
+ .input-area input { flex: 1; padding: 8px; border: 1px solid #d0d0d0; border-radius: 6px; outline: none; }
46
+ .input-area button { margin-left: 8px; padding: 8px 16px; background: #0066ff; color: white; border: none; border-radius: 6px; cursor: pointer; }
47
+ </style>
48
+ <div class="chat-container">
49
+ <div class="messages" id="messages"></div>
50
+ <div class="input-area">
51
+ <input type="text" placeholder="Type a message..." id="input" />
52
+ <button id="send">Send</button>
53
+ </div>
54
+ </div>
55
+ `;
56
+ const messagesEl = this.shadow.getElementById("messages");
57
+ const inputEl = this.shadow.getElementById("input");
58
+ const sendBtn = this.shadow.getElementById("send");
59
+ this.client.on("message", ({ message }) => {
60
+ this.appendMessage(messagesEl, message);
61
+ this.dispatchEvent(
62
+ new CustomEvent("chat-message", { detail: message, composed: true, bubbles: true })
63
+ );
64
+ });
65
+ const doSend = () => {
66
+ const content = inputEl.value.trim();
67
+ if (!content || !conversationId || !this.client) return;
68
+ this.client.sendMessage(conversationId, { content });
69
+ inputEl.value = "";
70
+ };
71
+ sendBtn.addEventListener("click", doSend);
72
+ inputEl.addEventListener("keydown", (e) => {
73
+ if (e.key === "Enter") doSend();
74
+ });
75
+ const clientAtInit = this.client;
76
+ if (conversationId) {
77
+ this.client.getConversation(conversationId).then(() => {
78
+ if (this.client !== clientAtInit) return;
79
+ this.unsub = this.client.subscribeToConversation(conversationId);
80
+ this.client.connect();
81
+ });
82
+ this.client.getMessages(conversationId).then((result) => {
83
+ if (result.data?.messages) {
84
+ for (const msg of result.data.messages) {
85
+ this.appendMessage(messagesEl, msg);
86
+ }
87
+ }
88
+ });
89
+ }
90
+ }
91
+ appendMessage(container, message) {
92
+ const el = document.createElement("div");
93
+ el.className = "message";
94
+ el.textContent = message.content;
95
+ container.appendChild(el);
96
+ container.scrollTop = container.scrollHeight;
97
+ }
98
+ cleanup() {
99
+ this.unsub?.();
100
+ this.unsub = null;
101
+ this.client?.destroy();
102
+ this.client = null;
103
+ this.shadow.innerHTML = "";
104
+ }
105
+ };
106
+ if (typeof customElements !== "undefined" && !customElements.get("scalemule-chat")) {
107
+ customElements.define("scalemule-chat", ScaleMuleChatElement);
108
+ }
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,106 @@
1
+ import { ChatClient } from './chunk-6QRI4CJZ.js';
2
+
3
+ // src/element.ts
4
+ var ScaleMuleChatElement = class extends HTMLElement {
5
+ constructor() {
6
+ super();
7
+ this.client = null;
8
+ this.unsub = null;
9
+ this.shadow = this.attachShadow({ mode: "open" });
10
+ }
11
+ static get observedAttributes() {
12
+ return ["api-key", "conversation-id", "api-base-url", "embed-token"];
13
+ }
14
+ connectedCallback() {
15
+ this.initialize();
16
+ }
17
+ disconnectedCallback() {
18
+ this.cleanup();
19
+ }
20
+ attributeChangedCallback() {
21
+ this.cleanup();
22
+ this.initialize();
23
+ }
24
+ initialize() {
25
+ const apiKey = this.getAttribute("api-key") ?? void 0;
26
+ const conversationId = this.getAttribute("conversation-id");
27
+ const apiBaseUrl = this.getAttribute("api-base-url") ?? void 0;
28
+ const embedToken = this.getAttribute("embed-token") ?? void 0;
29
+ if (!apiKey && !embedToken) return;
30
+ const config = {
31
+ apiKey,
32
+ embedToken,
33
+ apiBaseUrl
34
+ };
35
+ this.client = new ChatClient(config);
36
+ this.shadow.innerHTML = `
37
+ <style>
38
+ :host { display: block; width: 100%; height: 100%; }
39
+ .chat-container { width: 100%; height: 100%; display: flex; flex-direction: column; font-family: system-ui, sans-serif; }
40
+ .messages { flex: 1; overflow-y: auto; padding: 8px; }
41
+ .message { margin: 4px 0; padding: 6px 10px; background: #f0f0f0; border-radius: 8px; max-width: 80%; }
42
+ .input-area { display: flex; padding: 8px; border-top: 1px solid #e0e0e0; }
43
+ .input-area input { flex: 1; padding: 8px; border: 1px solid #d0d0d0; border-radius: 6px; outline: none; }
44
+ .input-area button { margin-left: 8px; padding: 8px 16px; background: #0066ff; color: white; border: none; border-radius: 6px; cursor: pointer; }
45
+ </style>
46
+ <div class="chat-container">
47
+ <div class="messages" id="messages"></div>
48
+ <div class="input-area">
49
+ <input type="text" placeholder="Type a message..." id="input" />
50
+ <button id="send">Send</button>
51
+ </div>
52
+ </div>
53
+ `;
54
+ const messagesEl = this.shadow.getElementById("messages");
55
+ const inputEl = this.shadow.getElementById("input");
56
+ const sendBtn = this.shadow.getElementById("send");
57
+ this.client.on("message", ({ message }) => {
58
+ this.appendMessage(messagesEl, message);
59
+ this.dispatchEvent(
60
+ new CustomEvent("chat-message", { detail: message, composed: true, bubbles: true })
61
+ );
62
+ });
63
+ const doSend = () => {
64
+ const content = inputEl.value.trim();
65
+ if (!content || !conversationId || !this.client) return;
66
+ this.client.sendMessage(conversationId, { content });
67
+ inputEl.value = "";
68
+ };
69
+ sendBtn.addEventListener("click", doSend);
70
+ inputEl.addEventListener("keydown", (e) => {
71
+ if (e.key === "Enter") doSend();
72
+ });
73
+ const clientAtInit = this.client;
74
+ if (conversationId) {
75
+ this.client.getConversation(conversationId).then(() => {
76
+ if (this.client !== clientAtInit) return;
77
+ this.unsub = this.client.subscribeToConversation(conversationId);
78
+ this.client.connect();
79
+ });
80
+ this.client.getMessages(conversationId).then((result) => {
81
+ if (result.data?.messages) {
82
+ for (const msg of result.data.messages) {
83
+ this.appendMessage(messagesEl, msg);
84
+ }
85
+ }
86
+ });
87
+ }
88
+ }
89
+ appendMessage(container, message) {
90
+ const el = document.createElement("div");
91
+ el.className = "message";
92
+ el.textContent = message.content;
93
+ container.appendChild(el);
94
+ container.scrollTop = container.scrollHeight;
95
+ }
96
+ cleanup() {
97
+ this.unsub?.();
98
+ this.unsub = null;
99
+ this.client?.destroy();
100
+ this.client = null;
101
+ this.shadow.innerHTML = "";
102
+ }
103
+ };
104
+ if (typeof customElements !== "undefined" && !customElements.get("scalemule-chat")) {
105
+ customElements.define("scalemule-chat", ScaleMuleChatElement);
106
+ }
@@ -0,0 +1,46 @@
1
+ 'use strict';
2
+
3
+ // src/iframe.ts
4
+ var ChatIframeController = class {
5
+ constructor(container, config) {
6
+ this.listeners = /* @__PURE__ */ new Map();
7
+ this.handleMessage = (event) => {
8
+ if (event.source !== this.iframe.contentWindow) return;
9
+ const { type, payload } = event.data ?? {};
10
+ if (type && this.listeners.has(type)) {
11
+ for (const cb of this.listeners.get(type)) {
12
+ cb(payload);
13
+ }
14
+ }
15
+ };
16
+ this.iframe = document.createElement("iframe");
17
+ this.iframe.style.width = "100%";
18
+ this.iframe.style.height = "100%";
19
+ this.iframe.style.border = "none";
20
+ const baseUrl = config.apiBaseUrl ?? "https://api.scalemule.com";
21
+ const src = config.iframeSrc ?? `${baseUrl}/v1/chat/embed/${config.conversationId ?? ""}?token=${encodeURIComponent(config.embedToken ?? "")}`;
22
+ this.iframe.src = src;
23
+ container.appendChild(this.iframe);
24
+ window.addEventListener("message", this.handleMessage);
25
+ }
26
+ on(event, callback) {
27
+ if (!this.listeners.has(event)) {
28
+ this.listeners.set(event, /* @__PURE__ */ new Set());
29
+ }
30
+ this.listeners.get(event).add(callback);
31
+ return () => this.listeners.get(event)?.delete(callback);
32
+ }
33
+ sendMessage(content) {
34
+ this.postToIframe("sendMessage", { content });
35
+ }
36
+ destroy() {
37
+ window.removeEventListener("message", this.handleMessage);
38
+ this.iframe.remove();
39
+ this.listeners.clear();
40
+ }
41
+ postToIframe(method, args) {
42
+ this.iframe.contentWindow?.postMessage({ method, args }, "*");
43
+ }
44
+ };
45
+
46
+ exports.ChatIframeController = ChatIframeController;
@@ -0,0 +1,17 @@
1
+ import { c as ChatConfig } from './types-BwgD_Etd.cjs';
2
+
3
+ declare class ChatIframeController {
4
+ private iframe;
5
+ private listeners;
6
+ constructor(container: HTMLElement, config: ChatConfig & {
7
+ conversationId?: string;
8
+ iframeSrc?: string;
9
+ });
10
+ on(event: string, callback: (data: unknown) => void): () => void;
11
+ sendMessage(content: string): void;
12
+ destroy(): void;
13
+ private handleMessage;
14
+ private postToIframe;
15
+ }
16
+
17
+ export { ChatIframeController };
@@ -0,0 +1,17 @@
1
+ import { c as ChatConfig } from './types-BwgD_Etd.js';
2
+
3
+ declare class ChatIframeController {
4
+ private iframe;
5
+ private listeners;
6
+ constructor(container: HTMLElement, config: ChatConfig & {
7
+ conversationId?: string;
8
+ iframeSrc?: string;
9
+ });
10
+ on(event: string, callback: (data: unknown) => void): () => void;
11
+ sendMessage(content: string): void;
12
+ destroy(): void;
13
+ private handleMessage;
14
+ private postToIframe;
15
+ }
16
+
17
+ export { ChatIframeController };
package/dist/iframe.js ADDED
@@ -0,0 +1,44 @@
1
+ // src/iframe.ts
2
+ var ChatIframeController = class {
3
+ constructor(container, config) {
4
+ this.listeners = /* @__PURE__ */ new Map();
5
+ this.handleMessage = (event) => {
6
+ if (event.source !== this.iframe.contentWindow) return;
7
+ const { type, payload } = event.data ?? {};
8
+ if (type && this.listeners.has(type)) {
9
+ for (const cb of this.listeners.get(type)) {
10
+ cb(payload);
11
+ }
12
+ }
13
+ };
14
+ this.iframe = document.createElement("iframe");
15
+ this.iframe.style.width = "100%";
16
+ this.iframe.style.height = "100%";
17
+ this.iframe.style.border = "none";
18
+ const baseUrl = config.apiBaseUrl ?? "https://api.scalemule.com";
19
+ const src = config.iframeSrc ?? `${baseUrl}/v1/chat/embed/${config.conversationId ?? ""}?token=${encodeURIComponent(config.embedToken ?? "")}`;
20
+ this.iframe.src = src;
21
+ container.appendChild(this.iframe);
22
+ window.addEventListener("message", this.handleMessage);
23
+ }
24
+ on(event, callback) {
25
+ if (!this.listeners.has(event)) {
26
+ this.listeners.set(event, /* @__PURE__ */ new Set());
27
+ }
28
+ this.listeners.get(event).add(callback);
29
+ return () => this.listeners.get(event)?.delete(callback);
30
+ }
31
+ sendMessage(content) {
32
+ this.postToIframe("sendMessage", { content });
33
+ }
34
+ destroy() {
35
+ window.removeEventListener("message", this.handleMessage);
36
+ this.iframe.remove();
37
+ this.listeners.clear();
38
+ }
39
+ postToIframe(method, args) {
40
+ this.iframe.contentWindow?.postMessage({ method, args }, "*");
41
+ }
42
+ };
43
+
44
+ export { ChatIframeController };
package/dist/index.cjs ADDED
@@ -0,0 +1,12 @@
1
+ 'use strict';
2
+
3
+ var chunkNFOVLPF2_cjs = require('./chunk-NFOVLPF2.cjs');
4
+
5
+ // src/version.ts
6
+ var CHAT_VERSION = "0.0.1";
7
+
8
+ Object.defineProperty(exports, "ChatClient", {
9
+ enumerable: true,
10
+ get: function () { return chunkNFOVLPF2_cjs.ChatClient; }
11
+ });
12
+ exports.CHAT_VERSION = CHAT_VERSION;
@@ -0,0 +1,6 @@
1
+ export { C as ChatClient } from './ChatClient-CXPywp1w.cjs';
2
+ export { A as ApiError, a as ApiResponse, b as Attachment, C as ChannelSettings, c as ChatConfig, d as ChatEventMap, e as ChatMessage, f as ChatReaction, g as ConnectionStatus, h as Conversation, i as CreateConversationOptions, G as GetMessagesOptions, L as ListConversationsOptions, M as MessagesResponse, P as Participant, j as PresenceMember, R as ReadStatus, S as SendMessageOptions } from './types-BwgD_Etd.cjs';
3
+
4
+ declare const CHAT_VERSION = "0.0.1";
5
+
6
+ export { CHAT_VERSION };
@@ -0,0 +1,6 @@
1
+ export { C as ChatClient } from './ChatClient-DCid_mmU.js';
2
+ export { A as ApiError, a as ApiResponse, b as Attachment, C as ChannelSettings, c as ChatConfig, d as ChatEventMap, e as ChatMessage, f as ChatReaction, g as ConnectionStatus, h as Conversation, i as CreateConversationOptions, G as GetMessagesOptions, L as ListConversationsOptions, M as MessagesResponse, P as Participant, j as PresenceMember, R as ReadStatus, S as SendMessageOptions } from './types-BwgD_Etd.js';
3
+
4
+ declare const CHAT_VERSION = "0.0.1";
5
+
6
+ export { CHAT_VERSION };
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { ChatClient } from './chunk-6QRI4CJZ.js';
2
+
3
+ // src/version.ts
4
+ var CHAT_VERSION = "0.0.1";
5
+
6
+ export { CHAT_VERSION };
package/dist/react.cjs ADDED
@@ -0,0 +1,242 @@
1
+ 'use strict';
2
+
3
+ var chunkNFOVLPF2_cjs = require('./chunk-NFOVLPF2.cjs');
4
+ var react = require('react');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+
7
+ var ChatContext = react.createContext(null);
8
+ function useChatContext() {
9
+ const ctx = react.useContext(ChatContext);
10
+ if (!ctx) throw new Error("useChatContext must be used within a ChatProvider");
11
+ return ctx;
12
+ }
13
+ function ChatProvider({ config, children }) {
14
+ const [client] = react.useState(() => new chunkNFOVLPF2_cjs.ChatClient(config));
15
+ react.useEffect(() => {
16
+ return () => {
17
+ client.destroy();
18
+ };
19
+ }, [client]);
20
+ return /* @__PURE__ */ jsxRuntime.jsx(ChatContext.Provider, { value: { client }, children });
21
+ }
22
+ function useConnection() {
23
+ const { client } = useChatContext();
24
+ const [status, setStatus] = react.useState(client.status);
25
+ react.useEffect(() => {
26
+ return client.on("connected", () => setStatus("connected"));
27
+ }, [client]);
28
+ react.useEffect(() => {
29
+ return client.on("disconnected", () => setStatus("disconnected"));
30
+ }, [client]);
31
+ react.useEffect(() => {
32
+ return client.on("reconnecting", () => setStatus("reconnecting"));
33
+ }, [client]);
34
+ return {
35
+ status,
36
+ connect: () => client.connect(),
37
+ disconnect: () => client.disconnect()
38
+ };
39
+ }
40
+ function useChat(conversationId) {
41
+ const { client } = useChatContext();
42
+ const [messages, setMessages] = react.useState([]);
43
+ const [isLoading, setIsLoading] = react.useState(false);
44
+ const [error, setError] = react.useState(null);
45
+ const [hasMore, setHasMore] = react.useState(false);
46
+ react.useEffect(() => {
47
+ if (!conversationId) return;
48
+ setIsLoading(true);
49
+ setError(null);
50
+ let cancelled = false;
51
+ let unsub;
52
+ (async () => {
53
+ await client.getConversation(conversationId);
54
+ if (cancelled) return;
55
+ const result = await client.getMessages(conversationId);
56
+ if (cancelled) return;
57
+ if (result.data?.messages) {
58
+ setMessages(result.data.messages);
59
+ setHasMore(result.data.has_more ?? false);
60
+ } else if (result.error) {
61
+ setError(result.error.message);
62
+ }
63
+ setIsLoading(false);
64
+ unsub = client.subscribeToConversation(conversationId);
65
+ client.connect();
66
+ })();
67
+ return () => {
68
+ cancelled = true;
69
+ unsub?.();
70
+ };
71
+ }, [client, conversationId]);
72
+ react.useEffect(() => {
73
+ if (!conversationId) return;
74
+ return client.on("message", ({ message, conversationId: convId }) => {
75
+ if (convId === conversationId) {
76
+ setMessages((prev) => {
77
+ if (prev.some((m) => m.id === message.id)) return prev;
78
+ return [...prev, message];
79
+ });
80
+ }
81
+ });
82
+ }, [client, conversationId]);
83
+ react.useEffect(() => {
84
+ if (!conversationId) return;
85
+ return client.on("message:updated", ({ message, conversationId: convId }) => {
86
+ if (convId === conversationId) {
87
+ setMessages((prev) => prev.map((m) => m.id === message.id ? message : m));
88
+ }
89
+ });
90
+ }, [client, conversationId]);
91
+ react.useEffect(() => {
92
+ if (!conversationId) return;
93
+ return client.on("message:deleted", ({ messageId, conversationId: convId }) => {
94
+ if (convId === conversationId) {
95
+ setMessages((prev) => prev.filter((m) => m.id !== messageId));
96
+ }
97
+ });
98
+ }, [client, conversationId]);
99
+ const sendMessage = react.useCallback(
100
+ async (content, options) => {
101
+ if (!conversationId) return;
102
+ return client.sendMessage(conversationId, { content, ...options });
103
+ },
104
+ [client, conversationId]
105
+ );
106
+ const loadMore = react.useCallback(async () => {
107
+ if (!conversationId || !messages.length) return;
108
+ const oldestId = messages[0]?.id;
109
+ const result = await client.getMessages(conversationId, { before: oldestId });
110
+ if (result.data?.messages) {
111
+ setMessages((prev) => [...result.data.messages, ...prev]);
112
+ setHasMore(result.data.has_more ?? false);
113
+ }
114
+ }, [client, conversationId, messages]);
115
+ const markRead = react.useCallback(async () => {
116
+ if (!conversationId) return;
117
+ await client.markRead(conversationId);
118
+ }, [client, conversationId]);
119
+ return {
120
+ messages,
121
+ isLoading,
122
+ error,
123
+ hasMore,
124
+ sendMessage,
125
+ loadMore,
126
+ markRead
127
+ };
128
+ }
129
+ function usePresence(conversationId) {
130
+ const { client } = useChatContext();
131
+ const [members, setMembers] = react.useState([]);
132
+ react.useEffect(() => {
133
+ if (!conversationId) return;
134
+ let cancelled = false;
135
+ (async () => {
136
+ await client.getConversation(conversationId);
137
+ if (cancelled) return;
138
+ client.joinPresence(conversationId);
139
+ })();
140
+ const unsubState = client.on("presence:state", ({ conversationId: convId, members: m }) => {
141
+ if (convId === conversationId) {
142
+ setMembers(
143
+ m.map((p) => ({
144
+ userId: p.user_id,
145
+ status: p.status ?? "online",
146
+ userData: p.user_data
147
+ }))
148
+ );
149
+ }
150
+ });
151
+ const unsubJoin = client.on("presence:join", ({ conversationId: convId, userId, userData }) => {
152
+ if (convId === conversationId) {
153
+ setMembers((prev) => {
154
+ if (prev.some((m) => m.userId === userId)) return prev;
155
+ return [...prev, { userId, status: "online", userData }];
156
+ });
157
+ }
158
+ });
159
+ const unsubLeave = client.on("presence:leave", ({ conversationId: convId, userId }) => {
160
+ if (convId === conversationId) {
161
+ setMembers((prev) => prev.filter((m) => m.userId !== userId));
162
+ }
163
+ });
164
+ const unsubUpdate = client.on(
165
+ "presence:update",
166
+ ({ conversationId: convId, userId, status, userData }) => {
167
+ if (convId === conversationId) {
168
+ setMembers(
169
+ (prev) => prev.map(
170
+ (m) => m.userId === userId ? { ...m, status, userData: userData ?? m.userData } : m
171
+ )
172
+ );
173
+ }
174
+ }
175
+ );
176
+ return () => {
177
+ cancelled = true;
178
+ client.leavePresence(conversationId);
179
+ unsubState();
180
+ unsubJoin();
181
+ unsubLeave();
182
+ unsubUpdate();
183
+ };
184
+ }, [client, conversationId]);
185
+ return { members };
186
+ }
187
+ function useTyping(conversationId) {
188
+ const { client } = useChatContext();
189
+ const [typingUsers, setTypingUsers] = react.useState([]);
190
+ const typingTimers = react.useRef(/* @__PURE__ */ new Map());
191
+ react.useEffect(() => {
192
+ if (!conversationId) return;
193
+ const unsubTyping = client.on("typing", ({ conversationId: convId, userId }) => {
194
+ if (convId !== conversationId) return;
195
+ setTypingUsers((prev) => prev.includes(userId) ? prev : [...prev, userId]);
196
+ const existing = typingTimers.current.get(userId);
197
+ if (existing) clearTimeout(existing);
198
+ typingTimers.current.set(
199
+ userId,
200
+ setTimeout(() => {
201
+ setTypingUsers((prev) => prev.filter((id) => id !== userId));
202
+ typingTimers.current.delete(userId);
203
+ }, 3e3)
204
+ );
205
+ });
206
+ const unsubStop = client.on("typing:stop", ({ conversationId: convId, userId }) => {
207
+ if (convId !== conversationId) return;
208
+ setTypingUsers((prev) => prev.filter((id) => id !== userId));
209
+ const timer = typingTimers.current.get(userId);
210
+ if (timer) {
211
+ clearTimeout(timer);
212
+ typingTimers.current.delete(userId);
213
+ }
214
+ });
215
+ return () => {
216
+ unsubTyping();
217
+ unsubStop();
218
+ for (const timer of typingTimers.current.values()) {
219
+ clearTimeout(timer);
220
+ }
221
+ typingTimers.current.clear();
222
+ };
223
+ }, [client, conversationId]);
224
+ const sendTyping = react.useCallback(
225
+ (isTyping = true) => {
226
+ if (!conversationId) return;
227
+ client.sendTyping(conversationId, isTyping);
228
+ },
229
+ [client, conversationId]
230
+ );
231
+ return { typingUsers, sendTyping };
232
+ }
233
+
234
+ Object.defineProperty(exports, "ChatClient", {
235
+ enumerable: true,
236
+ get: function () { return chunkNFOVLPF2_cjs.ChatClient; }
237
+ });
238
+ exports.ChatProvider = ChatProvider;
239
+ exports.useChat = useChat;
240
+ exports.useConnection = useConnection;
241
+ exports.usePresence = usePresence;
242
+ exports.useTyping = useTyping;
@@ -0,0 +1,37 @@
1
+ import React, { ReactNode } from 'react';
2
+ import { c as ChatConfig, e as ChatMessage, S as SendMessageOptions, a as ApiResponse, g as ConnectionStatus } from './types-BwgD_Etd.cjs';
3
+ export { h as Conversation, G as GetMessagesOptions, M as MessagesResponse } from './types-BwgD_Etd.cjs';
4
+ export { C as ChatClient } from './ChatClient-CXPywp1w.cjs';
5
+
6
+ interface ChatProviderProps {
7
+ config: ChatConfig;
8
+ children: ReactNode;
9
+ }
10
+ declare function ChatProvider({ config, children }: ChatProviderProps): React.JSX.Element;
11
+ declare function useConnection(): {
12
+ status: ConnectionStatus;
13
+ connect: () => void;
14
+ disconnect: () => void;
15
+ };
16
+ declare function useChat(conversationId?: string): {
17
+ messages: ChatMessage[];
18
+ isLoading: boolean;
19
+ error: string | null;
20
+ hasMore: boolean;
21
+ sendMessage: (content: string, options?: Partial<SendMessageOptions>) => Promise<ApiResponse<ChatMessage> | undefined>;
22
+ loadMore: () => Promise<void>;
23
+ markRead: () => Promise<void>;
24
+ };
25
+ declare function usePresence(conversationId?: string): {
26
+ members: {
27
+ userId: string;
28
+ status: string;
29
+ userData?: unknown;
30
+ }[];
31
+ };
32
+ declare function useTyping(conversationId?: string): {
33
+ typingUsers: string[];
34
+ sendTyping: (isTyping?: boolean) => void;
35
+ };
36
+
37
+ export { ApiResponse, ChatConfig, ChatMessage, ChatProvider, ConnectionStatus, SendMessageOptions, useChat, useConnection, usePresence, useTyping };