featuredrop 2.6.1 → 2.7.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,461 @@
1
+ "use client";
2
+ import { createContext, useContext, useRef, useEffect, useMemo, useState, useCallback } from 'react';
3
+
4
+ // src/react/hooks/use-feature-drop.ts
5
+ var FeatureDropContext = createContext(
6
+ null
7
+ );
8
+
9
+ // src/react/hooks/use-feature-drop.ts
10
+ function useFeatureDrop() {
11
+ const context = useContext(FeatureDropContext);
12
+ if (!context) {
13
+ throw new Error(
14
+ "useFeatureDrop must be used within a <FeatureDropProvider>"
15
+ );
16
+ }
17
+ return context;
18
+ }
19
+
20
+ // src/react/hooks/use-new-feature.ts
21
+ function useNewFeature(sidebarKey) {
22
+ const { isNew, getFeature, dismiss } = useFeatureDrop();
23
+ const feature = getFeature(sidebarKey);
24
+ const isNewValue = isNew(sidebarKey);
25
+ return {
26
+ isNew: isNewValue,
27
+ feature,
28
+ dismiss: () => {
29
+ if (feature) {
30
+ dismiss(feature.id);
31
+ }
32
+ }
33
+ };
34
+ }
35
+
36
+ // src/react/hooks/use-new-count.ts
37
+ function useNewCount() {
38
+ const { newCount } = useFeatureDrop();
39
+ return newCount;
40
+ }
41
+ function useTabNotification(options = {}) {
42
+ const {
43
+ enabled = true,
44
+ template = "({count}) {title}",
45
+ flash = false,
46
+ flashInterval = 1500
47
+ } = options;
48
+ const { newCount } = useFeatureDrop();
49
+ const originalTitleRef = useRef("");
50
+ const intervalRef = useRef(null);
51
+ useEffect(() => {
52
+ if (typeof document === "undefined") return;
53
+ if (!originalTitleRef.current) {
54
+ originalTitleRef.current = document.title;
55
+ }
56
+ const originalTitle = originalTitleRef.current;
57
+ if (!enabled || newCount === 0) {
58
+ document.title = originalTitle;
59
+ if (intervalRef.current) {
60
+ clearInterval(intervalRef.current);
61
+ intervalRef.current = null;
62
+ }
63
+ return;
64
+ }
65
+ const notificationTitle = template.replace("{count}", String(newCount)).replace("{title}", originalTitle);
66
+ if (flash) {
67
+ let showNotification = true;
68
+ document.title = notificationTitle;
69
+ intervalRef.current = setInterval(() => {
70
+ showNotification = !showNotification;
71
+ document.title = showNotification ? notificationTitle : originalTitle;
72
+ }, flashInterval);
73
+ } else {
74
+ document.title = notificationTitle;
75
+ }
76
+ return () => {
77
+ document.title = originalTitle;
78
+ if (intervalRef.current) {
79
+ clearInterval(intervalRef.current);
80
+ intervalRef.current = null;
81
+ }
82
+ };
83
+ }, [enabled, newCount, template, flash, flashInterval]);
84
+ }
85
+
86
+ // src/analytics.ts
87
+ function createAdoptionMetrics(events) {
88
+ const getAdoptionRate = (featureId) => {
89
+ const seen = events.filter((event) => event.type === "feature_seen" && event.featureId === featureId).length;
90
+ if (seen === 0) return 0;
91
+ const clicked = events.filter((event) => event.type === "feature_clicked" && event.featureId === featureId).length;
92
+ return clicked / seen;
93
+ };
94
+ const getTourCompletionRate = (tourId) => {
95
+ const started = events.filter((event) => event.type === "tour_started" && event.tourId === tourId).length;
96
+ if (started === 0) return 0;
97
+ const completed = events.filter((event) => event.type === "tour_completed" && event.tourId === tourId).length;
98
+ return completed / started;
99
+ };
100
+ const getChecklistCompletionRate = (checklistId) => {
101
+ const taskCompleted = events.filter(
102
+ (event) => event.type === "checklist_task_completed" && event.metadata?.checklistId === checklistId
103
+ ).length;
104
+ if (taskCompleted === 0) return 0;
105
+ const completed = events.filter(
106
+ (event) => event.type === "checklist_completed" && event.metadata?.checklistId === checklistId
107
+ ).length;
108
+ return completed / taskCompleted;
109
+ };
110
+ const getFeatureEngagement = (featureId) => ({
111
+ seen: events.filter((event) => event.type === "feature_seen" && event.featureId === featureId).length,
112
+ clicked: events.filter((event) => event.type === "feature_clicked" && event.featureId === featureId).length,
113
+ dismissed: events.filter((event) => event.type === "feature_dismissed" && event.featureId === featureId).length
114
+ });
115
+ const getVariantPerformance = (featureId) => {
116
+ const byVariant = /* @__PURE__ */ new Map();
117
+ for (const event of events) {
118
+ if (event.featureId !== featureId) continue;
119
+ const variant = event.variant ?? "control";
120
+ const bucket = byVariant.get(variant) ?? { seen: 0, clicked: 0 };
121
+ if (event.type === "feature_seen") bucket.seen += 1;
122
+ if (event.type === "feature_clicked") bucket.clicked += 1;
123
+ byVariant.set(variant, bucket);
124
+ }
125
+ const output = {};
126
+ for (const [variant, bucket] of byVariant.entries()) {
127
+ output[variant] = bucket.seen === 0 ? 0 : bucket.clicked / bucket.seen;
128
+ }
129
+ return output;
130
+ };
131
+ return {
132
+ getAdoptionRate,
133
+ getTourCompletionRate,
134
+ getChecklistCompletionRate,
135
+ getFeatureEngagement,
136
+ getVariantPerformance
137
+ };
138
+ }
139
+
140
+ // src/react/hooks/use-adoption-analytics.ts
141
+ function useAdoptionAnalytics(events) {
142
+ return useMemo(() => createAdoptionMetrics(events), [events]);
143
+ }
144
+
145
+ // src/react/tour-registry.ts
146
+ var controllers = /* @__PURE__ */ new Map();
147
+ var registryListeners = /* @__PURE__ */ new Map();
148
+ function getTourController(id) {
149
+ return controllers.get(id);
150
+ }
151
+ function subscribeTourRegistry(id, listener) {
152
+ const listeners = registryListeners.get(id) ?? /* @__PURE__ */ new Set();
153
+ listeners.add(listener);
154
+ registryListeners.set(id, listeners);
155
+ return () => {
156
+ const current = registryListeners.get(id);
157
+ if (!current) return;
158
+ current.delete(listener);
159
+ if (current.size === 0) {
160
+ registryListeners.delete(id);
161
+ }
162
+ };
163
+ }
164
+
165
+ // src/react/hooks/use-tour.ts
166
+ var EMPTY_SNAPSHOT = {
167
+ isActive: false,
168
+ currentStepIndex: -1,
169
+ currentStep: null,
170
+ totalSteps: 0
171
+ };
172
+ function readSnapshot(id) {
173
+ const controller = getTourController(id);
174
+ if (!controller) return EMPTY_SNAPSHOT;
175
+ return controller.getSnapshot();
176
+ }
177
+ function useTour(id) {
178
+ const [snapshot, setSnapshot] = useState(() => readSnapshot(id));
179
+ useEffect(() => {
180
+ let unsubscribeController = null;
181
+ const bindController = () => {
182
+ if (unsubscribeController) {
183
+ unsubscribeController();
184
+ unsubscribeController = null;
185
+ }
186
+ const controller = getTourController(id);
187
+ if (!controller) {
188
+ setSnapshot(EMPTY_SNAPSHOT);
189
+ return;
190
+ }
191
+ setSnapshot(controller.getSnapshot());
192
+ unsubscribeController = controller.subscribe(() => {
193
+ setSnapshot(controller.getSnapshot());
194
+ });
195
+ };
196
+ bindController();
197
+ const unsubscribeRegistry = subscribeTourRegistry(id, bindController);
198
+ return () => {
199
+ unsubscribeRegistry();
200
+ if (unsubscribeController) unsubscribeController();
201
+ };
202
+ }, [id]);
203
+ const call = useCallback((method) => {
204
+ const controller = getTourController(id);
205
+ if (!controller) return;
206
+ controller[method]();
207
+ }, [id]);
208
+ return useMemo(
209
+ () => ({
210
+ startTour: () => call("startTour"),
211
+ nextStep: () => call("nextStep"),
212
+ prevStep: () => call("prevStep"),
213
+ skipTour: () => call("skipTour"),
214
+ closeTour: () => call("closeTour"),
215
+ currentStep: snapshot.currentStep,
216
+ currentStepIndex: snapshot.currentStepIndex,
217
+ totalSteps: snapshot.totalSteps,
218
+ isActive: snapshot.isActive
219
+ }),
220
+ [call, snapshot]
221
+ );
222
+ }
223
+ function useTourSequencer(sequence) {
224
+ const {
225
+ newFeatures,
226
+ canShowTour,
227
+ markTourShown,
228
+ markFeatureSeen
229
+ } = useFeatureDrop();
230
+ const [startedFeatureIds, setStartedFeatureIds] = useState(/* @__PURE__ */ new Set());
231
+ const visibleFeatureIds = useMemo(
232
+ () => new Set(newFeatures.map((feature) => feature.id)),
233
+ [newFeatures]
234
+ );
235
+ const remaining = useMemo(
236
+ () => sequence.filter(
237
+ (item) => visibleFeatureIds.has(item.featureId) && !startedFeatureIds.has(item.featureId)
238
+ ),
239
+ [sequence, startedFeatureIds, visibleFeatureIds]
240
+ );
241
+ const next = remaining[0] ?? null;
242
+ const startNextTour = useCallback(() => {
243
+ if (!next) return false;
244
+ if (!canShowTour()) return false;
245
+ const controller = getTourController(next.tourId);
246
+ if (!controller) return false;
247
+ setStartedFeatureIds((previous) => {
248
+ if (previous.has(next.featureId)) return previous;
249
+ const updated = new Set(previous);
250
+ updated.add(next.featureId);
251
+ return updated;
252
+ });
253
+ markFeatureSeen(next.featureId);
254
+ markTourShown();
255
+ controller.startTour();
256
+ return true;
257
+ }, [canShowTour, markFeatureSeen, markTourShown, next]);
258
+ return {
259
+ nextTourId: next?.tourId ?? null,
260
+ nextFeatureId: next?.featureId ?? null,
261
+ remainingTours: remaining.length,
262
+ startNextTour
263
+ };
264
+ }
265
+
266
+ // src/react/checklist-registry.ts
267
+ var controllers2 = /* @__PURE__ */ new Map();
268
+ var registryListeners2 = /* @__PURE__ */ new Map();
269
+ function getChecklistController(id) {
270
+ return controllers2.get(id);
271
+ }
272
+ function subscribeChecklistRegistry(id, listener) {
273
+ const listeners = registryListeners2.get(id) ?? /* @__PURE__ */ new Set();
274
+ listeners.add(listener);
275
+ registryListeners2.set(id, listeners);
276
+ return () => {
277
+ const current = registryListeners2.get(id);
278
+ if (!current) return;
279
+ current.delete(listener);
280
+ if (current.size === 0) registryListeners2.delete(id);
281
+ };
282
+ }
283
+
284
+ // src/react/hooks/use-checklist.ts
285
+ var EMPTY_SNAPSHOT2 = {
286
+ exists: false,
287
+ tasks: [],
288
+ progress: { completed: 0, total: 0, percent: 0 },
289
+ isComplete: false,
290
+ dismissed: false,
291
+ collapsed: false
292
+ };
293
+ function readSnapshot2(id) {
294
+ const controller = getChecklistController(id);
295
+ if (!controller) return EMPTY_SNAPSHOT2;
296
+ return controller.getSnapshot();
297
+ }
298
+ function useChecklist(id) {
299
+ const [snapshot, setSnapshot] = useState(() => readSnapshot2(id));
300
+ useEffect(() => {
301
+ let unsubscribeController = null;
302
+ const bind = () => {
303
+ if (unsubscribeController) {
304
+ unsubscribeController();
305
+ unsubscribeController = null;
306
+ }
307
+ const controller = getChecklistController(id);
308
+ if (!controller) {
309
+ setSnapshot(EMPTY_SNAPSHOT2);
310
+ return;
311
+ }
312
+ setSnapshot(controller.getSnapshot());
313
+ unsubscribeController = controller.subscribe(() => {
314
+ setSnapshot(controller.getSnapshot());
315
+ });
316
+ };
317
+ bind();
318
+ const unsubscribeRegistry = subscribeChecklistRegistry(id, bind);
319
+ return () => {
320
+ unsubscribeRegistry();
321
+ if (unsubscribeController) unsubscribeController();
322
+ };
323
+ }, [id]);
324
+ const invoke = useCallback((method, arg) => {
325
+ const controller = getChecklistController(id);
326
+ if (!controller) return;
327
+ if (method === "completeTask") {
328
+ controller.completeTask(arg ?? "");
329
+ return;
330
+ }
331
+ controller[method]();
332
+ }, [id]);
333
+ return useMemo(
334
+ () => ({
335
+ completeTask: (taskId) => invoke("completeTask", taskId),
336
+ resetChecklist: () => invoke("resetChecklist"),
337
+ dismissChecklist: () => invoke("dismissChecklist"),
338
+ toggleCollapsed: () => invoke("toggleCollapsed"),
339
+ isComplete: snapshot.isComplete,
340
+ progress: snapshot.progress,
341
+ tasks: snapshot.tasks,
342
+ dismissed: snapshot.dismissed,
343
+ collapsed: snapshot.collapsed
344
+ }),
345
+ [invoke, snapshot]
346
+ );
347
+ }
348
+
349
+ // src/react/survey-registry.ts
350
+ var controllers3 = /* @__PURE__ */ new Map();
351
+ var registryListeners3 = /* @__PURE__ */ new Map();
352
+ function getSurveyController(id) {
353
+ return controllers3.get(id);
354
+ }
355
+ function subscribeSurveyRegistry(id, listener) {
356
+ const listeners = registryListeners3.get(id) ?? /* @__PURE__ */ new Set();
357
+ listeners.add(listener);
358
+ registryListeners3.set(id, listeners);
359
+ return () => {
360
+ const current = registryListeners3.get(id);
361
+ if (!current) return;
362
+ current.delete(listener);
363
+ if (current.size === 0) {
364
+ registryListeners3.delete(id);
365
+ }
366
+ };
367
+ }
368
+
369
+ // src/react/hooks/use-survey.ts
370
+ var EMPTY_SNAPSHOT3 = {
371
+ exists: false,
372
+ isOpen: false,
373
+ submitted: false,
374
+ canShow: false,
375
+ type: "custom"
376
+ };
377
+ function readSnapshot3(id) {
378
+ const controller = getSurveyController(id);
379
+ if (!controller) return EMPTY_SNAPSHOT3;
380
+ return controller.getSnapshot();
381
+ }
382
+ function useSurvey(id) {
383
+ const [snapshot, setSnapshot] = useState(() => readSnapshot3(id));
384
+ useEffect(() => {
385
+ let unsubscribeController = null;
386
+ const bind = () => {
387
+ if (unsubscribeController) {
388
+ unsubscribeController();
389
+ unsubscribeController = null;
390
+ }
391
+ const controller = getSurveyController(id);
392
+ if (!controller) {
393
+ setSnapshot(EMPTY_SNAPSHOT3);
394
+ return;
395
+ }
396
+ setSnapshot(controller.getSnapshot());
397
+ unsubscribeController = controller.subscribe(() => {
398
+ setSnapshot(controller.getSnapshot());
399
+ });
400
+ };
401
+ bind();
402
+ const unsubscribeRegistry = subscribeSurveyRegistry(id, bind);
403
+ return () => {
404
+ unsubscribeRegistry();
405
+ if (unsubscribeController) unsubscribeController();
406
+ };
407
+ }, [id]);
408
+ const show = useCallback((options) => {
409
+ const controller = getSurveyController(id);
410
+ if (!controller) return false;
411
+ return controller.show(options);
412
+ }, [id]);
413
+ const hide = useCallback(() => {
414
+ getSurveyController(id)?.hide();
415
+ }, [id]);
416
+ const askLater = useCallback(() => {
417
+ getSurveyController(id)?.askLater();
418
+ }, [id]);
419
+ return useMemo(
420
+ () => ({
421
+ show,
422
+ hide,
423
+ askLater,
424
+ isOpen: snapshot.isOpen,
425
+ submitted: snapshot.submitted,
426
+ canShow: snapshot.canShow,
427
+ type: snapshot.type
428
+ }),
429
+ [askLater, hide, show, snapshot]
430
+ );
431
+ }
432
+ function useChangelog() {
433
+ const ctx = useFeatureDrop();
434
+ const markAllSeen = useCallback(() => {
435
+ void ctx.dismissAll();
436
+ }, [ctx]);
437
+ const getByCategory = useCallback(
438
+ (category) => {
439
+ return ctx.newFeatures.filter((f) => f.category === category);
440
+ },
441
+ [ctx.newFeatures]
442
+ );
443
+ return useMemo(
444
+ () => ({
445
+ features: ctx.manifest,
446
+ newFeatures: ctx.newFeatures,
447
+ newCount: ctx.newCount,
448
+ newFeaturesSorted: ctx.newFeaturesSorted,
449
+ dismiss: ctx.dismiss,
450
+ dismissAll: () => void ctx.dismissAll(),
451
+ isNew: ctx.isNew,
452
+ markAllSeen,
453
+ getByCategory
454
+ }),
455
+ [ctx.manifest, ctx.newFeatures, ctx.newCount, ctx.newFeaturesSorted, ctx.dismiss, ctx.dismissAll, ctx.isNew, markAllSeen, getByCategory]
456
+ );
457
+ }
458
+
459
+ export { useAdoptionAnalytics, useChangelog, useChecklist, useFeatureDrop, useNewCount, useNewFeature, useSurvey, useTabNotification, useTour, useTourSequencer };
460
+ //# sourceMappingURL=react-hooks.js.map
461
+ //# sourceMappingURL=react-hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react/context.ts","../src/react/hooks/use-feature-drop.ts","../src/react/hooks/use-new-feature.ts","../src/react/hooks/use-new-count.ts","../src/react/hooks/use-tab-notification.ts","../src/analytics.ts","../src/react/hooks/use-adoption-analytics.ts","../src/react/tour-registry.ts","../src/react/hooks/use-tour.ts","../src/react/hooks/use-tour-sequencer.ts","../src/react/checklist-registry.ts","../src/react/hooks/use-checklist.ts","../src/react/survey-registry.ts","../src/react/hooks/use-survey.ts","../src/react/hooks/use-changelog.ts"],"names":["useEffect","useMemo","useState","useCallback","controllers","registryListeners","EMPTY_SNAPSHOT","readSnapshot"],"mappings":"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","file":"react-hooks.js","sourcesContent":["import { createContext } from \"react\";\nimport type { FeatureDropAnimationPreset, FeatureDropEngine, FeatureEntry, FeaturePriority } from \"../types\";\nimport type { AdoptionEventInput } from \"../analytics\";\nimport type { FeatureDropTranslations } from \"../i18n\";\n\nexport interface FeatureDropContextValue {\n /** Full manifest provided to the provider */\n manifest: FeatureEntry[] | readonly FeatureEntry[];\n /** All currently \"new\" features */\n newFeatures: FeatureEntry[];\n /** New features currently queued by throttling rules */\n queuedFeatures: FeatureEntry[];\n /** Count of new features */\n newCount: number;\n /** Count before throttling (all pending new features) */\n totalNewCount: number;\n /** All new features sorted by priority then release date */\n newFeaturesSorted: FeatureEntry[];\n /** Check if a sidebar key has any new features */\n isNew: (sidebarKey: string) => boolean;\n /** Dismiss a single feature by ID */\n dismiss: (id: string) => void;\n /** Dismiss all features (marks all as seen) */\n dismissAll: () => Promise<void>;\n /** Get the feature entry for a sidebar key (if it's new) */\n getFeature: (sidebarKey: string) => FeatureEntry | undefined;\n /** Whether quiet mode (Do Not Disturb) is enabled */\n quietMode: boolean;\n /** Enable/disable quiet mode */\n setQuietMode: (enabled: boolean) => void;\n /** Mark a feature as seen for dependency-chain resolution */\n markFeatureSeen: (featureId: string) => void;\n /** Mark a feature as clicked for dependency-chain resolution */\n markFeatureClicked: (featureId: string) => void;\n /** Remaining toasts allowed in this session under throttle rules */\n getRemainingToastSlots: () => number;\n /** Mark toasts as shown in this session */\n markToastsShown: (featureIds: string[]) => void;\n /** Whether a modal can open right now under throttle rules */\n canShowModal: (priority?: FeaturePriority) => boolean;\n /** Record a modal display timestamp */\n markModalShown: () => void;\n /** Whether a tour can start right now under throttle rules */\n canShowTour: () => boolean;\n /** Record a tour start timestamp */\n markTourShown: () => void;\n /** Acquire/release spotlight slots under maxSimultaneousSpotlights */\n acquireSpotlightSlot: (id: string, priority?: FeaturePriority) => boolean;\n releaseSpotlightSlot: (id: string) => void;\n /** Number of currently active spotlight slots */\n activeSpotlightCount: number;\n /** Emit an adoption analytics event (collector-backed when configured) */\n trackAdoptionEvent: (event: AdoptionEventInput) => void;\n /** Report a component/runtime error to provider-level monitoring hooks */\n reportError: (\n error: unknown,\n context?: { component?: string; componentStack?: string },\n ) => void;\n /** Active locale code used by built-in UI strings */\n locale: string;\n /** Text direction derived from locale */\n direction: \"ltr\" | \"rtl\";\n /** Active motion preset for built-in component transitions */\n animation: FeatureDropAnimationPreset;\n /** Resolved translation strings for built-in React components */\n translations: FeatureDropTranslations;\n /** Track a named usage event for trigger rules */\n trackUsageEvent: (event: string, delta?: number) => void;\n /** Track a named trigger event for trigger rules */\n trackTriggerEvent: (event: string) => void;\n /** Mark a milestone for trigger rules */\n trackMilestone: (event: string) => void;\n /** Manually override current path for page trigger rules */\n setTriggerPath: (path: string) => void;\n /** Optional engine instance for AI-powered delivery intelligence */\n engine: FeatureDropEngine | null;\n}\n\nexport const FeatureDropContext = createContext<FeatureDropContextValue | null>(\n null,\n);\n","import { useContext } from \"react\";\nimport { FeatureDropContext } from \"../context\";\nimport type { FeatureDropContextValue } from \"../context\";\n\n/**\n * Access the full feature discovery context.\n *\n * Returns: `{ newFeatures, newCount, isNew, dismiss, dismissAll, getFeature }`\n *\n * @throws Error if used outside of `<FeatureDropProvider>`\n */\nexport function useFeatureDrop(): FeatureDropContextValue {\n const context = useContext(FeatureDropContext);\n if (!context) {\n throw new Error(\n \"useFeatureDrop must be used within a <FeatureDropProvider>\",\n );\n }\n return context;\n}\n","import { useFeatureDrop } from \"./use-feature-drop\";\nimport type { FeatureEntry } from \"../../types\";\n\nexport interface UseNewFeatureResult {\n /** Whether this sidebar key has a new feature */\n isNew: boolean;\n /** The feature entry, if new */\n feature: FeatureEntry | undefined;\n /** Dismiss the feature for this sidebar key */\n dismiss: () => void;\n}\n\n/**\n * Check if a single navigation item has a new feature.\n *\n * @param sidebarKey - The key to check (e.g. \"/journal\", \"settings\")\n * @returns `{ isNew, feature, dismiss }`\n */\nexport function useNewFeature(sidebarKey: string): UseNewFeatureResult {\n const { isNew, getFeature, dismiss } = useFeatureDrop();\n\n const feature = getFeature(sidebarKey);\n const isNewValue = isNew(sidebarKey);\n\n return {\n isNew: isNewValue,\n feature,\n dismiss: () => {\n if (feature) {\n dismiss(feature.id);\n }\n },\n };\n}\n","import { useFeatureDrop } from \"./use-feature-drop\";\n\n/**\n * Get the count of currently new features.\n *\n * Useful for rendering a badge count on a \"What's New\" button.\n *\n * @returns The number of new features\n */\nexport function useNewCount(): number {\n const { newCount } = useFeatureDrop();\n return newCount;\n}\n","import { useEffect, useRef } from \"react\";\nimport { useFeatureDrop } from \"./use-feature-drop\";\n\nexport interface UseTabNotificationOptions {\n /** Whether tab notifications are enabled. Default: true */\n enabled?: boolean;\n /** Template string. `{count}` is replaced with the number. Default: \"({count}) {title}\" */\n template?: string;\n /** Enable flashing/blinking pattern for attention. Default: false */\n flash?: boolean;\n /** Flash interval in ms. Default: 1500 */\n flashInterval?: number;\n}\n\n/**\n * Updates the browser tab title with the unread feature count.\n *\n * Shows \"(3) My App\" when there are new features, restores the original\n * title when all features are read. Optional flash/blink pattern for attention.\n *\n * @example\n * ```tsx\n * function App() {\n * useTabNotification();\n * return <div>...</div>;\n * }\n * ```\n *\n * @example With flash\n * ```tsx\n * useTabNotification({ flash: true, template: \"[{count} new] {title}\" });\n * ```\n */\nexport function useTabNotification(options: UseTabNotificationOptions = {}): void {\n const {\n enabled = true,\n template = \"({count}) {title}\",\n flash = false,\n flashInterval = 1500,\n } = options;\n\n const { newCount } = useFeatureDrop();\n const originalTitleRef = useRef<string>(\"\");\n const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\n\n useEffect(() => {\n if (typeof document === \"undefined\") return;\n\n // Capture original title once\n if (!originalTitleRef.current) {\n originalTitleRef.current = document.title;\n }\n const originalTitle = originalTitleRef.current;\n\n if (!enabled || newCount === 0) {\n // Restore original title\n document.title = originalTitle;\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n return;\n }\n\n const notificationTitle = template\n .replace(\"{count}\", String(newCount))\n .replace(\"{title}\", originalTitle);\n\n if (flash) {\n // Alternate between notification and original title\n let showNotification = true;\n document.title = notificationTitle;\n intervalRef.current = setInterval(() => {\n showNotification = !showNotification;\n document.title = showNotification ? notificationTitle : originalTitle;\n }, flashInterval);\n } else {\n document.title = notificationTitle;\n }\n\n return () => {\n document.title = originalTitle;\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n };\n }, [enabled, newCount, template, flash, flashInterval]);\n}\n","export type AdoptionEventType =\n | \"feature_seen\"\n | \"feature_clicked\"\n | \"feature_dismissed\"\n | \"tour_started\"\n | \"tour_completed\"\n | \"tour_skipped\"\n | \"checklist_task_completed\"\n | \"checklist_completed\"\n | \"survey_submitted\"\n | \"feedback_submitted\"\n | \"announcement_shown\"\n | \"cta_clicked\";\n\nexport interface AdoptionEvent {\n type: AdoptionEventType;\n featureId?: string;\n tourId?: string;\n variant?: string;\n timestamp: string;\n sessionId?: string;\n userId?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport type AdoptionEventInput = Omit<AdoptionEvent, \"timestamp\"> & {\n timestamp?: string;\n};\n\nexport interface AnalyticsAdapter {\n track: (event: AdoptionEvent) => void | Promise<void>;\n trackBatch?: (events: AdoptionEvent[]) => void | Promise<void>;\n}\n\nexport interface AnalyticsCollectorOptions {\n adapter: AnalyticsAdapter;\n batchSize?: number;\n flushInterval?: number;\n sampleRate?: number;\n enabled?: boolean;\n sessionId?: string;\n userId?: string;\n now?: () => Date;\n random?: () => number;\n}\n\nexport class AnalyticsCollector {\n private adapter: AnalyticsAdapter;\n private queue: AdoptionEvent[] = [];\n private batchSize: number;\n private flushInterval: number;\n private sampleRate: number;\n private enabled: boolean;\n private now: () => Date;\n private random: () => number;\n private sessionId?: string;\n private userId?: string;\n private timer: ReturnType<typeof setInterval> | null = null;\n private flushing = false;\n\n constructor(options: AnalyticsCollectorOptions) {\n this.adapter = options.adapter;\n this.batchSize = options.batchSize ?? 20;\n this.flushInterval = options.flushInterval ?? 10_000;\n this.sampleRate = options.sampleRate ?? 1;\n this.enabled = options.enabled ?? true;\n this.sessionId = options.sessionId;\n this.userId = options.userId;\n this.now = options.now ?? (() => new Date());\n this.random = options.random ?? Math.random;\n this.startTimer();\n }\n\n setEnabled(enabled: boolean): void {\n this.enabled = enabled;\n }\n\n setContext(context: { sessionId?: string; userId?: string }): void {\n if (context.sessionId !== undefined) this.sessionId = context.sessionId;\n if (context.userId !== undefined) this.userId = context.userId;\n }\n\n getQueueSize(): number {\n return this.queue.length;\n }\n\n track(event: AdoptionEventInput): void {\n if (!this.enabled) return;\n if (this.sampleRate < 1 && this.random() > this.sampleRate) return;\n const normalized: AdoptionEvent = {\n ...event,\n timestamp: event.timestamp ?? this.now().toISOString(),\n sessionId: event.sessionId ?? this.sessionId,\n userId: event.userId ?? this.userId,\n };\n this.queue.push(normalized);\n if (this.queue.length >= this.batchSize) {\n void this.flush();\n }\n }\n\n async flush(): Promise<void> {\n if (this.flushing) return;\n if (this.queue.length === 0) return;\n this.flushing = true;\n const batch = this.queue.splice(0, this.queue.length);\n try {\n if (this.adapter.trackBatch) {\n await this.adapter.trackBatch(batch);\n } else {\n for (const event of batch) {\n await this.adapter.track(event);\n }\n }\n } catch {\n // Requeue on transient failures.\n this.queue = [...batch, ...this.queue];\n } finally {\n this.flushing = false;\n }\n }\n\n async destroy(): Promise<void> {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n await this.flush();\n }\n\n private startTimer(): void {\n if (this.flushInterval <= 0) return;\n this.timer = setInterval(() => {\n void this.flush();\n }, this.flushInterval);\n }\n}\n\nexport class PostHogAdapter implements AnalyticsAdapter {\n constructor(\n private readonly client: {\n capture: (event: string, properties?: Record<string, unknown>) => void;\n },\n ) {}\n\n track(event: AdoptionEvent): void {\n this.client.capture(event.type, {\n featureId: event.featureId,\n tourId: event.tourId,\n variant: event.variant,\n timestamp: event.timestamp,\n sessionId: event.sessionId,\n userId: event.userId,\n ...event.metadata,\n });\n }\n}\n\nexport class AmplitudeAdapter implements AnalyticsAdapter {\n constructor(\n private readonly client: {\n track: (event: string, properties?: Record<string, unknown>) => void;\n },\n ) {}\n\n track(event: AdoptionEvent): void {\n this.client.track(event.type, {\n featureId: event.featureId,\n tourId: event.tourId,\n variant: event.variant,\n timestamp: event.timestamp,\n sessionId: event.sessionId,\n userId: event.userId,\n ...event.metadata,\n });\n }\n}\n\nexport class MixpanelAdapter implements AnalyticsAdapter {\n constructor(\n private readonly client: {\n track: (event: string, properties?: Record<string, unknown>) => void;\n },\n ) {}\n\n track(event: AdoptionEvent): void {\n this.client.track(event.type, {\n featureId: event.featureId,\n tourId: event.tourId,\n variant: event.variant,\n timestamp: event.timestamp,\n sessionId: event.sessionId,\n userId: event.userId,\n ...event.metadata,\n });\n }\n}\n\nexport class SegmentAdapter implements AnalyticsAdapter {\n constructor(\n private readonly client: {\n track: (event: string, properties?: Record<string, unknown>) => void;\n },\n ) {}\n\n track(event: AdoptionEvent): void {\n this.client.track(event.type, {\n featureId: event.featureId,\n tourId: event.tourId,\n variant: event.variant,\n timestamp: event.timestamp,\n sessionId: event.sessionId,\n userId: event.userId,\n ...event.metadata,\n });\n }\n}\n\nexport class CustomAdapter implements AnalyticsAdapter {\n constructor(private readonly handler: (event: AdoptionEvent) => void | Promise<void>) {}\n\n track(event: AdoptionEvent): void | Promise<void> {\n return this.handler(event);\n }\n}\n\nexport interface FeatureEngagementMetrics {\n seen: number;\n clicked: number;\n dismissed: number;\n}\n\nexport interface AdoptionMetrics {\n getAdoptionRate: (featureId: string) => number;\n getTourCompletionRate: (tourId: string) => number;\n getChecklistCompletionRate: (checklistId: string) => number;\n getFeatureEngagement: (featureId: string) => FeatureEngagementMetrics;\n getVariantPerformance: (featureId: string) => Record<string, number>;\n}\n\nexport function createAdoptionMetrics(events: AdoptionEvent[]): AdoptionMetrics {\n const getAdoptionRate = (featureId: string): number => {\n const seen = events.filter((event) => event.type === \"feature_seen\" && event.featureId === featureId).length;\n if (seen === 0) return 0;\n const clicked = events.filter((event) => event.type === \"feature_clicked\" && event.featureId === featureId).length;\n return clicked / seen;\n };\n\n const getTourCompletionRate = (tourId: string): number => {\n const started = events.filter((event) => event.type === \"tour_started\" && event.tourId === tourId).length;\n if (started === 0) return 0;\n const completed = events.filter((event) => event.type === \"tour_completed\" && event.tourId === tourId).length;\n return completed / started;\n };\n\n const getChecklistCompletionRate = (checklistId: string): number => {\n const taskCompleted = events.filter((event) =>\n event.type === \"checklist_task_completed\" &&\n event.metadata?.checklistId === checklistId\n ).length;\n if (taskCompleted === 0) return 0;\n const completed = events.filter((event) =>\n event.type === \"checklist_completed\" &&\n event.metadata?.checklistId === checklistId\n ).length;\n return completed / taskCompleted;\n };\n\n const getFeatureEngagement = (featureId: string): FeatureEngagementMetrics => ({\n seen: events.filter((event) => event.type === \"feature_seen\" && event.featureId === featureId).length,\n clicked: events.filter((event) => event.type === \"feature_clicked\" && event.featureId === featureId).length,\n dismissed: events.filter((event) => event.type === \"feature_dismissed\" && event.featureId === featureId).length,\n });\n\n const getVariantPerformance = (featureId: string): Record<string, number> => {\n const byVariant = new Map<string, { seen: number; clicked: number }>();\n for (const event of events) {\n if (event.featureId !== featureId) continue;\n const variant = event.variant ?? \"control\";\n const bucket = byVariant.get(variant) ?? { seen: 0, clicked: 0 };\n if (event.type === \"feature_seen\") bucket.seen += 1;\n if (event.type === \"feature_clicked\") bucket.clicked += 1;\n byVariant.set(variant, bucket);\n }\n const output: Record<string, number> = {};\n for (const [variant, bucket] of byVariant.entries()) {\n output[variant] = bucket.seen === 0 ? 0 : bucket.clicked / bucket.seen;\n }\n return output;\n };\n\n return {\n getAdoptionRate,\n getTourCompletionRate,\n getChecklistCompletionRate,\n getFeatureEngagement,\n getVariantPerformance,\n };\n}\n","import { useMemo } from \"react\";\nimport type { AdoptionEvent } from \"../../analytics\";\nimport { createAdoptionMetrics, type AdoptionMetrics } from \"../../analytics\";\n\nexport function useAdoptionAnalytics(events: AdoptionEvent[]): AdoptionMetrics {\n return useMemo(() => createAdoptionMetrics(events), [events]);\n}\n","import type { TourStep } from \"./components/tour\";\n\nexport interface TourSnapshot {\n isActive: boolean;\n currentStepIndex: number;\n currentStep: TourStep | null;\n totalSteps: number;\n}\n\nexport interface TourController {\n startTour: () => void;\n nextStep: () => void;\n prevStep: () => void;\n skipTour: () => void;\n closeTour: () => void;\n getSnapshot: () => TourSnapshot;\n subscribe: (listener: () => void) => () => void;\n}\n\nconst controllers = new Map<string, TourController>();\nconst registryListeners = new Map<string, Set<() => void>>();\n\nfunction emitRegistry(id: string): void {\n const listeners = registryListeners.get(id);\n if (!listeners) return;\n for (const listener of listeners) listener();\n}\n\nexport function registerTourController(id: string, controller: TourController): () => void {\n controllers.set(id, controller);\n emitRegistry(id);\n return () => {\n if (controllers.get(id) === controller) {\n controllers.delete(id);\n emitRegistry(id);\n }\n };\n}\n\nexport function getTourController(id: string): TourController | undefined {\n return controllers.get(id);\n}\n\nexport function subscribeTourRegistry(id: string, listener: () => void): () => void {\n const listeners = registryListeners.get(id) ?? new Set<() => void>();\n listeners.add(listener);\n registryListeners.set(id, listeners);\n return () => {\n const current = registryListeners.get(id);\n if (!current) return;\n current.delete(listener);\n if (current.size === 0) {\n registryListeners.delete(id);\n }\n };\n}\n","import { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { getTourController, subscribeTourRegistry, type TourSnapshot } from \"../tour-registry\";\n\nconst EMPTY_SNAPSHOT: TourSnapshot = {\n isActive: false,\n currentStepIndex: -1,\n currentStep: null,\n totalSteps: 0,\n};\n\nexport interface UseTourResult {\n startTour: () => void;\n nextStep: () => void;\n prevStep: () => void;\n skipTour: () => void;\n closeTour: () => void;\n currentStep: TourSnapshot[\"currentStep\"];\n currentStepIndex: number;\n totalSteps: number;\n isActive: boolean;\n}\n\nfunction readSnapshot(id: string): TourSnapshot {\n const controller = getTourController(id);\n if (!controller) return EMPTY_SNAPSHOT;\n return controller.getSnapshot();\n}\n\nexport function useTour(id: string): UseTourResult {\n const [snapshot, setSnapshot] = useState<TourSnapshot>(() => readSnapshot(id));\n\n useEffect(() => {\n let unsubscribeController: (() => void) | null = null;\n\n const bindController = (): void => {\n if (unsubscribeController) {\n unsubscribeController();\n unsubscribeController = null;\n }\n const controller = getTourController(id);\n if (!controller) {\n setSnapshot(EMPTY_SNAPSHOT);\n return;\n }\n setSnapshot(controller.getSnapshot());\n unsubscribeController = controller.subscribe(() => {\n setSnapshot(controller.getSnapshot());\n });\n };\n\n bindController();\n const unsubscribeRegistry = subscribeTourRegistry(id, bindController);\n return () => {\n unsubscribeRegistry();\n if (unsubscribeController) unsubscribeController();\n };\n }, [id]);\n\n const call = useCallback((method: keyof Omit<UseTourResult, \"currentStep\" | \"currentStepIndex\" | \"totalSteps\" | \"isActive\">) => {\n const controller = getTourController(id);\n if (!controller) return;\n controller[method]();\n }, [id]);\n\n return useMemo(\n () => ({\n startTour: () => call(\"startTour\"),\n nextStep: () => call(\"nextStep\"),\n prevStep: () => call(\"prevStep\"),\n skipTour: () => call(\"skipTour\"),\n closeTour: () => call(\"closeTour\"),\n currentStep: snapshot.currentStep,\n currentStepIndex: snapshot.currentStepIndex,\n totalSteps: snapshot.totalSteps,\n isActive: snapshot.isActive,\n }),\n [call, snapshot],\n );\n}\n","import { useCallback, useMemo, useState } from \"react\";\nimport { useFeatureDrop } from \"./use-feature-drop\";\nimport { getTourController } from \"../tour-registry\";\n\nexport interface TourSequenceItem {\n featureId: string;\n tourId: string;\n}\n\nexport interface UseTourSequencerResult {\n nextTourId: string | null;\n nextFeatureId: string | null;\n remainingTours: number;\n startNextTour: () => boolean;\n}\n\nexport function useTourSequencer(sequence: TourSequenceItem[]): UseTourSequencerResult {\n const {\n newFeatures,\n canShowTour,\n markTourShown,\n markFeatureSeen,\n } = useFeatureDrop();\n const [startedFeatureIds, setStartedFeatureIds] = useState<Set<string>>(new Set());\n\n const visibleFeatureIds = useMemo(\n () => new Set(newFeatures.map((feature) => feature.id)),\n [newFeatures],\n );\n\n const remaining = useMemo(\n () =>\n sequence.filter(\n (item) =>\n visibleFeatureIds.has(item.featureId) &&\n !startedFeatureIds.has(item.featureId),\n ),\n [sequence, startedFeatureIds, visibleFeatureIds],\n );\n\n const next = remaining[0] ?? null;\n\n const startNextTour = useCallback((): boolean => {\n if (!next) return false;\n if (!canShowTour()) return false;\n const controller = getTourController(next.tourId);\n if (!controller) return false;\n setStartedFeatureIds((previous) => {\n if (previous.has(next.featureId)) return previous;\n const updated = new Set(previous);\n updated.add(next.featureId);\n return updated;\n });\n markFeatureSeen(next.featureId);\n markTourShown();\n controller.startTour();\n return true;\n }, [canShowTour, markFeatureSeen, markTourShown, next]);\n\n return {\n nextTourId: next?.tourId ?? null,\n nextFeatureId: next?.featureId ?? null,\n remainingTours: remaining.length,\n startNextTour,\n };\n}\n","export interface ChecklistSnapshot {\n exists: boolean;\n tasks: Array<{ id: string; completed: boolean }>;\n progress: { completed: number; total: number; percent: number };\n isComplete: boolean;\n dismissed: boolean;\n collapsed: boolean;\n}\n\nexport interface ChecklistController {\n completeTask: (taskId: string) => void;\n resetChecklist: () => void;\n dismissChecklist: () => void;\n toggleCollapsed: () => void;\n getSnapshot: () => ChecklistSnapshot;\n subscribe: (listener: () => void) => () => void;\n}\n\nconst controllers = new Map<string, ChecklistController>();\nconst registryListeners = new Map<string, Set<() => void>>();\n\nfunction emitRegistry(id: string): void {\n const listeners = registryListeners.get(id);\n if (!listeners) return;\n for (const listener of listeners) listener();\n}\n\nexport function registerChecklistController(id: string, controller: ChecklistController): () => void {\n controllers.set(id, controller);\n emitRegistry(id);\n return () => {\n if (controllers.get(id) === controller) {\n controllers.delete(id);\n emitRegistry(id);\n }\n };\n}\n\nexport function getChecklistController(id: string): ChecklistController | undefined {\n return controllers.get(id);\n}\n\nexport function subscribeChecklistRegistry(id: string, listener: () => void): () => void {\n const listeners = registryListeners.get(id) ?? new Set<() => void>();\n listeners.add(listener);\n registryListeners.set(id, listeners);\n return () => {\n const current = registryListeners.get(id);\n if (!current) return;\n current.delete(listener);\n if (current.size === 0) registryListeners.delete(id);\n };\n}\n","import { useCallback, useEffect, useMemo, useState } from \"react\";\nimport {\n getChecklistController,\n subscribeChecklistRegistry,\n type ChecklistSnapshot,\n} from \"../checklist-registry\";\n\nconst EMPTY_SNAPSHOT: ChecklistSnapshot = {\n exists: false,\n tasks: [],\n progress: { completed: 0, total: 0, percent: 0 },\n isComplete: false,\n dismissed: false,\n collapsed: false,\n};\n\nexport interface UseChecklistResult {\n completeTask: (taskId: string) => void;\n resetChecklist: () => void;\n dismissChecklist: () => void;\n toggleCollapsed: () => void;\n isComplete: boolean;\n progress: { completed: number; total: number; percent: number };\n tasks: Array<{ id: string; completed: boolean }>;\n dismissed: boolean;\n collapsed: boolean;\n}\n\nfunction readSnapshot(id: string): ChecklistSnapshot {\n const controller = getChecklistController(id);\n if (!controller) return EMPTY_SNAPSHOT;\n return controller.getSnapshot();\n}\n\nexport function useChecklist(id: string): UseChecklistResult {\n const [snapshot, setSnapshot] = useState<ChecklistSnapshot>(() => readSnapshot(id));\n\n useEffect(() => {\n let unsubscribeController: (() => void) | null = null;\n\n const bind = (): void => {\n if (unsubscribeController) {\n unsubscribeController();\n unsubscribeController = null;\n }\n const controller = getChecklistController(id);\n if (!controller) {\n setSnapshot(EMPTY_SNAPSHOT);\n return;\n }\n setSnapshot(controller.getSnapshot());\n unsubscribeController = controller.subscribe(() => {\n setSnapshot(controller.getSnapshot());\n });\n };\n\n bind();\n const unsubscribeRegistry = subscribeChecklistRegistry(id, bind);\n return () => {\n unsubscribeRegistry();\n if (unsubscribeController) unsubscribeController();\n };\n }, [id]);\n\n const invoke = useCallback((method: \"completeTask\" | \"resetChecklist\" | \"dismissChecklist\" | \"toggleCollapsed\", arg?: string) => {\n const controller = getChecklistController(id);\n if (!controller) return;\n if (method === \"completeTask\") {\n controller.completeTask(arg ?? \"\");\n return;\n }\n controller[method]();\n }, [id]);\n\n return useMemo(\n () => ({\n completeTask: (taskId: string) => invoke(\"completeTask\", taskId),\n resetChecklist: () => invoke(\"resetChecklist\"),\n dismissChecklist: () => invoke(\"dismissChecklist\"),\n toggleCollapsed: () => invoke(\"toggleCollapsed\"),\n isComplete: snapshot.isComplete,\n progress: snapshot.progress,\n tasks: snapshot.tasks,\n dismissed: snapshot.dismissed,\n collapsed: snapshot.collapsed,\n }),\n [invoke, snapshot],\n );\n}\n","import type { SurveyType } from \"./components/survey\";\n\nexport interface SurveySnapshot {\n exists: boolean;\n isOpen: boolean;\n submitted: boolean;\n canShow: boolean;\n type: SurveyType;\n}\n\nexport interface SurveyController {\n show: (options?: { force?: boolean }) => boolean;\n hide: () => void;\n askLater: () => void;\n getSnapshot: () => SurveySnapshot;\n subscribe: (listener: () => void) => () => void;\n}\n\nconst controllers = new Map<string, SurveyController>();\nconst registryListeners = new Map<string, Set<() => void>>();\n\nfunction emitRegistry(id: string): void {\n const listeners = registryListeners.get(id);\n if (!listeners) return;\n for (const listener of listeners) listener();\n}\n\nexport function registerSurveyController(id: string, controller: SurveyController): () => void {\n controllers.set(id, controller);\n emitRegistry(id);\n return () => {\n if (controllers.get(id) === controller) {\n controllers.delete(id);\n emitRegistry(id);\n }\n };\n}\n\nexport function getSurveyController(id: string): SurveyController | undefined {\n return controllers.get(id);\n}\n\nexport function subscribeSurveyRegistry(id: string, listener: () => void): () => void {\n const listeners = registryListeners.get(id) ?? new Set<() => void>();\n listeners.add(listener);\n registryListeners.set(id, listeners);\n return () => {\n const current = registryListeners.get(id);\n if (!current) return;\n current.delete(listener);\n if (current.size === 0) {\n registryListeners.delete(id);\n }\n };\n}\n","import { useCallback, useEffect, useMemo, useState } from \"react\";\nimport {\n getSurveyController,\n subscribeSurveyRegistry,\n type SurveySnapshot,\n} from \"../survey-registry\";\nimport type { SurveyType } from \"../components/survey\";\n\nconst EMPTY_SNAPSHOT: SurveySnapshot = {\n exists: false,\n isOpen: false,\n submitted: false,\n canShow: false,\n type: \"custom\",\n};\n\nexport interface UseSurveyResult {\n show: (options?: { force?: boolean }) => boolean;\n hide: () => void;\n askLater: () => void;\n isOpen: boolean;\n submitted: boolean;\n canShow: boolean;\n type: SurveyType;\n}\n\nfunction readSnapshot(id: string): SurveySnapshot {\n const controller = getSurveyController(id);\n if (!controller) return EMPTY_SNAPSHOT;\n return controller.getSnapshot();\n}\n\nexport function useSurvey(id: string): UseSurveyResult {\n const [snapshot, setSnapshot] = useState<SurveySnapshot>(() => readSnapshot(id));\n\n useEffect(() => {\n let unsubscribeController: (() => void) | null = null;\n\n const bind = (): void => {\n if (unsubscribeController) {\n unsubscribeController();\n unsubscribeController = null;\n }\n const controller = getSurveyController(id);\n if (!controller) {\n setSnapshot(EMPTY_SNAPSHOT);\n return;\n }\n setSnapshot(controller.getSnapshot());\n unsubscribeController = controller.subscribe(() => {\n setSnapshot(controller.getSnapshot());\n });\n };\n\n bind();\n const unsubscribeRegistry = subscribeSurveyRegistry(id, bind);\n return () => {\n unsubscribeRegistry();\n if (unsubscribeController) unsubscribeController();\n };\n }, [id]);\n\n const show = useCallback((options?: { force?: boolean }): boolean => {\n const controller = getSurveyController(id);\n if (!controller) return false;\n return controller.show(options);\n }, [id]);\n\n const hide = useCallback(() => {\n getSurveyController(id)?.hide();\n }, [id]);\n\n const askLater = useCallback(() => {\n getSurveyController(id)?.askLater();\n }, [id]);\n\n return useMemo(\n () => ({\n show,\n hide,\n askLater,\n isOpen: snapshot.isOpen,\n submitted: snapshot.submitted,\n canShow: snapshot.canShow,\n type: snapshot.type,\n }),\n [askLater, hide, show, snapshot],\n );\n}\n","import { useCallback, useMemo } from \"react\";\nimport { useFeatureDrop } from \"./use-feature-drop\";\nimport type { FeatureEntry } from \"../../types\";\n\nexport interface UseChangelogResult {\n /** All features from the manifest (including non-new) */\n features: readonly FeatureEntry[];\n /** Only features that are currently \"new\" (unread) */\n newFeatures: readonly FeatureEntry[];\n /** Count of new/unread features */\n newCount: number;\n /** Sorted new features (critical first, then by date) */\n newFeaturesSorted: readonly FeatureEntry[];\n /** Dismiss a single feature by ID */\n dismiss: (id: string) => void;\n /** Dismiss all features at once */\n dismissAll: () => void;\n /** Check if a specific feature is new */\n isNew: (sidebarKey: string) => boolean;\n /** Mark all currently visible features as seen (advances watermark) */\n markAllSeen: () => void;\n /** Get features filtered by category */\n getByCategory: (category: string) => readonly FeatureEntry[];\n}\n\nexport function useChangelog(): UseChangelogResult {\n const ctx = useFeatureDrop();\n\n const markAllSeen = useCallback(() => {\n void ctx.dismissAll();\n }, [ctx]);\n\n const getByCategory = useCallback(\n (category: string): readonly FeatureEntry[] => {\n return ctx.newFeatures.filter((f) => f.category === category);\n },\n [ctx.newFeatures],\n );\n\n return useMemo(\n () => ({\n features: ctx.manifest,\n newFeatures: ctx.newFeatures,\n newCount: ctx.newCount,\n newFeaturesSorted: ctx.newFeaturesSorted,\n dismiss: ctx.dismiss,\n dismissAll: () => void ctx.dismissAll(),\n isNew: ctx.isNew,\n markAllSeen,\n getByCategory,\n }),\n [ctx.manifest, ctx.newFeatures, ctx.newCount, ctx.newFeaturesSorted, ctx.dismiss, ctx.dismissAll, ctx.isNew, markAllSeen, getByCategory],\n );\n}\n"]}
package/dist/react.cjs CHANGED
@@ -1100,6 +1100,7 @@ function FeatureDropProvider({
1100
1100
  locale = "en",
1101
1101
  animation = "normal",
1102
1102
  translations: translationOverrides,
1103
+ engine,
1103
1104
  children
1104
1105
  }) {
1105
1106
  const analyticsRef = react.useRef(analytics);
@@ -1156,20 +1157,20 @@ function FeatureDropProvider({
1156
1157
  seenFeatureIds: readIdSet(SEEN_FEATURES_STORAGE_KEY),
1157
1158
  clickedFeatureIds: readIdSet(CLICKED_FEATURES_STORAGE_KEY),
1158
1159
  triggerContext: (() => {
1159
- const engine = triggerEngineRef.current;
1160
- if (!engine) return void 0;
1161
- engine.setElapsedMs(Date.now() - sessionStartedAtRef.current);
1162
- return engine.getContext();
1160
+ const engine2 = triggerEngineRef.current;
1161
+ if (!engine2) return void 0;
1162
+ engine2.setElapsedMs(Date.now() - sessionStartedAtRef.current);
1163
+ return engine2.getContext();
1163
1164
  })(),
1164
1165
  flagBridge
1165
1166
  })
1166
1167
  );
