@cstar.help/react 0.1.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.
- package/dist/index.cjs +494 -0
- package/dist/index.d.cts +218 -0
- package/dist/index.d.ts +218 -0
- package/dist/index.js +454 -0
- package/package.json +63 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
CStarChatContext: () => CStarChatContext,
|
|
24
|
+
CStarChatProvider: () => CStarChatProvider,
|
|
25
|
+
CStarLibraryContext: () => CStarLibraryContext,
|
|
26
|
+
CStarLibraryProvider: () => CStarLibraryProvider,
|
|
27
|
+
useArticle: () => useArticle,
|
|
28
|
+
useArticleSearch: () => useArticleSearch,
|
|
29
|
+
useArticles: () => useArticles,
|
|
30
|
+
useCategories: () => useCategories,
|
|
31
|
+
useChat: () => useChat,
|
|
32
|
+
useChatClient: () => useChatClient,
|
|
33
|
+
useConversations: () => useConversations,
|
|
34
|
+
useLibraryClient: () => useLibraryClient,
|
|
35
|
+
useMessages: () => useMessages,
|
|
36
|
+
useTyping: () => useTyping
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(src_exports);
|
|
39
|
+
|
|
40
|
+
// src/context.tsx
|
|
41
|
+
var import_react = require("react");
|
|
42
|
+
var CStarChatContext = (0, import_react.createContext)(null);
|
|
43
|
+
var CStarLibraryContext = (0, import_react.createContext)(null);
|
|
44
|
+
function useChatClient() {
|
|
45
|
+
const client = (0, import_react.useContext)(CStarChatContext);
|
|
46
|
+
if (!client) {
|
|
47
|
+
throw new Error("useChatClient must be used within a <CStarChatProvider>");
|
|
48
|
+
}
|
|
49
|
+
return client;
|
|
50
|
+
}
|
|
51
|
+
function useLibraryClient() {
|
|
52
|
+
const client = (0, import_react.useContext)(CStarLibraryContext);
|
|
53
|
+
if (!client) {
|
|
54
|
+
throw new Error("useLibraryClient must be used within a <CStarLibraryProvider>");
|
|
55
|
+
}
|
|
56
|
+
return client;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// src/chat/CStarChatProvider.tsx
|
|
60
|
+
var import_react2 = require("react");
|
|
61
|
+
var import_chat = require("@cstar.help/js/chat");
|
|
62
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
63
|
+
function CStarChatProvider({ children, ...config }) {
|
|
64
|
+
const clientRef = (0, import_react2.useRef)(null);
|
|
65
|
+
if (!clientRef.current) {
|
|
66
|
+
clientRef.current = new import_chat.ChatClient(config);
|
|
67
|
+
}
|
|
68
|
+
(0, import_react2.useEffect)(() => {
|
|
69
|
+
return () => {
|
|
70
|
+
clientRef.current?.disconnect();
|
|
71
|
+
clientRef.current = null;
|
|
72
|
+
};
|
|
73
|
+
}, []);
|
|
74
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CStarChatContext.Provider, { value: clientRef.current, children });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// src/chat/useChat.ts
|
|
78
|
+
var import_react3 = require("react");
|
|
79
|
+
function useChat() {
|
|
80
|
+
const client = useChatClient();
|
|
81
|
+
const [isIdentified, setIsIdentified] = (0, import_react3.useState)(false);
|
|
82
|
+
const [isRealtimeReady, setIsRealtimeReady] = (0, import_react3.useState)(false);
|
|
83
|
+
const [error, setError] = (0, import_react3.useState)(null);
|
|
84
|
+
const identify = (0, import_react3.useCallback)(
|
|
85
|
+
async (customer, signature) => {
|
|
86
|
+
setError(null);
|
|
87
|
+
try {
|
|
88
|
+
const result = await client.identify(customer, signature);
|
|
89
|
+
setIsIdentified(true);
|
|
90
|
+
client.waitForRealtime().then(() => {
|
|
91
|
+
setIsRealtimeReady(true);
|
|
92
|
+
}).catch(() => {
|
|
93
|
+
});
|
|
94
|
+
return result;
|
|
95
|
+
} catch (err) {
|
|
96
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
97
|
+
setError(e);
|
|
98
|
+
throw e;
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
[client]
|
|
102
|
+
);
|
|
103
|
+
const disconnect = (0, import_react3.useCallback)(() => {
|
|
104
|
+
client.disconnect();
|
|
105
|
+
setIsIdentified(false);
|
|
106
|
+
setIsRealtimeReady(false);
|
|
107
|
+
setError(null);
|
|
108
|
+
}, [client]);
|
|
109
|
+
return { identify, disconnect, isIdentified, isRealtimeReady, error };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/chat/useConversations.ts
|
|
113
|
+
var import_react4 = require("react");
|
|
114
|
+
function useConversations() {
|
|
115
|
+
const client = useChatClient();
|
|
116
|
+
const [conversations, setConversations] = (0, import_react4.useState)([]);
|
|
117
|
+
const [isLoading, setIsLoading] = (0, import_react4.useState)(true);
|
|
118
|
+
const [error, setError] = (0, import_react4.useState)(null);
|
|
119
|
+
const [hasMore, setHasMore] = (0, import_react4.useState)(false);
|
|
120
|
+
const mountedRef = (0, import_react4.useRef)(true);
|
|
121
|
+
const refresh = (0, import_react4.useCallback)(async () => {
|
|
122
|
+
setIsLoading(true);
|
|
123
|
+
setError(null);
|
|
124
|
+
try {
|
|
125
|
+
const result = await client.conversations.list();
|
|
126
|
+
if (mountedRef.current) {
|
|
127
|
+
setConversations(result.conversations);
|
|
128
|
+
setHasMore(result.hasMore);
|
|
129
|
+
}
|
|
130
|
+
} catch (err) {
|
|
131
|
+
if (mountedRef.current) {
|
|
132
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
133
|
+
}
|
|
134
|
+
} finally {
|
|
135
|
+
if (mountedRef.current) {
|
|
136
|
+
setIsLoading(false);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}, [client]);
|
|
140
|
+
(0, import_react4.useEffect)(() => {
|
|
141
|
+
mountedRef.current = true;
|
|
142
|
+
refresh();
|
|
143
|
+
return () => {
|
|
144
|
+
mountedRef.current = false;
|
|
145
|
+
};
|
|
146
|
+
}, [refresh]);
|
|
147
|
+
const create = (0, import_react4.useCallback)(
|
|
148
|
+
async (params) => {
|
|
149
|
+
const conversation = await client.conversations.create(params);
|
|
150
|
+
if (mountedRef.current) {
|
|
151
|
+
setConversations((prev) => [conversation, ...prev]);
|
|
152
|
+
}
|
|
153
|
+
return conversation;
|
|
154
|
+
},
|
|
155
|
+
[client]
|
|
156
|
+
);
|
|
157
|
+
return { conversations, isLoading, error, hasMore, refresh, create };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// src/chat/useMessages.ts
|
|
161
|
+
var import_react5 = require("react");
|
|
162
|
+
function useMessages(ticketId) {
|
|
163
|
+
const client = useChatClient();
|
|
164
|
+
const [messages, setMessages] = (0, import_react5.useState)([]);
|
|
165
|
+
const [isLoading, setIsLoading] = (0, import_react5.useState)(true);
|
|
166
|
+
const [error, setError] = (0, import_react5.useState)(null);
|
|
167
|
+
const mountedRef = (0, import_react5.useRef)(true);
|
|
168
|
+
const seenIdsRef = (0, import_react5.useRef)(/* @__PURE__ */ new Set());
|
|
169
|
+
const refresh = (0, import_react5.useCallback)(async () => {
|
|
170
|
+
if (!ticketId) return;
|
|
171
|
+
setIsLoading(true);
|
|
172
|
+
setError(null);
|
|
173
|
+
try {
|
|
174
|
+
const msgs = await client.messages.list(ticketId);
|
|
175
|
+
if (mountedRef.current) {
|
|
176
|
+
seenIdsRef.current = new Set(msgs.map((m) => m.id));
|
|
177
|
+
setMessages(msgs);
|
|
178
|
+
}
|
|
179
|
+
} catch (err) {
|
|
180
|
+
if (mountedRef.current) {
|
|
181
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
182
|
+
}
|
|
183
|
+
} finally {
|
|
184
|
+
if (mountedRef.current) {
|
|
185
|
+
setIsLoading(false);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}, [client, ticketId]);
|
|
189
|
+
(0, import_react5.useEffect)(() => {
|
|
190
|
+
mountedRef.current = true;
|
|
191
|
+
if (!ticketId) {
|
|
192
|
+
setMessages([]);
|
|
193
|
+
setIsLoading(false);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
refresh();
|
|
197
|
+
const unsubscribe = client.onMessage(ticketId, (msg) => {
|
|
198
|
+
if (mountedRef.current && !seenIdsRef.current.has(msg.id)) {
|
|
199
|
+
seenIdsRef.current.add(msg.id);
|
|
200
|
+
setMessages((prev) => {
|
|
201
|
+
const optimisticIdx = prev.findIndex(
|
|
202
|
+
(m) => m.id.startsWith("optimistic-") && m.content === msg.content
|
|
203
|
+
);
|
|
204
|
+
if (optimisticIdx >= 0) {
|
|
205
|
+
const next = [...prev];
|
|
206
|
+
next[optimisticIdx] = msg;
|
|
207
|
+
return next;
|
|
208
|
+
}
|
|
209
|
+
return [...prev, msg];
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
return () => {
|
|
214
|
+
mountedRef.current = false;
|
|
215
|
+
unsubscribe();
|
|
216
|
+
};
|
|
217
|
+
}, [client, ticketId, refresh]);
|
|
218
|
+
const send = (0, import_react5.useCallback)(
|
|
219
|
+
async (content) => {
|
|
220
|
+
if (!ticketId) throw new Error("No conversation selected");
|
|
221
|
+
const optimistic = {
|
|
222
|
+
id: `optimistic-${Date.now()}`,
|
|
223
|
+
content,
|
|
224
|
+
sender: "customer",
|
|
225
|
+
sender_name: "",
|
|
226
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
227
|
+
read_at: null,
|
|
228
|
+
attachments: []
|
|
229
|
+
};
|
|
230
|
+
setMessages((prev) => [...prev, optimistic]);
|
|
231
|
+
try {
|
|
232
|
+
const msg = await client.messages.send(ticketId, content);
|
|
233
|
+
seenIdsRef.current.add(msg.id);
|
|
234
|
+
setMessages(
|
|
235
|
+
(prev) => prev.map((m) => m.id === optimistic.id ? msg : m)
|
|
236
|
+
);
|
|
237
|
+
return msg;
|
|
238
|
+
} catch (err) {
|
|
239
|
+
setMessages((prev) => prev.filter((m) => m.id !== optimistic.id));
|
|
240
|
+
throw err;
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
[client, ticketId]
|
|
244
|
+
);
|
|
245
|
+
return { messages, isLoading, error, send, refresh };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// src/chat/useTyping.ts
|
|
249
|
+
var import_react6 = require("react");
|
|
250
|
+
var TYPING_TIMEOUT = 4e3;
|
|
251
|
+
function useTyping(ticketId) {
|
|
252
|
+
const client = useChatClient();
|
|
253
|
+
const [typingAgents, setTypingAgents] = (0, import_react6.useState)([]);
|
|
254
|
+
const timersRef = (0, import_react6.useRef)(/* @__PURE__ */ new Map());
|
|
255
|
+
const mountedRef = (0, import_react6.useRef)(true);
|
|
256
|
+
(0, import_react6.useEffect)(() => {
|
|
257
|
+
mountedRef.current = true;
|
|
258
|
+
if (!ticketId) {
|
|
259
|
+
setTypingAgents([]);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const unsubscribe = client.onTyping(ticketId, (payload) => {
|
|
263
|
+
if (!mountedRef.current) return;
|
|
264
|
+
const { agent_id, agent_name, is_typing } = payload;
|
|
265
|
+
const existing = timersRef.current.get(agent_id);
|
|
266
|
+
if (existing) clearTimeout(existing);
|
|
267
|
+
if (is_typing) {
|
|
268
|
+
setTypingAgents((prev) => {
|
|
269
|
+
if (prev.some((a) => a.agentId === agent_id)) return prev;
|
|
270
|
+
return [...prev, { agentId: agent_id, agentName: agent_name }];
|
|
271
|
+
});
|
|
272
|
+
const timer = setTimeout(() => {
|
|
273
|
+
if (mountedRef.current) {
|
|
274
|
+
setTypingAgents((prev) => prev.filter((a) => a.agentId !== agent_id));
|
|
275
|
+
timersRef.current.delete(agent_id);
|
|
276
|
+
}
|
|
277
|
+
}, TYPING_TIMEOUT);
|
|
278
|
+
timersRef.current.set(agent_id, timer);
|
|
279
|
+
} else {
|
|
280
|
+
setTypingAgents((prev) => prev.filter((a) => a.agentId !== agent_id));
|
|
281
|
+
timersRef.current.delete(agent_id);
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
return () => {
|
|
285
|
+
mountedRef.current = false;
|
|
286
|
+
unsubscribe();
|
|
287
|
+
for (const timer of timersRef.current.values()) {
|
|
288
|
+
clearTimeout(timer);
|
|
289
|
+
}
|
|
290
|
+
timersRef.current.clear();
|
|
291
|
+
};
|
|
292
|
+
}, [client, ticketId]);
|
|
293
|
+
const sendTyping = (0, import_react6.useCallback)(
|
|
294
|
+
async (isTyping) => {
|
|
295
|
+
if (!ticketId) return;
|
|
296
|
+
await client.sendTyping(ticketId, isTyping);
|
|
297
|
+
},
|
|
298
|
+
[client, ticketId]
|
|
299
|
+
);
|
|
300
|
+
return { typingAgents, sendTyping };
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// src/library/CStarLibraryProvider.tsx
|
|
304
|
+
var import_react7 = require("react");
|
|
305
|
+
var import_library = require("@cstar.help/js/library");
|
|
306
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
307
|
+
function CStarLibraryProvider({ children, ...config }) {
|
|
308
|
+
const clientRef = (0, import_react7.useRef)(null);
|
|
309
|
+
if (!clientRef.current) {
|
|
310
|
+
clientRef.current = new import_library.LibraryClient(config);
|
|
311
|
+
}
|
|
312
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CStarLibraryContext.Provider, { value: clientRef.current, children });
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// src/library/useCategories.ts
|
|
316
|
+
var import_react8 = require("react");
|
|
317
|
+
function useCategories() {
|
|
318
|
+
const client = useLibraryClient();
|
|
319
|
+
const [categories, setCategories] = (0, import_react8.useState)([]);
|
|
320
|
+
const [isLoading, setIsLoading] = (0, import_react8.useState)(true);
|
|
321
|
+
const [error, setError] = (0, import_react8.useState)(null);
|
|
322
|
+
const mountedRef = (0, import_react8.useRef)(true);
|
|
323
|
+
const refresh = (0, import_react8.useCallback)(async () => {
|
|
324
|
+
setIsLoading(true);
|
|
325
|
+
setError(null);
|
|
326
|
+
try {
|
|
327
|
+
const data = await client.categories();
|
|
328
|
+
if (mountedRef.current) {
|
|
329
|
+
setCategories(data);
|
|
330
|
+
}
|
|
331
|
+
} catch (err) {
|
|
332
|
+
if (mountedRef.current) {
|
|
333
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
334
|
+
}
|
|
335
|
+
} finally {
|
|
336
|
+
if (mountedRef.current) {
|
|
337
|
+
setIsLoading(false);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}, [client]);
|
|
341
|
+
(0, import_react8.useEffect)(() => {
|
|
342
|
+
mountedRef.current = true;
|
|
343
|
+
refresh();
|
|
344
|
+
return () => {
|
|
345
|
+
mountedRef.current = false;
|
|
346
|
+
};
|
|
347
|
+
}, [refresh]);
|
|
348
|
+
return { categories, isLoading, error, refresh };
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// src/library/useArticles.ts
|
|
352
|
+
var import_react9 = require("react");
|
|
353
|
+
function useArticles(params) {
|
|
354
|
+
const client = useLibraryClient();
|
|
355
|
+
const [articles, setArticles] = (0, import_react9.useState)([]);
|
|
356
|
+
const [isLoading, setIsLoading] = (0, import_react9.useState)(true);
|
|
357
|
+
const [error, setError] = (0, import_react9.useState)(null);
|
|
358
|
+
const mountedRef = (0, import_react9.useRef)(true);
|
|
359
|
+
const categorySlug = params?.categorySlug;
|
|
360
|
+
const limit = params?.limit;
|
|
361
|
+
const offset = params?.offset;
|
|
362
|
+
const refresh = (0, import_react9.useCallback)(async () => {
|
|
363
|
+
setIsLoading(true);
|
|
364
|
+
setError(null);
|
|
365
|
+
try {
|
|
366
|
+
const data = await client.articles({ categorySlug, limit, offset });
|
|
367
|
+
if (mountedRef.current) {
|
|
368
|
+
setArticles(data);
|
|
369
|
+
}
|
|
370
|
+
} catch (err) {
|
|
371
|
+
if (mountedRef.current) {
|
|
372
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
373
|
+
}
|
|
374
|
+
} finally {
|
|
375
|
+
if (mountedRef.current) {
|
|
376
|
+
setIsLoading(false);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}, [client, categorySlug, limit, offset]);
|
|
380
|
+
(0, import_react9.useEffect)(() => {
|
|
381
|
+
mountedRef.current = true;
|
|
382
|
+
refresh();
|
|
383
|
+
return () => {
|
|
384
|
+
mountedRef.current = false;
|
|
385
|
+
};
|
|
386
|
+
}, [refresh]);
|
|
387
|
+
return { articles, isLoading, error, refresh };
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// src/library/useArticle.ts
|
|
391
|
+
var import_react10 = require("react");
|
|
392
|
+
function useArticle(slug) {
|
|
393
|
+
const client = useLibraryClient();
|
|
394
|
+
const [article, setArticle] = (0, import_react10.useState)(null);
|
|
395
|
+
const [isLoading, setIsLoading] = (0, import_react10.useState)(true);
|
|
396
|
+
const [error, setError] = (0, import_react10.useState)(null);
|
|
397
|
+
const mountedRef = (0, import_react10.useRef)(true);
|
|
398
|
+
const refresh = (0, import_react10.useCallback)(async () => {
|
|
399
|
+
if (!slug) return;
|
|
400
|
+
setIsLoading(true);
|
|
401
|
+
setError(null);
|
|
402
|
+
try {
|
|
403
|
+
const data = await client.article(slug);
|
|
404
|
+
if (mountedRef.current) {
|
|
405
|
+
setArticle(data);
|
|
406
|
+
}
|
|
407
|
+
} catch (err) {
|
|
408
|
+
if (mountedRef.current) {
|
|
409
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
410
|
+
}
|
|
411
|
+
} finally {
|
|
412
|
+
if (mountedRef.current) {
|
|
413
|
+
setIsLoading(false);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}, [client, slug]);
|
|
417
|
+
(0, import_react10.useEffect)(() => {
|
|
418
|
+
mountedRef.current = true;
|
|
419
|
+
if (!slug) {
|
|
420
|
+
setArticle(null);
|
|
421
|
+
setIsLoading(false);
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
refresh();
|
|
425
|
+
return () => {
|
|
426
|
+
mountedRef.current = false;
|
|
427
|
+
};
|
|
428
|
+
}, [slug, refresh]);
|
|
429
|
+
return { article, isLoading, error, refresh };
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// src/library/useArticleSearch.ts
|
|
433
|
+
var import_react11 = require("react");
|
|
434
|
+
var DEBOUNCE_MS = 300;
|
|
435
|
+
function useArticleSearch(query) {
|
|
436
|
+
const client = useLibraryClient();
|
|
437
|
+
const [results, setResults] = (0, import_react11.useState)([]);
|
|
438
|
+
const [totalCount, setTotalCount] = (0, import_react11.useState)(0);
|
|
439
|
+
const [isLoading, setIsLoading] = (0, import_react11.useState)(false);
|
|
440
|
+
const [error, setError] = (0, import_react11.useState)(null);
|
|
441
|
+
const mountedRef = (0, import_react11.useRef)(true);
|
|
442
|
+
(0, import_react11.useEffect)(() => {
|
|
443
|
+
mountedRef.current = true;
|
|
444
|
+
const trimmed = query.trim();
|
|
445
|
+
if (!trimmed) {
|
|
446
|
+
setResults([]);
|
|
447
|
+
setTotalCount(0);
|
|
448
|
+
setIsLoading(false);
|
|
449
|
+
setError(null);
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
setIsLoading(true);
|
|
453
|
+
const timer = setTimeout(async () => {
|
|
454
|
+
try {
|
|
455
|
+
const result = await client.search(trimmed);
|
|
456
|
+
if (mountedRef.current) {
|
|
457
|
+
setResults(result.data);
|
|
458
|
+
setTotalCount(result.count);
|
|
459
|
+
setError(null);
|
|
460
|
+
}
|
|
461
|
+
} catch (err) {
|
|
462
|
+
if (mountedRef.current) {
|
|
463
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
464
|
+
}
|
|
465
|
+
} finally {
|
|
466
|
+
if (mountedRef.current) {
|
|
467
|
+
setIsLoading(false);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}, DEBOUNCE_MS);
|
|
471
|
+
return () => {
|
|
472
|
+
mountedRef.current = false;
|
|
473
|
+
clearTimeout(timer);
|
|
474
|
+
};
|
|
475
|
+
}, [client, query]);
|
|
476
|
+
return { results, totalCount, isLoading, error };
|
|
477
|
+
}
|
|
478
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
479
|
+
0 && (module.exports = {
|
|
480
|
+
CStarChatContext,
|
|
481
|
+
CStarChatProvider,
|
|
482
|
+
CStarLibraryContext,
|
|
483
|
+
CStarLibraryProvider,
|
|
484
|
+
useArticle,
|
|
485
|
+
useArticleSearch,
|
|
486
|
+
useArticles,
|
|
487
|
+
useCategories,
|
|
488
|
+
useChat,
|
|
489
|
+
useChatClient,
|
|
490
|
+
useConversations,
|
|
491
|
+
useLibraryClient,
|
|
492
|
+
useMessages,
|
|
493
|
+
useTyping
|
|
494
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { ChatClient, ChatConfig, IdentifyParams, IdentifyResult, Conversation, ConversationCreateParams, WidgetMessage } from '@cstar.help/js/chat';
|
|
4
|
+
export { ChatConfig, Conversation, ConversationCreateParams, ConversationListResponse, IdentifyParams, IdentifyResult, TypingPayload, WidgetMessage } from '@cstar.help/js/chat';
|
|
5
|
+
import { LibraryClient, LibraryConfig, Category, LibraryArticle, LibraryArticleListParams } from '@cstar.help/js/library';
|
|
6
|
+
export { Category, LibraryArticle, LibraryArticleListParams, LibraryConfig, LibrarySearchParams, LibrarySearchResult } from '@cstar.help/js/library';
|
|
7
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
8
|
+
|
|
9
|
+
declare const CStarChatContext: react.Context<ChatClient | null>;
|
|
10
|
+
declare const CStarLibraryContext: react.Context<LibraryClient | null>;
|
|
11
|
+
/**
|
|
12
|
+
* Access the ChatClient from the nearest CStarChatProvider.
|
|
13
|
+
* Throws if used outside a provider.
|
|
14
|
+
*/
|
|
15
|
+
declare function useChatClient(): ChatClient;
|
|
16
|
+
/**
|
|
17
|
+
* Access the LibraryClient from the nearest CStarLibraryProvider.
|
|
18
|
+
* Throws if used outside a provider.
|
|
19
|
+
*/
|
|
20
|
+
declare function useLibraryClient(): LibraryClient;
|
|
21
|
+
|
|
22
|
+
interface CStarChatProviderProps extends ChatConfig {
|
|
23
|
+
children: ReactNode;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Provides a ChatClient instance to all child components.
|
|
27
|
+
* Creates the client on mount and disconnects on unmount.
|
|
28
|
+
*
|
|
29
|
+
* ```tsx
|
|
30
|
+
* <CStarChatProvider teamSlug="acme" supabaseUrl="..." supabaseAnonKey="...">
|
|
31
|
+
* <ChatWidget />
|
|
32
|
+
* </CStarChatProvider>
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
declare function CStarChatProvider({ children, ...config }: CStarChatProviderProps): react_jsx_runtime.JSX.Element;
|
|
36
|
+
|
|
37
|
+
interface UseChatReturn {
|
|
38
|
+
/** Identify a customer with HMAC signature. */
|
|
39
|
+
identify: (customer: IdentifyParams, signature: string) => Promise<IdentifyResult>;
|
|
40
|
+
/** Disconnect the client and clean up all subscriptions. */
|
|
41
|
+
disconnect: () => void;
|
|
42
|
+
/** Whether a customer has been successfully identified. */
|
|
43
|
+
isIdentified: boolean;
|
|
44
|
+
/** Whether the Supabase Realtime connection is ready. */
|
|
45
|
+
isRealtimeReady: boolean;
|
|
46
|
+
/** The last error from identify, or null. */
|
|
47
|
+
error: Error | null;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Core chat hook — manages identity verification and realtime readiness.
|
|
51
|
+
*
|
|
52
|
+
* ```tsx
|
|
53
|
+
* const { identify, disconnect, isIdentified, isRealtimeReady, error } = useChat();
|
|
54
|
+
*
|
|
55
|
+
* await identify(
|
|
56
|
+
* { externalId: 'usr_123', email: 'jane@co.com', timestamp: Date.now() / 1000 },
|
|
57
|
+
* hmacSignature
|
|
58
|
+
* );
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
declare function useChat(): UseChatReturn;
|
|
62
|
+
|
|
63
|
+
interface UseConversationsReturn {
|
|
64
|
+
/** List of conversations for the identified customer. */
|
|
65
|
+
conversations: Conversation[];
|
|
66
|
+
/** Whether the initial fetch is in progress. */
|
|
67
|
+
isLoading: boolean;
|
|
68
|
+
/** The last error, or null. */
|
|
69
|
+
error: Error | null;
|
|
70
|
+
/** Whether there are more conversations to load. */
|
|
71
|
+
hasMore: boolean;
|
|
72
|
+
/** Re-fetch the conversation list. */
|
|
73
|
+
refresh: () => Promise<void>;
|
|
74
|
+
/** Create a new conversation. */
|
|
75
|
+
create: (params: ConversationCreateParams) => Promise<Conversation>;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Manage the customer's conversation list.
|
|
79
|
+
*
|
|
80
|
+
* ```tsx
|
|
81
|
+
* const { conversations, isLoading, create, refresh } = useConversations();
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
declare function useConversations(): UseConversationsReturn;
|
|
85
|
+
|
|
86
|
+
interface UseMessagesReturn {
|
|
87
|
+
/** Messages in the conversation, oldest first. */
|
|
88
|
+
messages: WidgetMessage[];
|
|
89
|
+
/** Whether the initial fetch is in progress. */
|
|
90
|
+
isLoading: boolean;
|
|
91
|
+
/** The last error, or null. */
|
|
92
|
+
error: Error | null;
|
|
93
|
+
/** Send a message. Optimistically appended, replaced with server response. */
|
|
94
|
+
send: (content: string) => Promise<WidgetMessage>;
|
|
95
|
+
/** Re-fetch messages from the API. */
|
|
96
|
+
refresh: () => Promise<void>;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Manage messages for a single conversation with automatic real-time updates.
|
|
100
|
+
*
|
|
101
|
+
* ```tsx
|
|
102
|
+
* const { messages, isLoading, send } = useMessages(ticketId);
|
|
103
|
+
* await send('Hello!');
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
declare function useMessages(ticketId: string | null): UseMessagesReturn;
|
|
107
|
+
|
|
108
|
+
interface TypingAgent {
|
|
109
|
+
agentId: string;
|
|
110
|
+
agentName: string;
|
|
111
|
+
}
|
|
112
|
+
interface UseTypingReturn {
|
|
113
|
+
/** Agents currently typing. Auto-cleared after 4s of no activity. */
|
|
114
|
+
typingAgents: TypingAgent[];
|
|
115
|
+
/** Send a customer typing indicator. */
|
|
116
|
+
sendTyping: (isTyping: boolean) => Promise<void>;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Track agent typing indicators for a conversation.
|
|
120
|
+
*
|
|
121
|
+
* ```tsx
|
|
122
|
+
* const { typingAgents, sendTyping } = useTyping(ticketId);
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
declare function useTyping(ticketId: string | null): UseTypingReturn;
|
|
126
|
+
|
|
127
|
+
interface CStarLibraryProviderProps extends LibraryConfig {
|
|
128
|
+
children: ReactNode;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Provides a LibraryClient instance to all child components.
|
|
132
|
+
*
|
|
133
|
+
* ```tsx
|
|
134
|
+
* <CStarLibraryProvider teamSlug="acme">
|
|
135
|
+
* <KnowledgeBase />
|
|
136
|
+
* </CStarLibraryProvider>
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
declare function CStarLibraryProvider({ children, ...config }: CStarLibraryProviderProps): react_jsx_runtime.JSX.Element;
|
|
140
|
+
|
|
141
|
+
interface UseCategoriesReturn {
|
|
142
|
+
/** All public categories. */
|
|
143
|
+
categories: Category[];
|
|
144
|
+
/** Whether the initial fetch is in progress. */
|
|
145
|
+
isLoading: boolean;
|
|
146
|
+
/** The last error, or null. */
|
|
147
|
+
error: Error | null;
|
|
148
|
+
/** Re-fetch categories. */
|
|
149
|
+
refresh: () => Promise<void>;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Fetch public knowledge base categories.
|
|
153
|
+
*
|
|
154
|
+
* ```tsx
|
|
155
|
+
* const { categories, isLoading, error } = useCategories();
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
declare function useCategories(): UseCategoriesReturn;
|
|
159
|
+
|
|
160
|
+
interface UseArticlesReturn {
|
|
161
|
+
/** Published articles. */
|
|
162
|
+
articles: LibraryArticle[];
|
|
163
|
+
/** Whether the fetch is in progress. */
|
|
164
|
+
isLoading: boolean;
|
|
165
|
+
/** The last error, or null. */
|
|
166
|
+
error: Error | null;
|
|
167
|
+
/** Re-fetch articles with the current params. */
|
|
168
|
+
refresh: () => Promise<void>;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Fetch published articles, optionally filtered by category.
|
|
172
|
+
*
|
|
173
|
+
* ```tsx
|
|
174
|
+
* const { articles, isLoading } = useArticles({ categorySlug: 'getting-started' });
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
declare function useArticles(params?: LibraryArticleListParams): UseArticlesReturn;
|
|
178
|
+
|
|
179
|
+
interface UseArticleReturn {
|
|
180
|
+
/** The article, or null if not yet loaded. */
|
|
181
|
+
article: LibraryArticle | null;
|
|
182
|
+
/** Whether the fetch is in progress. */
|
|
183
|
+
isLoading: boolean;
|
|
184
|
+
/** The last error, or null. */
|
|
185
|
+
error: Error | null;
|
|
186
|
+
/** Re-fetch the article. */
|
|
187
|
+
refresh: () => Promise<void>;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Fetch a single article by slug.
|
|
191
|
+
*
|
|
192
|
+
* ```tsx
|
|
193
|
+
* const { article, isLoading, error } = useArticle('getting-started');
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
196
|
+
declare function useArticle(slug: string | null): UseArticleReturn;
|
|
197
|
+
|
|
198
|
+
interface UseArticleSearchReturn {
|
|
199
|
+
/** Search results. */
|
|
200
|
+
results: LibraryArticle[];
|
|
201
|
+
/** Total number of matching articles. */
|
|
202
|
+
totalCount: number;
|
|
203
|
+
/** Whether a search is in progress. */
|
|
204
|
+
isLoading: boolean;
|
|
205
|
+
/** The last error, or null. */
|
|
206
|
+
error: Error | null;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Search articles with automatic debouncing (300ms).
|
|
210
|
+
* Clears results when query is empty.
|
|
211
|
+
*
|
|
212
|
+
* ```tsx
|
|
213
|
+
* const { results, totalCount, isLoading } = useArticleSearch(searchTerm);
|
|
214
|
+
* ```
|
|
215
|
+
*/
|
|
216
|
+
declare function useArticleSearch(query: string): UseArticleSearchReturn;
|
|
217
|
+
|
|
218
|
+
export { CStarChatContext, CStarChatProvider, type CStarChatProviderProps, CStarLibraryContext, CStarLibraryProvider, type CStarLibraryProviderProps, type TypingAgent, type UseArticleReturn, type UseArticleSearchReturn, type UseArticlesReturn, type UseCategoriesReturn, type UseChatReturn, type UseConversationsReturn, type UseMessagesReturn, type UseTypingReturn, useArticle, useArticleSearch, useArticles, useCategories, useChat, useChatClient, useConversations, useLibraryClient, useMessages, useTyping };
|