@pxlabz/tracey-react 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.
Files changed (135) hide show
  1. package/README.md +83 -0
  2. package/dist/LauncherTrigger-5pNAvA05.js +1056 -0
  3. package/dist/LauncherTrigger-5pNAvA05.js.map +1 -0
  4. package/dist/LauncherTrigger-DjXDXHTm.cjs +1055 -0
  5. package/dist/LauncherTrigger-DjXDXHTm.cjs.map +1 -0
  6. package/dist/__tests__/api-client.test.d.ts +2 -0
  7. package/dist/__tests__/api-client.test.d.ts.map +1 -0
  8. package/dist/__tests__/events.test.d.ts +2 -0
  9. package/dist/__tests__/events.test.d.ts.map +1 -0
  10. package/dist/__tests__/survey-logic.test.d.ts +2 -0
  11. package/dist/__tests__/survey-logic.test.d.ts.map +1 -0
  12. package/dist/components/announcements/AnnouncementBell.d.ts +9 -0
  13. package/dist/components/announcements/AnnouncementBell.d.ts.map +1 -0
  14. package/dist/components/announcements/AnnouncementList.d.ts +7 -0
  15. package/dist/components/announcements/AnnouncementList.d.ts.map +1 -0
  16. package/dist/components/announcements/AnnouncementModal.d.ts +9 -0
  17. package/dist/components/announcements/AnnouncementModal.d.ts.map +1 -0
  18. package/dist/components/announcements/headless/AnnouncementClose.d.ts +9 -0
  19. package/dist/components/announcements/headless/AnnouncementClose.d.ts.map +1 -0
  20. package/dist/components/announcements/headless/AnnouncementContent.d.ts +7 -0
  21. package/dist/components/announcements/headless/AnnouncementContent.d.ts.map +1 -0
  22. package/dist/components/announcements/headless/AnnouncementNavigation.d.ts +15 -0
  23. package/dist/components/announcements/headless/AnnouncementNavigation.d.ts.map +1 -0
  24. package/dist/components/announcements/headless/AnnouncementPortal.d.ts +7 -0
  25. package/dist/components/announcements/headless/AnnouncementPortal.d.ts.map +1 -0
  26. package/dist/components/announcements/headless/AnnouncementRoot.d.ts +24 -0
  27. package/dist/components/announcements/headless/AnnouncementRoot.d.ts.map +1 -0
  28. package/dist/components/announcements/headless/AnnouncementSlidePrimitive.d.ts +14 -0
  29. package/dist/components/announcements/headless/AnnouncementSlidePrimitive.d.ts.map +1 -0
  30. package/dist/components/announcements/headless/AnnouncementTrigger.d.ts +9 -0
  31. package/dist/components/announcements/headless/AnnouncementTrigger.d.ts.map +1 -0
  32. package/dist/components/announcements/headless/index.d.ts +8 -0
  33. package/dist/components/announcements/headless/index.d.ts.map +1 -0
  34. package/dist/components/announcements/index.d.ts +4 -0
  35. package/dist/components/announcements/index.d.ts.map +1 -0
  36. package/dist/components/feedback/FeedbackForm.d.ts +8 -0
  37. package/dist/components/feedback/FeedbackForm.d.ts.map +1 -0
  38. package/dist/components/feedback/FeedbackWidget.d.ts +9 -0
  39. package/dist/components/feedback/FeedbackWidget.d.ts.map +1 -0
  40. package/dist/components/feedback/headless/FeedbackClose.d.ts +9 -0
  41. package/dist/components/feedback/headless/FeedbackClose.d.ts.map +1 -0
  42. package/dist/components/feedback/headless/FeedbackContent.d.ts +6 -0
  43. package/dist/components/feedback/headless/FeedbackContent.d.ts.map +1 -0
  44. package/dist/components/feedback/headless/FeedbackFormPrimitive.d.ts +28 -0
  45. package/dist/components/feedback/headless/FeedbackFormPrimitive.d.ts.map +1 -0
  46. package/dist/components/feedback/headless/FeedbackPortal.d.ts +7 -0
  47. package/dist/components/feedback/headless/FeedbackPortal.d.ts.map +1 -0
  48. package/dist/components/feedback/headless/FeedbackRoot.d.ts +16 -0
  49. package/dist/components/feedback/headless/FeedbackRoot.d.ts.map +1 -0
  50. package/dist/components/feedback/headless/FeedbackTrigger.d.ts +9 -0
  51. package/dist/components/feedback/headless/FeedbackTrigger.d.ts.map +1 -0
  52. package/dist/components/feedback/headless/index.d.ts +7 -0
  53. package/dist/components/feedback/headless/index.d.ts.map +1 -0
  54. package/dist/components/feedback/index.d.ts +4 -0
  55. package/dist/components/feedback/index.d.ts.map +1 -0
  56. package/dist/components/index.d.ts +5 -0
  57. package/dist/components/index.d.ts.map +1 -0
  58. package/dist/components/launcher/TraceyLauncher.d.ts +8 -0
  59. package/dist/components/launcher/TraceyLauncher.d.ts.map +1 -0
  60. package/dist/components/launcher/headless/LauncherBadge.d.ts +8 -0
  61. package/dist/components/launcher/headless/LauncherBadge.d.ts.map +1 -0
  62. package/dist/components/launcher/headless/LauncherMenu.d.ts +6 -0
  63. package/dist/components/launcher/headless/LauncherMenu.d.ts.map +1 -0
  64. package/dist/components/launcher/headless/LauncherMenuItem.d.ts +9 -0
  65. package/dist/components/launcher/headless/LauncherMenuItem.d.ts.map +1 -0
  66. package/dist/components/launcher/headless/LauncherPanel.d.ts +6 -0
  67. package/dist/components/launcher/headless/LauncherPanel.d.ts.map +1 -0
  68. package/dist/components/launcher/headless/LauncherPortal.d.ts +7 -0
  69. package/dist/components/launcher/headless/LauncherPortal.d.ts.map +1 -0
  70. package/dist/components/launcher/headless/LauncherRoot.d.ts +21 -0
  71. package/dist/components/launcher/headless/LauncherRoot.d.ts.map +1 -0
  72. package/dist/components/launcher/headless/LauncherTab.d.ts +8 -0
  73. package/dist/components/launcher/headless/LauncherTab.d.ts.map +1 -0
  74. package/dist/components/launcher/headless/LauncherTabContent.d.ts +8 -0
  75. package/dist/components/launcher/headless/LauncherTabContent.d.ts.map +1 -0
  76. package/dist/components/launcher/headless/LauncherTrigger.d.ts +10 -0
  77. package/dist/components/launcher/headless/LauncherTrigger.d.ts.map +1 -0
  78. package/dist/components/launcher/headless/index.d.ts +10 -0
  79. package/dist/components/launcher/headless/index.d.ts.map +1 -0
  80. package/dist/components/launcher/index.d.ts +3 -0
  81. package/dist/components/launcher/index.d.ts.map +1 -0
  82. package/dist/components/surveys/SurveyWidget.d.ts +10 -0
  83. package/dist/components/surveys/SurveyWidget.d.ts.map +1 -0
  84. package/dist/components/surveys/index.d.ts +2 -0
  85. package/dist/components/surveys/index.d.ts.map +1 -0
  86. package/dist/core/api-client.d.ts +33 -0
  87. package/dist/core/api-client.d.ts.map +1 -0
  88. package/dist/core/events.d.ts +11 -0
  89. package/dist/core/events.d.ts.map +1 -0
  90. package/dist/core/index.d.ts +4 -0
  91. package/dist/core/index.d.ts.map +1 -0
  92. package/dist/core/screenshot.d.ts +12 -0
  93. package/dist/core/screenshot.d.ts.map +1 -0
  94. package/dist/core/survey-logic.d.ts +7 -0
  95. package/dist/core/survey-logic.d.ts.map +1 -0
  96. package/dist/headless.cjs +100 -0
  97. package/dist/headless.cjs.map +1 -0
  98. package/dist/headless.d.ts +11 -0
  99. package/dist/headless.d.ts.map +1 -0
  100. package/dist/headless.js +101 -0
  101. package/dist/headless.js.map +1 -0
  102. package/dist/hooks/index.d.ts +7 -0
  103. package/dist/hooks/index.d.ts.map +1 -0
  104. package/dist/hooks/useAnnouncements.d.ts +14 -0
  105. package/dist/hooks/useAnnouncements.d.ts.map +1 -0
  106. package/dist/hooks/useFeedback.d.ts +10 -0
  107. package/dist/hooks/useFeedback.d.ts.map +1 -0
  108. package/dist/hooks/useMyFeedback.d.ts +16 -0
  109. package/dist/hooks/useMyFeedback.d.ts.map +1 -0
  110. package/dist/hooks/useSurvey.d.ts +22 -0
  111. package/dist/hooks/useSurvey.d.ts.map +1 -0
  112. package/dist/hooks/useTracey.d.ts +3 -0
  113. package/dist/hooks/useTracey.d.ts.map +1 -0
  114. package/dist/hooks/useTraceyEvents.d.ts +14 -0
  115. package/dist/hooks/useTraceyEvents.d.ts.map +1 -0
  116. package/dist/index.cjs +4913 -0
  117. package/dist/index.cjs.map +1 -0
  118. package/dist/index.d.ts +19 -0
  119. package/dist/index.d.ts.map +1 -0
  120. package/dist/index.js +4892 -0
  121. package/dist/index.js.map +1 -0
  122. package/dist/lib/utils.d.ts +3 -0
  123. package/dist/lib/utils.d.ts.map +1 -0
  124. package/dist/provider/MockTraceyProvider.d.ts +14 -0
  125. package/dist/provider/MockTraceyProvider.d.ts.map +1 -0
  126. package/dist/provider/TraceyProvider.d.ts +11 -0
  127. package/dist/provider/TraceyProvider.d.ts.map +1 -0
  128. package/dist/provider/context.d.ts +19 -0
  129. package/dist/provider/context.d.ts.map +1 -0
  130. package/dist/provider/index.d.ts +4 -0
  131. package/dist/provider/index.d.ts.map +1 -0
  132. package/dist/styles.css +800 -0
  133. package/dist/types.d.ts +188 -0
  134. package/dist/types.d.ts.map +1 -0
  135. package/package.json +94 -0