1167
1168
  const recompute = react.useCallback(() => {
1168
- const engine = triggerEngineRef.current;
1169
+ const engine2 = triggerEngineRef.current;
1169
1170
  let triggerContext;
1170
- if (engine) {
1171
- engine.setElapsedMs(Date.now() - sessionStartedAtRef.current);
1172
- triggerContext = engine.getContext();
1171
+ if (engine2) {
1172
+ engine2.setElapsedMs(Date.now() - sessionStartedAtRef.current);
1173
+ triggerContext = engine2.getContext();
1173
1174
  }
1174
1175
  setFeatureState(
1175
1176
  computeFeatureState({
@@ -1206,6 +1207,12 @@ function FeatureDropProvider({
1206
1207
  react.useEffect(() => {
1207
1208
  recompute();
1208
1209
  }, [recompute]);
1210
+ react.useEffect(() => {
1211
+ engine?.initialize?.();
1212
+ return () => {
1213
+ engine?.destroy?.();
1214
+ };
1215
+ }, [engine]);
1209
1216
  const hasTimeTriggers = react.useMemo(
1210
1217
  () => resolvedManifest.some((feature) => feature.trigger?.type === "time"),
1211
1218
  [resolvedManifest]
@@ -1488,7 +1495,8 @@ function FeatureDropProvider({
1488
1495
  trackUsageEvent,
1489
1496
  trackTriggerEvent,
1490
1497
  trackMilestone,
1491
- setTriggerPath
1498
+ setTriggerPath,
1499
+ engine: engine ?? null
1492
1500
  }),
1493
1501
  [
1494
1502
  resolvedManifest,
@@ -1522,7 +1530,8 @@ function FeatureDropProvider({
1522
1530
  trackUsageEvent,
1523
1531
  trackTriggerEvent,
1524
1532
  trackMilestone,
1525
- setTriggerPath
1533
+ setTriggerPath,
1534
+ engine
1526
1535
  ]
1527
1536
  );
1528
1537
  return /* @__PURE__ */ jsxRuntime.jsx(FeatureDropContext.Provider, { value, children });