@@ -0,0 +1,1056 @@
1
+ var __typeError = (msg) => {
2
+ throw TypeError(msg);
3
+ };
4
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
5
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
6
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
7
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
8
+ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
9
+ var _apiKey, _baseUrl, _customerId, _TraceyApiClient_instances, request_fn, _listeners;
10
+ import { jsx, Fragment } from "react/jsx-runtime";
11
+ import { createContext, useMemo, useState, useRef, useCallback, useEffect, useContext, cloneElement } from "react";
12
+ import { createPortal } from "react-dom";
13
+ const TraceyContext = createContext(null);
14
+ class TraceyApiClient {
15
+ constructor(config) {
16
+ __privateAdd(this, _TraceyApiClient_instances);
17
+ __privateAdd(this, _apiKey);
18
+ __privateAdd(this, _baseUrl);
19
+ __privateAdd(this, _customerId, null);
20
+ __privateSet(this, _apiKey, config.apiKey);
21
+ __privateSet(this, _baseUrl, config.baseUrl ?? "https://api.tracey.dev");
22
+ }
23
+ setCustomerId(id) {
24
+ __privateSet(this, _customerId, id);
25
+ }
26
+ getCustomerId() {
27
+ return __privateGet(this, _customerId);
28
+ }
29
+ async identify(user) {
30
+ const { userHash, ...userData } = user;
31
+ return __privateMethod(this, _TraceyApiClient_instances, request_fn).call(this, "/api/sdk/identify", {
32
+ method: "POST",
33
+ body: JSON.stringify({
34
+ ...userData,
35
+ ...userHash ? { userHash } : {}
36
+ })
37
+ });
38
+ }
39
+ async getAnnouncements(filter = "unread") {
40
+ const params = new URLSearchParams();
41
+ if (__privateGet(this, _customerId)) {
42
+ params.set("customerId", __privateGet(this, _customerId));
43
+ }
44
+ params.set("filter", filter);
45
+ return __privateMethod(this, _TraceyApiClient_instances, request_fn).call(this, `/api/sdk/announcements?${params.toString()}`);
46
+ }
47
+ async getUnreadCount() {
48
+ if (!__privateGet(this, _customerId)) {
49
+ throw new Error("Customer ID not set. Call identify first.");
50
+ }
51
+ return __privateMethod(this, _TraceyApiClient_instances, request_fn).call(this, `/api/sdk/announcements/unread-count?customerId=${encodeURIComponent(__privateGet(this, _customerId))}`);
52
+ }
53
+ async trackAnnouncementView(announcementId) {
54
+ if (!__privateGet(this, _customerId)) {
55
+ throw new Error("Customer ID not set. Call identify first.");
56
+ }
57
+ await __privateMethod(this, _TraceyApiClient_instances, request_fn).call(this, `/api/sdk/announcements/${announcementId}/view`, {
58
+ method: "POST",
59
+ body: JSON.stringify({ customerId: __privateGet(this, _customerId) })
60
+ });
61
+ }
62
+ async trackAnnouncementCta(announcementId) {
63
+ if (!__privateGet(this, _customerId)) {
64
+ throw new Error("Customer ID not set. Call identify first.");
65
+ }
66
+ await __privateMethod(this, _TraceyApiClient_instances, request_fn).call(this, `/api/sdk/announcements/${announcementId}/cta`, {
67
+ method: "POST",
68
+ body: JSON.stringify({ customerId: __privateGet(this, _customerId) })
69
+ });
70
+ }
71
+ async submitFeedback(feedback) {
72
+ return __privateMethod(this, _TraceyApiClient_instances, request_fn).call(this, "/api/sdk/feedback", {
73
+ method: "POST",
74
+ body: JSON.stringify({
75
+ ...feedback,
76
+ customerId: __privateGet(this, _customerId) ?? void 0
77
+ })
78
+ });
79
+ }
80
+ async getMyFeedback(params) {
81
+ if (!__privateGet(this, _customerId)) {
82
+ throw new Error("Customer ID not set. Call identify first.");
83
+ }
84
+ const searchParams = new URLSearchParams();
85
+ searchParams.set("customerId", __privateGet(this, _customerId));
86
+ if ((params == null ? void 0 : params.limit) !== void 0) {
87
+ searchParams.set("limit", String(params.limit));
88
+ }
89
+ if ((params == null ? void 0 : params.offset) !== void 0) {
90
+ searchParams.set("offset", String(params.offset));
91
+ }
92
+ return __privateMethod(this, _TraceyApiClient_instances, request_fn).call(this, `/api/sdk/feedback/mine?${searchParams.toString()}`);
93
+ }
94
+ async getActiveSurvey(context) {
95
+ const params = new URLSearchParams();
96
+ if (__privateGet(this, _customerId)) params.set("customerId", __privateGet(this, _customerId));
97
+ if (context == null ? void 0 : context.anonymousSessionId) {
98
+ params.set("anonymousSessionId", context.anonymousSessionId);
99
+ }
100
+ if (context == null ? void 0 : context.url) params.set("url", context.url);
101
+ const query = params.toString();
102
+ const response = await __privateMethod(this, _TraceyApiClient_instances, request_fn).call(this, `/api/sdk/surveys${query ? `?${query}` : ""}`);
103
+ return response.survey;
104
+ }
105
+ async submitSurveyResponse(surveyId, payload) {
106
+ return __privateMethod(this, _TraceyApiClient_instances, request_fn).call(this, `/api/sdk/surveys/${surveyId}/responses`, {
107
+ method: "POST",
108
+ body: JSON.stringify({
109
+ customerId: __privateGet(this, _customerId) ?? void 0,
110
+ anonymousSessionId: payload.anonymousSessionId,
111
+ respondentEmail: payload.respondentEmail,
112
+ respondentName: payload.respondentName,
113
+ metadata: payload.metadata,
114
+ answers: payload.answers
115
+ })
116
+ });
117
+ }
118
+ }
119
+ _apiKey = new WeakMap();
120
+ _baseUrl = new WeakMap();
121
+ _customerId = new WeakMap();
122
+ _TraceyApiClient_instances = new WeakSet();
123
+ request_fn = async function(path, options = {}) {
124
+ const url = `${__privateGet(this, _baseUrl)}${path}`;
125
+ const headers = new Headers(options.headers);
126
+ headers.set("Authorization", `Bearer ${__privateGet(this, _apiKey)}`);
127
+ headers.set("Content-Type", "application/json");
128
+ const response = await fetch(url, {
129
+ ...options,
130
+ headers
131
+ });
132
+ if (!response.ok) {
133
+ const error = await response.json().catch(() => ({
134
+ message: `HTTP ${response.status}: ${response.statusText}`
135
+ }));
136
+ throw new Error(error.message);
137
+ }
138
+ return response.json();
139
+ };
140
+ class TraceyEventEmitter {
141
+ constructor() {
142
+ __privateAdd(this, _listeners, /* @__PURE__ */ new Map());
143
+ }
144
+ subscribe(event, callback) {
145
+ if (!__privateGet(this, _listeners).has(event)) {
146
+ __privateGet(this, _listeners).set(event, /* @__PURE__ */ new Set());
147
+ }
148
+ const listeners = __privateGet(this, _listeners).get(event);
149
+ listeners.add(callback);
150
+ return () => {
151
+ listeners.delete(callback);
152
+ };
153
+ }
154
+ unsubscribe(event, callback) {
155
+ const listeners = __privateGet(this, _listeners).get(event);
156
+ if (listeners) {
157
+ listeners.delete(callback);
158
+ }
159
+ }
160
+ emit(event, data) {
161
+ const listeners = __privateGet(this, _listeners).get(event);
162
+ if (listeners) {
163
+ listeners.forEach((callback) => {
164
+ try {
165
+ callback(data);
166
+ } catch (error) {
167
+ console.error(`Error in event listener for ${event}:`, error);
168
+ }
169
+ });
170
+ }
171
+ }
172
+ clear() {
173
+ __privateGet(this, _listeners).clear();
174
+ }
175
+ }
176
+ _listeners = new WeakMap();
177
+ const UNREAD_POLL_INTERVAL_MS = 5 * 60 * 1e3;
178
+ function getUnreadCacheKey(apiKey, userId) {
179
+ return `tracey:unread:${apiKey}:${userId}`;
180
+ }
181
+ function readCachedUnreadCount(apiKey, userId) {
182
+ try {
183
+ const raw = localStorage.getItem(getUnreadCacheKey(apiKey, userId));
184
+ if (raw !== null) {
185
+ const parsed = parseInt(raw, 10);
186
+ return isNaN(parsed) ? 0 : parsed;
187
+ }
188
+ } catch {
189
+ }
190
+ return 0;
191
+ }
192
+ function writeCachedUnreadCount(apiKey, userId, count) {
193
+ try {
194
+ localStorage.setItem(getUnreadCacheKey(apiKey, userId), String(count));
195
+ } catch {
196
+ }
197
+ }
198
+ function TraceyProvider({
199
+ apiKey,
200
+ baseUrl,
201
+ user,
202
+ children,
203
+ debug = false
204
+ }) {
205
+ const config = useMemo(
206
+ () => ({ apiKey, baseUrl, debug }),
207
+ [apiKey, baseUrl, debug]
208
+ );
209
+ const client = useMemo(() => new TraceyApiClient(config), [config]);
210
+ const events = useMemo(() => new TraceyEventEmitter(), []);
211
+ const [isIdentified, setIsIdentified] = useState(false);
212
+ const [isLoading, setIsLoading] = useState(true);
213
+ const [error, setError] = useState(null);
214
+ const [announcements, setAnnouncements] = useState([]);
215
+ const [announcementsLoading, setAnnouncementsLoading] = useState(false);
216
+ const [unreadCount, setUnreadCountState] = useState(
217
+ () => readCachedUnreadCount(apiKey, user.id)
218
+ );
219
+ const unreadCountRef = useRef(unreadCount);
220
+ const setUnreadCount = useCallback(
221
+ (count) => {
222
+ const previousCount = unreadCountRef.current;
223
+ unreadCountRef.current = count;
224
+ setUnreadCountState(count);
225
+ writeCachedUnreadCount(apiKey, user.id, count);
226
+ if (count !== previousCount) {
227
+ events.emit("unread-count-changed", {
228
+ count,
229
+ previousCount
230
+ });
231
+ }
232
+ },
233
+ [apiKey, user.id, events]
234
+ );
235
+ const fetchUnreadCount = useCallback(async () => {
236
+ try {
237
+ const { count } = await client.getUnreadCount();
238
+ setUnreadCount(count);
239
+ } catch {
240
+ }
241
+ }, [client, setUnreadCount]);
242
+ useEffect(() => {
243
+ let mounted = true;
244
+ const identify = async () => {
245
+ setIsLoading(true);
246
+ setError(null);
247
+ try {
248
+ await client.identify(user);
249
+ client.setCustomerId(user.id);
250
+ if (mounted) {
251
+ setIsIdentified(true);
252
+ events.emit("user:identified", { user });
253
+ }
254
+ } catch (err) {
255
+ if (mounted) {
256
+ const error2 = err instanceof Error ? err : new Error("Failed to identify user");
257
+ setError(error2);
258
+ events.emit("error", { error: error2, context: "identify" });
259
+ }
260
+ } finally {
261
+ if (mounted) {
262
+ setIsLoading(false);
263
+ }
264
+ }
265
+ };
266
+ identify();
267
+ return () => {
268
+ mounted = false;
269
+ };
270
+ }, [client, user, events]);
271
+ const fetchAnnouncements = useCallback(async () => {
272
+ if (!isIdentified) return;
273
+ setAnnouncementsLoading(true);
274
+ try {
275
+ const data = await client.getAnnouncements();
276
+ setAnnouncements(data);
277
+ } catch (err) {
278
+ const error2 = err instanceof Error ? err : new Error("Failed to fetch announcements");
279
+ events.emit("error", { error: error2, context: "announcements" });
280
+ } finally {
281
+ setAnnouncementsLoading(false);
282
+ }
283
+ }, [client, events, isIdentified]);
284
+ useEffect(() => {
285
+ if (isIdentified) {
286
+ fetchAnnouncements();
287
+ fetchUnreadCount();
288
+ }
289
+ }, [isIdentified, fetchAnnouncements, fetchUnreadCount]);
290
+ useEffect(() => {
291
+ if (!isIdentified) return;
292
+ const interval = setInterval(fetchUnreadCount, UNREAD_POLL_INTERVAL_MS);
293
+ return () => clearInterval(interval);
294
+ }, [isIdentified, fetchUnreadCount]);
295
+ const contextValue = useMemo(
296
+ () => ({
297
+ config,
298
+ client,
299
+ events,
300
+ user: isIdentified ? user : null,
301
+ isIdentified,
302
+ isLoading,
303
+ error,
304
+ announcements,
305
+ announcementsLoading,
306
+ refetchAnnouncements: fetchAnnouncements,
307
+ unreadCount,
308
+ setUnreadCount
309
+ }),
310
+ [
311
+ config,
312
+ client,
313
+ events,
314
+ user,
315
+ isIdentified,
316
+ isLoading,
317
+ error,
318
+ announcements,
319
+ announcementsLoading,
320
+ fetchAnnouncements,
321
+ unreadCount,
322
+ setUnreadCount
323
+ ]
324
+ );
325
+ return /* @__PURE__ */ jsx(TraceyContext.Provider, { value: contextValue, children });
326
+ }
327
+ const defaultMockUser = {
328
+ id: "mock-user-1",
329
+ email: "demo@example.com",
330
+ name: "Demo User",
331
+ role: "user"
332
+ };
333
+ const defaultMockAnnouncements = [
334
+ {
335
+ id: 1,
336
+ title: "Welcome to Our App!",
337
+ status: "published",
338
+ displayType: "modal",
339
+ publishAt: null,
340
+ expiresAt: null,
341
+ slides: [
342
+ {
343
+ id: 1,
344
+ order: 0,
345
+ title: "Getting Started",
346
+ body: "Here's how to get the most out of our platform.",
347
+ imageUrl: null,
348
+ videoUrl: null,
349
+ ctaText: "Learn More",
350
+ ctaUrl: "https://example.com/docs"
351
+ }
352
+ ],
353
+ viewed: false,
354
+ ctaClicked: false
355
+ }
356
+ ];
357
+ function MockTraceyProvider({
358
+ children,
359
+ mockUser = defaultMockUser,
360
+ mockAnnouncements = defaultMockAnnouncements
361
+ }) {
362
+ const config = useMemo(
363
+ () => ({
364
+ apiKey: "mock-api-key",
365
+ baseUrl: "https://mock.tracey.dev",
366
+ debug: true
367
+ }),
368
+ []
369
+ );
370
+ const client = useMemo(() => {
371
+ const apiClient = new TraceyApiClient(config);
372
+ apiClient.setCustomerId(mockUser.id);
373
+ apiClient.trackAnnouncementView = async () => {
374
+ };
375
+ apiClient.trackAnnouncementCta = async () => {
376
+ };
377
+ apiClient.submitFeedback = async (feedback) => ({
378
+ id: Date.now(),
379
+ title: feedback.title,
380
+ description: feedback.description ?? null,
381
+ type: feedback.type,
382
+ status: "new",
383
+ category: feedback.category ?? null,
384
+ tags: feedback.tags ?? null,
385
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
386
+ });
387
+ apiClient.identify = async () => ({ success: true });
388
+ apiClient.getAnnouncements = async () => mockAnnouncements;
389
+ apiClient.getUnreadCount = async () => ({
390
+ count: mockAnnouncements.filter((a) => !a.viewed).length
391
+ });
392
+ apiClient.getActiveSurvey = async () => null;
393
+ apiClient.submitSurveyResponse = async () => ({
394
+ success: true,
395
+ responseId: Date.now()
396
+ });
397
+ return apiClient;
398
+ }, [config, mockUser, mockAnnouncements]);
399
+ const events = useMemo(() => new TraceyEventEmitter(), []);
400
+ const [announcements, setAnnouncements] = useState(mockAnnouncements);
401
+ const [unreadCount, setUnreadCount] = useState(
402
+ () => mockAnnouncements.filter((a) => !a.viewed).length
403
+ );
404
+ const refetchAnnouncements = useCallback(async () => {
405
+ setAnnouncements(mockAnnouncements);
406
+ }, [mockAnnouncements]);
407
+ const contextValue = useMemo(
408
+ () => ({
409
+ config,
410
+ client,
411
+ events,
412
+ user: mockUser,
413
+ isIdentified: true,
414
+ isLoading: false,
415
+ error: null,
416
+ announcements,
417
+ announcementsLoading: false,
418
+ refetchAnnouncements,
419
+ unreadCount,
420
+ setUnreadCount
421
+ }),
422
+ [
423
+ config,
424
+ client,
425
+ events,
426
+ mockUser,
427
+ announcements,
428
+ refetchAnnouncements,
429
+ unreadCount
430
+ ]
431
+ );
432
+ return /* @__PURE__ */ jsx(TraceyContext.Provider, { value: contextValue, children });
433
+ }
434
+ function useTracey() {
435
+ const context = useContext(TraceyContext);
436
+ if (!context) {
437
+ throw new Error("useTracey must be used within a TraceyProvider");
438
+ }
439
+ return context;
440
+ }
441
+ const MAX_SUBMISSIONS_PER_MINUTE = 5;
442
+ const RATE_LIMIT_WINDOW_MS = 6e4;
443
+ function useFeedback() {
444
+ const { client, events } = useTracey();
445
+ const [isSubmitting, setIsSubmitting] = useState(false);
446
+ const [error, setError] = useState(null);
447
+ const [lastSubmission, setLastSubmission] = useState(
448
+ null
449
+ );
450
+ const submissionTimestamps = useRef([]);
451
+ const submit = useCallback(
452
+ async (feedback) => {
453
+ const now = Date.now();
454
+ submissionTimestamps.current = submissionTimestamps.current.filter(
455
+ (ts) => now - ts < RATE_LIMIT_WINDOW_MS
456
+ );
457
+ if (submissionTimestamps.current.length >= MAX_SUBMISSIONS_PER_MINUTE) {
458
+ const oldestInWindow = submissionTimestamps.current[0];
459
+ const retryAfterMs = RATE_LIMIT_WINDOW_MS - (now - oldestInWindow);
460
+ const rateLimitError = new Error(
461
+ "Too many submissions. Please wait before submitting again."
462
+ );
463
+ setError(rateLimitError);
464
+ events.emit("feedback:rate-limited", { retryAfterMs });
465
+ return null;
466
+ }
467
+ setIsSubmitting(true);
468
+ setError(null);
469
+ try {
470
+ const response = await client.submitFeedback(feedback);
471
+ submissionTimestamps.current.push(Date.now());
472
+ setLastSubmission(response);
473
+ events.emit("feedback:submitted", { feedback: response });
474
+ return response;
475
+ } catch (err) {
476
+ const error2 = err instanceof Error ? err : new Error("Failed to submit feedback");
477
+ setError(error2);
478
+ events.emit("feedback:error", { error: error2 });
479
+ return null;
480
+ } finally {
481
+ setIsSubmitting(false);
482
+ }
483
+ },
484
+ [client, events]
485
+ );
486
+ const reset = useCallback(() => {
487
+ setError(null);
488
+ setLastSubmission(null);
489
+ }, []);
490
+ return {
491
+ submit,
492
+ isSubmitting,
493
+ error,
494
+ lastSubmission,
495
+ reset
496
+ };
497
+ }
498
+ function useAnnouncements() {
499
+ const {
500
+ client,
501
+ events,
502
+ announcements,
503
+ announcementsLoading,
504
+ refetchAnnouncements,
505
+ unreadCount,
506
+ setUnreadCount
507
+ } = useTracey();
508
+ const unread = useMemo(
509
+ () => announcements.filter((a) => !a.viewed),
510
+ [announcements]
511
+ );
512
+ const markAsViewed = useCallback(
513
+ async (announcementId) => {
514
+ try {
515
+ await client.trackAnnouncementView(announcementId);
516
+ const announcement = announcements.find((a) => a.id === announcementId);
517
+ if (announcement) {
518
+ events.emit("announcement:viewed", { announcement });
519
+ }
520
+ setUnreadCount(Math.max(0, unreadCount - 1));
521
+ await refetchAnnouncements();
522
+ } catch (err) {
523
+ const error = err instanceof Error ? err : new Error("Failed to mark announcement as viewed");
524
+ events.emit("error", { error, context: "announcement:view" });
525
+ }
526
+ },
527
+ [
528
+ client,
529
+ events,
530
+ announcements,
531
+ refetchAnnouncements,
532
+ unreadCount,
533
+ setUnreadCount
534
+ ]
535
+ );
536
+ const markAsRead = useCallback(
537
+ async (announcementId) => {
538
+ await markAsViewed(announcementId);
539
+ },
540
+ [markAsViewed]
541
+ );
542
+ const markAllAsRead = useCallback(async () => {
543
+ const unreadAnnouncements = announcements.filter((a) => !a.viewed);
544
+ if (unreadAnnouncements.length === 0) return;
545
+ setUnreadCount(0);
546
+ try {
547
+ await Promise.all(
548
+ unreadAnnouncements.map((a) => client.trackAnnouncementView(a.id))
549
+ );
550
+ await refetchAnnouncements();
551
+ } catch (err) {
552
+ const error = err instanceof Error ? err : new Error("Failed to mark all announcements as read");
553
+ events.emit("error", { error, context: "announcement:mark-all-read" });
554
+ setUnreadCount(unreadAnnouncements.length);
555
+ }
556
+ }, [announcements, client, events, refetchAnnouncements, setUnreadCount]);
557
+ const markCtaClicked = useCallback(
558
+ async (announcementId, slideIndex) => {
559
+ try {
560
+ await client.trackAnnouncementCta(announcementId);
561
+ const announcement = announcements.find((a) => a.id === announcementId);
562
+ if (announcement) {
563
+ events.emit("announcement:cta-clicked", {
564
+ announcement,
565
+ slideIndex
566
+ });
567
+ }
568
+ } catch (err) {
569
+ const error = err instanceof Error ? err : new Error("Failed to track CTA click");
570
+ events.emit("error", { error, context: "announcement:cta" });
571
+ }
572
+ },
573
+ [client, events, announcements]
574
+ );
575
+ return {
576
+ announcements,
577
+ unread,
578
+ unreadCount,
579
+ isLoading: announcementsLoading,
580
+ markAsViewed,
581
+ markAsRead,
582
+ markAllAsRead,
583
+ markCtaClicked,
584
+ refetch: refetchAnnouncements
585
+ };
586
+ }
587
+ function useTraceyEvents() {
588
+ const { events } = useTracey();
589
+ const subscribe = useCallback(
590
+ (event, callback) => {
591
+ return events.subscribe(event, callback);
592
+ },
593
+ [events]
594
+ );
595
+ const emit = useCallback(
596
+ (event, data) => {
597
+ events.emit(event, data);
598
+ },
599
+ [events]
600
+ );
601
+ return { subscribe, emit };
602
+ }
603
+ const FeedbackContext = createContext(null);
604
+ function useFeedbackContext() {
605
+ const context = useContext(FeedbackContext);
606
+ if (!context) {
607
+ throw new Error(
608
+ "Feedback compound components must be used within FeedbackRoot"
609
+ );
610
+ }
611
+ return context;
612
+ }
613
+ function FeedbackRoot({
614
+ children,
615
+ defaultOpen = false,
616
+ open,
617
+ onOpenChange
618
+ }) {
619
+ const [internalOpen, setInternalOpen] = useState(defaultOpen);
620
+ const isOpen = open ?? internalOpen;
621
+ const setIsOpen = useCallback(
622
+ (newOpen) => {
623
+ if (open === void 0) {
624
+ setInternalOpen(newOpen);
625
+ }
626
+ onOpenChange == null ? void 0 : onOpenChange(newOpen);
627
+ },
628
+ [open, onOpenChange]
629
+ );
630
+ const toggle = useCallback(() => {
631
+ setIsOpen(!isOpen);
632
+ }, [isOpen, setIsOpen]);
633
+ return /* @__PURE__ */ jsx(FeedbackContext.Provider, { value: { isOpen, setIsOpen, toggle }, children });
634
+ }
635
+ function FeedbackClose({
636
+ children,
637
+ asChild = true
638
+ }) {
639
+ const { setIsOpen } = useFeedbackContext();
640
+ const handleClose = (e) => {
641
+ var _a, _b;
642
+ (_b = (_a = children.props).onClick) == null ? void 0 : _b.call(_a, e);
643
+ setIsOpen(false);
644
+ };
645
+ if (asChild) {
646
+ return cloneElement(children, { onClick: handleClose });
647
+ }
648
+ return /* @__PURE__ */ jsx("button", { type: "button", onClick: handleClose, children });
649
+ }
650
+ function FeedbackFormPrimitive({
651
+ children,
652
+ onSuccess,
653
+ closeOnSuccess = true
654
+ }) {
655
+ const { setIsOpen } = useFeedbackContext();
656
+ const { submit, isSubmitting, error } = useFeedback();
657
+ const [title, setTitle] = useState("");
658
+ const [description, setDescription] = useState("");
659
+ const [type, setType] = useState("general");
660
+ const [screenshot, setScreenshot] = useState(null);
661
+ const handleSubmit = useCallback(
662
+ async (e) => {
663
+ e.preventDefault();
664
+ const result = await submit({
665
+ title,
666
+ description: description || void 0,
667
+ type,
668
+ screenshot: screenshot ?? void 0
669
+ });
670
+ if (result) {
671
+ setTitle("");
672
+ setDescription("");
673
+ setType("general");
674
+ setScreenshot(null);
675
+ onSuccess == null ? void 0 : onSuccess();
676
+ if (closeOnSuccess) {
677
+ setIsOpen(false);
678
+ }
679
+ }
680
+ },
681
+ [
682
+ submit,
683
+ title,
684
+ description,
685
+ type,
686
+ screenshot,
687
+ onSuccess,
688
+ closeOnSuccess,
689
+ setIsOpen
690
+ ]
691
+ );
692
+ return /* @__PURE__ */ jsx(Fragment, { children: children({
693
+ title,
694
+ setTitle,
695
+ description,
696
+ setDescription,
697
+ type,
698
+ setType,
699
+ screenshot,
700
+ setScreenshot,
701
+ isSubmitting,
702
+ error,
703
+ handleSubmit
704
+ }) });
705
+ }
706
+ function FeedbackContent({ children, ...props }) {
707
+ const { isOpen } = useFeedbackContext();
708
+ if (!isOpen) {
709
+ return null;
710
+ }
711
+ return /* @__PURE__ */ jsx("div", { role: "dialog", "aria-modal": "true", ...props, children });
712
+ }
713
+ function FeedbackPortal({ children, container }) {
714
+ const [mounted, setMounted] = useState(false);
715
+ useEffect(() => {
716
+ setMounted(true);
717
+ }, []);
718
+ if (!mounted) {
719
+ return null;
720
+ }
721
+ const target = container ?? document.body;
722
+ return createPortal(children, target);
723
+ }
724
+ function FeedbackTrigger({
725
+ children,
726
+ asChild = true
727
+ }) {
728
+ const { toggle } = useFeedbackContext();
729
+ if (asChild) {
730
+ return cloneElement(children, {
731
+ onClick: (e) => {
732
+ var _a, _b;
733
+ (_b = (_a = children.props).onClick) == null ? void 0 : _b.call(_a, e);
734
+ toggle();
735
+ }
736
+ });
737
+ }
738
+ return /* @__PURE__ */ jsx("button", { type: "button", onClick: toggle, children });
739
+ }
740
+ const AnnouncementContext = createContext(
741
+ null
742
+ );
743
+ function useAnnouncementContext() {
744
+ const context = useContext(AnnouncementContext);
745
+ if (!context) {
746
+ throw new Error(
747
+ "Announcement compound components must be used within AnnouncementRoot"
748
+ );
749
+ }
750
+ return context;
751
+ }
752
+ function AnnouncementRoot({
753
+ children,
754
+ announcement = null,
755
+ defaultOpen = false,
756
+ open,
757
+ onOpenChange
758
+ }) {
759
+ const [internalOpen, setInternalOpen] = useState(defaultOpen);
760
+ const [currentAnnouncement, setCurrentAnnouncement] = useState(announcement);
761
+ const [currentSlideIndex, setCurrentSlideIndex] = useState(0);
762
+ useEffect(() => {
763
+ setCurrentAnnouncement(announcement);
764
+ setCurrentSlideIndex(0);
765
+ }, [announcement]);
766
+ const isOpen = open ?? internalOpen;
767
+ const totalSlides = (currentAnnouncement == null ? void 0 : currentAnnouncement.slides.length) ?? 0;
768
+ const setIsOpen = useCallback(
769
+ (newOpen) => {
770
+ if (open === void 0) {
771
+ setInternalOpen(newOpen);
772
+ }
773
+ onOpenChange == null ? void 0 : onOpenChange(newOpen);
774
+ },
775
+ [open, onOpenChange]
776
+ );
777
+ const nextSlide = useCallback(() => {
778
+ if (currentSlideIndex < totalSlides - 1) {
779
+ setCurrentSlideIndex((prev) => prev + 1);
780
+ }
781
+ }, [currentSlideIndex, totalSlides]);
782
+ const prevSlide = useCallback(() => {
783
+ if (currentSlideIndex > 0) {
784
+ setCurrentSlideIndex((prev) => prev - 1);
785
+ }
786
+ }, [currentSlideIndex]);
787
+ return /* @__PURE__ */ jsx(
788
+ AnnouncementContext.Provider,
789
+ {
790
+ value: {
791
+ isOpen,
792
+ setIsOpen,
793
+ currentAnnouncement,
794
+ setCurrentAnnouncement,
795
+ currentSlideIndex,
796
+ setCurrentSlideIndex,
797
+ nextSlide,
798
+ prevSlide,
799
+ totalSlides
800
+ },
801
+ children
802
+ }
803
+ );
804
+ }
805
+ function AnnouncementClose({
806
+ children,
807
+ asChild = true
808
+ }) {
809
+ const { setIsOpen } = useAnnouncementContext();
810
+ const handleClose = (e) => {
811
+ var _a, _b;
812
+ (_b = (_a = children.props).onClick) == null ? void 0 : _b.call(_a, e);
813
+ setIsOpen(false);
814
+ };
815
+ if (asChild) {
816
+ return cloneElement(children, { onClick: handleClose });
817
+ }
818
+ return /* @__PURE__ */ jsx("button", { type: "button", onClick: handleClose, children });
819
+ }
820
+ function AnnouncementContent({
821
+ children,
822
+ trackView = true,
823
+ ...props
824
+ }) {
825
+ const { isOpen, currentAnnouncement } = useAnnouncementContext();
826
+ const { markAsViewed } = useAnnouncements();
827
+ const viewedRef = useRef(/* @__PURE__ */ new Set());
828
+ useEffect(() => {
829
+ if (isOpen && trackView && currentAnnouncement && !currentAnnouncement.viewed && !viewedRef.current.has(currentAnnouncement.id)) {
830
+ viewedRef.current.add(currentAnnouncement.id);
831
+ markAsViewed(currentAnnouncement.id);
832
+ }
833
+ }, [isOpen, trackView, currentAnnouncement, markAsViewed]);
834
+ if (!isOpen) {
835
+ return null;
836
+ }
837
+ return /* @__PURE__ */ jsx("div", { role: "dialog", "aria-modal": "true", ...props, children });
838
+ }
839
+ function AnnouncementNavigation({
840
+ children
841
+ }) {
842
+ const {
843
+ currentSlideIndex,
844
+ totalSlides,
845
+ nextSlide,
846
+ prevSlide,
847
+ setCurrentSlideIndex
848
+ } = useAnnouncementContext();
849
+ const canGoNext = currentSlideIndex < totalSlides - 1;
850
+ const canGoPrev = currentSlideIndex > 0;
851
+ return /* @__PURE__ */ jsx(Fragment, { children: children({
852
+ currentSlide: currentSlideIndex,
853
+ totalSlides,
854
+ canGoNext,
855
+ canGoPrev,
856
+ nextSlide,
857
+ prevSlide,
858
+ goToSlide: setCurrentSlideIndex
859
+ }) });
860
+ }
861
+ function AnnouncementPortal({
862
+ children,
863
+ container
864
+ }) {
865
+ const [mounted, setMounted] = useState(false);
866
+ useEffect(() => {
867
+ setMounted(true);
868
+ }, []);
869
+ if (!mounted) {
870
+ return null;
871
+ }
872
+ const target = container ?? document.body;
873
+ return createPortal(children, target);
874
+ }
875
+ function LauncherBadge({
876
+ count,
877
+ children,
878
+ max = 99
879
+ }) {
880
+ if (count <= 0) {
881
+ return null;
882
+ }
883
+ if (children) {
884
+ return /* @__PURE__ */ jsx(Fragment, { children: children(count) });
885
+ }
886
+ return /* @__PURE__ */ jsx("span", { children: count > max ? `${max}+` : count });
887
+ }
888
+ const LauncherContext = createContext(null);
889
+ function useLauncherContext() {
890
+ const context = useContext(LauncherContext);
891
+ if (!context) {
892
+ throw new Error(
893
+ "Launcher compound components must be used within LauncherRoot"
894
+ );
895
+ }
896
+ return context;
897
+ }
898
+ function LauncherRoot({
899
+ children,
900
+ defaultOpen = false,
901
+ open,
902
+ onOpenChange,
903
+ defaultActiveItem = null
904
+ }) {
905
+ const [internalOpen, setInternalOpen] = useState(defaultOpen);
906
+ const [activeItem, setActiveItem] = useState(defaultActiveItem);
907
+ const triggerRef = useRef(null);
908
+ const isOpen = open ?? internalOpen;
909
+ const setIsOpen = useCallback(
910
+ (newOpen) => {
911
+ if (open === void 0) {
912
+ setInternalOpen(newOpen);
913
+ }
914
+ if (!newOpen) {
915
+ setActiveItem(defaultActiveItem);
916
+ }
917
+ onOpenChange == null ? void 0 : onOpenChange(newOpen);
918
+ },
919
+ [open, onOpenChange, defaultActiveItem]
920
+ );
921
+ const toggle = useCallback(() => {
922
+ setIsOpen(!isOpen);
923
+ }, [isOpen, setIsOpen]);
924
+ return /* @__PURE__ */ jsx(
925
+ LauncherContext.Provider,
926
+ {
927
+ value: {
928
+ isOpen,
929
+ setIsOpen,
930
+ toggle,
931
+ activeItem,
932
+ setActiveItem,
933
+ triggerRef
934
+ },
935
+ children
936
+ }
937
+ );
938
+ }
939
+ function LauncherPanel({ children, ...props }) {
940
+ const { isOpen, setIsOpen, triggerRef } = useLauncherContext();
941
+ const panelRef = useRef(null);
942
+ useEffect(() => {
943
+ if (!isOpen) return;
944
+ const handleClickOutside = (e) => {
945
+ const target = e.target;
946
+ if (panelRef.current && !panelRef.current.contains(target) && triggerRef.current && !triggerRef.current.contains(target)) {
947
+ setIsOpen(false);
948
+ }
949
+ };
950
+ const handleEscape = (e) => {
951
+ if (e.key === "Escape") {
952
+ setIsOpen(false);
953
+ }
954
+ };
955
+ document.addEventListener("mousedown", handleClickOutside);
956
+ document.addEventListener("keydown", handleEscape);
957
+ return () => {
958
+ document.removeEventListener("mousedown", handleClickOutside);
959
+ document.removeEventListener("keydown", handleEscape);
960
+ };
961
+ }, [isOpen, setIsOpen, triggerRef]);
962
+ if (!isOpen) {
963
+ return null;
964
+ }
965
+ return /* @__PURE__ */ jsx("div", { ref: panelRef, role: "dialog", "aria-label": "Tracey widget", ...props, children });
966
+ }
967
+ function LauncherPortal({ children, container }) {
968
+ const [mounted, setMounted] = useState(false);
969
+ useEffect(() => {
970
+ setMounted(true);
971
+ }, []);
972
+ if (!mounted) {
973
+ return null;
974
+ }
975
+ const target = container ?? document.body;
976
+ return createPortal(children, target);
977
+ }
978
+ function LauncherTab({ value, children, ...props }) {
979
+ const { activeItem, setActiveItem } = useLauncherContext();
980
+ const isActive = activeItem === value;
981
+ return /* @__PURE__ */ jsx(
982
+ "button",
983
+ {
984
+ type: "button",
985
+ role: "tab",
986
+ "aria-selected": isActive,
987
+ "data-state": isActive ? "active" : "inactive",
988
+ onClick: () => setActiveItem(value),
989
+ ...props,
990
+ children
991
+ }
992
+ );
993
+ }
994
+ function LauncherTabContent({
995
+ value,
996
+ children,
997
+ ...props
998
+ }) {
999
+ const { activeItem } = useLauncherContext();
1000
+ if (activeItem !== value) {
1001
+ return null;
1002
+ }
1003
+ return /* @__PURE__ */ jsx("div", { role: "tabpanel", ...props, children });
1004
+ }
1005
+ function LauncherTrigger({
1006
+ children,
1007
+ asChild = true
1008
+ }) {
1009
+ const { toggle, triggerRef } = useLauncherContext();
1010
+ const refCallback = useCallback(
1011
+ (node) => {
1012
+ triggerRef.current = node;
1013
+ },
1014
+ [triggerRef]
1015
+ );
1016
+ if (asChild) {
1017
+ return cloneElement(children, {
1018
+ ref: refCallback,
1019
+ onClick: (e) => {
1020
+ var _a, _b;
1021
+ (_b = (_a = children.props).onClick) == null ? void 0 : _b.call(_a, e);
1022
+ toggle();
1023
+ }
1024
+ });
1025
+ }
1026
+ return /* @__PURE__ */ jsx("button", { type: "button", ref: refCallback, onClick: toggle, children });
1027
+ }
1028
+ export {
1029
+ AnnouncementRoot as A,
1030
+ FeedbackFormPrimitive as F,
1031
+ LauncherRoot as L,
1032
+ MockTraceyProvider as M,
1033
+ TraceyProvider as T,
1034
+ FeedbackClose as a,
1035
+ FeedbackRoot as b,
1036
+ FeedbackTrigger as c,
1037
+ FeedbackPortal as d,
1038
+ FeedbackContent as e,
1039
+ useAnnouncements as f,
1040
+ AnnouncementPortal as g,
1041
+ AnnouncementContent as h,
1042
+ AnnouncementNavigation as i,
1043
+ AnnouncementClose as j,
1044
+ LauncherTrigger as k,
1045
+ LauncherBadge as l,
1046
+ LauncherPortal as m,
1047
+ LauncherPanel as n,
1048
+ LauncherTab as o,
1049
+ LauncherTabContent as p,
1050
+ useFeedback as q,
1051
+ useTraceyEvents as r,
1052
+ useAnnouncementContext as s,
1053
+ useLauncherContext as t,
1054
+ useTracey as u
1055
+ };
1056
+ //# sourceMappingURL=LauncherTrigger-5pNAvA05.js.map