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,472 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ var react = require('react');
5
+
6
+ // src/react/hooks/use-feature-drop.ts
7
+ var FeatureDropContext = react.createContext(
8
+ null
9
+ );
10
+
11
+ // src/react/hooks/use-feature-drop.ts
12
+ function useFeatureDrop() {
13
+ const context = react.useContext(FeatureDropContext);
14
+ if (!context) {
15
+ throw new Error(
16
+ "useFeatureDrop must be used within a <FeatureDropProvider>"
17
+ );
18
+ }
19
+ return context;
20
+ }
21
+
22
+ // src/react/hooks/use-new-feature.ts
23
+ function useNewFeature(sidebarKey) {
24
+ const { isNew, getFeature, dismiss } = useFeatureDrop();
25
+ const feature = getFeature(sidebarKey);
26
+ const isNewValue = isNew(sidebarKey);
27
+ return {
28
+ isNew: isNewValue,
29
+ feature,
30
+ dismiss: () => {
31
+ if (feature) {
32
+ dismiss(feature.id);
33
+ }
34
+ }
35
+ };
36
+ }
37
+
38
+ // src/react/hooks/use-new-count.ts
39
+ function useNewCount() {
40
+ const { newCount } = useFeatureDrop();
41
+ return newCount;
42
+ }
43
+ function useTabNotification(options = {}) {
44
+ const {
45
+ enabled = true,
46
+ template = "({count}) {title}",
47
+ flash = false,
48
+ flashInterval = 1500
49
+ } = options;
50
+ const { newCount } = useFeatureDrop();
51
+ const originalTitleRef = react.useRef("");
52
+ const intervalRef = react.useRef(null);
53
+ react.useEffect(() => {
54
+ if (typeof document === "undefined") return;
55
+ if (!originalTitleRef.current) {
56
+ originalTitleRef.current = document.title;
57
+ }
58
+ const originalTitle = originalTitleRef.current;
59
+ if (!enabled || newCount === 0) {
60
+ document.title = originalTitle;
61
+ if (intervalRef.current) {
62
+ clearInterval(intervalRef.current);
63
+ intervalRef.current = null;
64
+ }
65
+ return;
66
+ }
67
+ const notificationTitle = template.replace("{count}", String(newCount)).replace("{title}", originalTitle);
68
+ if (flash) {
69
+ let showNotification = true;
70
+ document.title = notificationTitle;
71
+ intervalRef.current = setInterval(() => {
72
+ showNotification = !showNotification;
73
+ document.title = showNotification ? notificationTitle : originalTitle;
74
+ }, flashInterval);
75
+ } else {
76
+ document.title = notificationTitle;
77
+ }
78
+ return () => {
79
+ document.title = originalTitle;
80
+ if (intervalRef.current) {
81
+ clearInterval(intervalRef.current);
82
+ intervalRef.current = null;
83
+ }
84
+ };
85
+ }, [enabled, newCount, template, flash, flashInterval]);
86
+ }
87
+
88
+ // src/analytics.ts
89
+ function createAdoptionMetrics(events) {
90
+ const getAdoptionRate = (featureId) => {
91
+ const seen = events.filter((event) => event.type === "feature_seen" && event.featureId === featureId).length;
92
+ if (seen === 0) return 0;
93
+ const clicked = events.filter((event) => event.type === "feature_clicked" && event.featureId === featureId).length;
94
+ return clicked / seen;
95
+ };
96
+ const getTourCompletionRate = (tourId) => {
97
+ const started = events.filter((event) => event.type === "tour_started" && event.tourId === tourId).length;
98
+ if (started === 0) return 0;
99
+ const completed = events.filter((event) => event.type === "tour_completed" && event.tourId === tourId).length;
100
+ return completed / started;
101
+ };
102
+ const getChecklistCompletionRate = (checklistId) => {
103
+ const taskCompleted = events.filter(
104
+ (event) => event.type === "checklist_task_completed" && event.metadata?.checklistId === checklistId
105
+ ).length;
106
+ if (taskCompleted === 0) return 0;
107
+ const completed = events.filter(
108
+ (event) => event.type === "checklist_completed" && event.metadata?.checklistId === checklistId
109
+ ).length;
110
+ return completed / taskCompleted;
111
+ };
112
+ const getFeatureEngagement = (featureId) => ({
113
+ seen: events.filter((event) => event.type === "feature_seen" && event.featureId === featureId).length,
114
+ clicked: events.filter((event) => event.type === "feature_clicked" && event.featureId === featureId).length,
115
+ dismissed: events.filter((event) => event.type === "feature_dismissed" && event.featureId === featureId).length
116
+ });
117
+ const getVariantPerformance = (featureId) => {
118
+ const byVariant = /* @__PURE__ */ new Map();
119
+ for (const event of events) {
120
+ if (event.featureId !== featureId) continue;
121
+ const variant = event.variant ?? "control";
122
+ const bucket = byVariant.get(variant) ?? { seen: 0, clicked: 0 };
123
+ if (event.type === "feature_seen") bucket.seen += 1;
124
+ if (event.type === "feature_clicked") bucket.clicked += 1;
125
+ byVariant.set(variant, bucket);
126
+ }
127
+ const output = {};
128
+ for (const [variant, bucket] of byVariant.entries()) {
129
+ output[variant] = bucket.seen === 0 ? 0 : bucket.clicked / bucket.seen;
130
+ }
131
+ return output;
132
+ };
133
+ return {
134
+ getAdoptionRate,
135
+ getTourCompletionRate,
136
+ getChecklistCompletionRate,
137
+ getFeatureEngagement,
138
+ getVariantPerformance
139
+ };
140
+ }
141
+
142
+ // src/react/hooks/use-adoption-analytics.ts
143
+ function useAdoptionAnalytics(events) {
144
+ return react.useMemo(() => createAdoptionMetrics(events), [events]);
145
+ }
146
+
147
+ // src/react/tour-registry.ts
148
+ var controllers = /* @__PURE__ */ new Map();
149
+ var registryListeners = /* @__PURE__ */ new Map();
150
+ function getTourController(id) {
151
+ return controllers.get(id);
152
+ }
153
+ function subscribeTourRegistry(id, listener) {
154
+ const listeners = registryListeners.get(id) ?? /* @__PURE__ */ new Set();
155
+ listeners.add(listener);
156
+ registryListeners.set(id, listeners);
157
+ return () => {
158
+ const current = registryListeners.get(id);
159
+ if (!current) return;
160
+ current.delete(listener);
161
+ if (current.size === 0) {
162
+ registryListeners.delete(id);
163
+ }
164
+ };
165
+ }
166
+
167
+ // src/react/hooks/use-tour.ts
168
+ var EMPTY_SNAPSHOT = {
169
+ isActive: false,
170
+ currentStepIndex: -1,
171
+ currentStep: null,
172
+ totalSteps: 0
173
+ };
174
+ function readSnapshot(id) {
175
+ const controller = getTourController(id);
176
+ if (!controller) return EMPTY_SNAPSHOT;
177
+ return controller.getSnapshot();
178
+ }
179
+ function useTour(id) {
180
+ const [snapshot, setSnapshot] = react.useState(() => readSnapshot(id));
181
+ react.useEffect(() => {
182
+ let unsubscribeController = null;
183
+ const bindController = () => {
184
+ if (unsubscribeController) {
185
+ unsubscribeController();
186
+ unsubscribeController = null;
187
+ }
188
+ const controller = getTourController(id);
189
+ if (!controller) {
190
+ setSnapshot(EMPTY_SNAPSHOT);
191
+ return;
192
+ }
193
+ setSnapshot(controller.getSnapshot());
194
+ unsubscribeController = controller.subscribe(() => {
195
+ setSnapshot(controller.getSnapshot());
196
+ });
197
+ };
198
+ bindController();
199
+ const unsubscribeRegistry = subscribeTourRegistry(id, bindController);
200
+ return () => {
201
+ unsubscribeRegistry();
202
+ if (unsubscribeController) unsubscribeController();
203
+ };
204
+ }, [id]);
205
+ const call = react.useCallback((method) => {
206
+ const controller = getTourController(id);
207
+ if (!controller) return;
208
+ controller[method]();
209
+ }, [id]);
210
+ return react.useMemo(
211
+ () => ({
212
+ startTour: () => call("startTour"),
213
+ nextStep: () => call("nextStep"),
214
+ prevStep: () => call("prevStep"),
215
+ skipTour: () => call("skipTour"),
216
+ closeTour: () => call("closeTour"),
217
+ currentStep: snapshot.currentStep,
218
+ currentStepIndex: snapshot.currentStepIndex,
219
+ totalSteps: snapshot.totalSteps,
220
+ isActive: snapshot.isActive
221
+ }),
222
+ [call, snapshot]
223
+ );
224
+ }
225
+ function useTourSequencer(sequence) {
226
+ const {
227
+ newFeatures,
228
+ canShowTour,
229
+ markTourShown,
230
+ markFeatureSeen
231
+ } = useFeatureDrop();
232
+ const [startedFeatureIds, setStartedFeatureIds] = react.useState(/* @__PURE__ */ new Set());
233
+ const visibleFeatureIds = react.useMemo(
234
+ () => new Set(newFeatures.map((feature) => feature.id)),
235
+ [newFeatures]
236
+ );
237
+ const remaining = react.useMemo(
238
+ () => sequence.filter(
239
+ (item) => visibleFeatureIds.has(item.featureId) && !startedFeatureIds.has(item.featureId)
240
+ ),
241
+ [sequence, startedFeatureIds, visibleFeatureIds]
242
+ );
243
+ const next = remaining[0] ?? null;
244
+ const startNextTour = react.useCallback(() => {
245
+ if (!next) return false;
246
+ if (!canShowTour()) return false;
247
+ const controller = getTourController(next.tourId);
248
+ if (!controller) return false;
249
+ setStartedFeatureIds((previous) => {
250
+ if (previous.has(next.featureId)) return previous;
251
+ const updated = new Set(previous);
252
+ updated.add(next.featureId);
253
+ return updated;
254
+ });
255
+ markFeatureSeen(next.featureId);
256
+ markTourShown();
257
+ controller.startTour();
258
+ return true;
259
+ }, [canShowTour, markFeatureSeen, markTourShown, next]);
260
+ return {
261
+ nextTourId: next?.tourId ?? null,
262
+ nextFeatureId: next?.featureId ?? null,
263
+ remainingTours: remaining.length,
264
+ startNextTour
265
+ };
266
+ }
267
+
268
+ // src/react/checklist-registry.ts
269
+ var controllers2 = /* @__PURE__ */ new Map();
270
+ var registryListeners2 = /* @__PURE__ */ new Map();
271
+ function getChecklistController(id) {
272
+ return controllers2.get(id);
273
+ }
274
+ function subscribeChecklistRegistry(id, listener) {
275
+ const listeners = registryListeners2.get(id) ?? /* @__PURE__ */ new Set();
276
+ listeners.add(listener);
277
+ registryListeners2.set(id, listeners);
278
+ return () => {
279
+ const current = registryListeners2.get(id);
280
+ if (!current) return;
281
+ current.delete(listener);
282
+ if (current.size === 0) registryListeners2.delete(id);
283
+ };
284
+ }
285
+
286
+ // src/react/hooks/use-checklist.ts
287
+ var EMPTY_SNAPSHOT2 = {
288
+ exists: false,
289
+ tasks: [],
290
+ progress: { completed: 0, total: 0, percent: 0 },
291
+ isComplete: false,
292
+ dismissed: false,
293
+ collapsed: false
294
+ };
295
+ function readSnapshot2(id) {
296
+ const controller = getChecklistController(id);
297
+ if (!controller) return EMPTY_SNAPSHOT2;
298
+ return controller.getSnapshot();
299
+ }
300
+ function useChecklist(id) {
301
+ const [snapshot, setSnapshot] = react.useState(() => readSnapshot2(id));
302
+ react.useEffect(() => {
303
+ let unsubscribeController = null;
304
+ const bind = () => {
305
+ if (unsubscribeController) {
306
+ unsubscribeController();
307
+ unsubscribeController = null;
308
+ }
309
+ const controller = getChecklistController(id);
310
+ if (!controller) {
311
+ setSnapshot(EMPTY_SNAPSHOT2);
312
+ return;
313
+ }
314
+ setSnapshot(controller.getSnapshot());
315
+ unsubscribeController = controller.subscribe(() => {
316
+ setSnapshot(controller.getSnapshot());
317
+ });
318
+ };
319
+ bind();
320
+ const unsubscribeRegistry = subscribeChecklistRegistry(id, bind);
321
+ return () => {
322
+ unsubscribeRegistry();
323
+ if (unsubscribeController) unsubscribeController();
324
+ };
325
+ }, [id]);
326
+ const invoke = react.useCallback((method, arg) => {
327
+ const controller = getChecklistController(id);
328
+ if (!controller) return;
329
+ if (method === "completeTask") {
330
+ controller.completeTask(arg ?? "");
331
+ return;
332
+ }
333
+ controller[method]();
334
+ }, [id]);
335
+ return react.useMemo(
336
+ () => ({
337
+ completeTask: (taskId) => invoke("completeTask", taskId),
338
+ resetChecklist: () => invoke("resetChecklist"),
339
+ dismissChecklist: () => invoke("dismissChecklist"),
340
+ toggleCollapsed: () => invoke("toggleCollapsed"),
341
+ isComplete: snapshot.isComplete,
342
+ progress: snapshot.progress,
343
+ tasks: snapshot.tasks,
344
+ dismissed: snapshot.dismissed,
345
+ collapsed: snapshot.collapsed
346
+ }),
347
+ [invoke, snapshot]
348
+ );
349
+ }
350
+
351
+ // src/react/survey-registry.ts
352
+ var controllers3 = /* @__PURE__ */ new Map();
353
+ var registryListeners3 = /* @__PURE__ */ new Map();
354
+ function getSurveyController(id) {
355
+ return controllers3.get(id);
356
+ }
357
+ function subscribeSurveyRegistry(id, listener) {
358
+ const listeners = registryListeners3.get(id) ?? /* @__PURE__ */ new Set();
359
+ listeners.add(listener);
360
+ registryListeners3.set(id, listeners);
361
+ return () => {
362
+ const current = registryListeners3.get(id);
363
+ if (!current) return;
364
+ current.delete(listener);
365
+ if (current.size === 0) {
366
+ registryListeners3.delete(id);
367
+ }
368
+ };
369
+ }
370
+
371
+ // src/react/hooks/use-survey.ts
372
+ var EMPTY_SNAPSHOT3 = {
373
+ exists: false,
374
+ isOpen: false,
375
+ submitted: false,
376
+ canShow: false,
377
+ type: "custom"
378
+ };
379
+ function readSnapshot3(id) {
380
+ const controller = getSurveyController(id);
381
+ if (!controller) return EMPTY_SNAPSHOT3;
382
+ return controller.getSnapshot();
383
+ }
384
+ function useSurvey(id) {
385
+ const [snapshot, setSnapshot] = react.useState(() => readSnapshot3(id));
386
+ react.useEffect(() => {
387
+ let unsubscribeController = null;
388
+ const bind = () => {
389
+ if (unsubscribeController) {
390
+ unsubscribeController();
391
+ unsubscribeController = null;
392
+ }
393
+ const controller = getSurveyController(id);
394
+ if (!controller) {
395
+ setSnapshot(EMPTY_SNAPSHOT3);
396
+ return;
397
+ }
398
+ setSnapshot(controller.getSnapshot());
399
+ unsubscribeController = controller.subscribe(() => {
400
+ setSnapshot(controller.getSnapshot());
401
+ });
402
+ };
403
+ bind();
404
+ const unsubscribeRegistry = subscribeSurveyRegistry(id, bind);
405
+ return () => {
406
+ unsubscribeRegistry();
407
+ if (unsubscribeController) unsubscribeController();
408
+ };
409
+ }, [id]);
410
+ const show = react.useCallback((options) => {
411
+ const controller = getSurveyController(id);
412
+ if (!controller) return false;
413
+ return controller.show(options);
414
+ }, [id]);
415
+ const hide = react.useCallback(() => {
416
+ getSurveyController(id)?.hide();
417
+ }, [id]);
418
+ const askLater = react.useCallback(() => {
419
+ getSurveyController(id)?.askLater();
420
+ }, [id]);
421
+ return react.useMemo(
422
+ () => ({
423
+ show,
424
+ hide,
425
+ askLater,
426
+ isOpen: snapshot.isOpen,
427
+ submitted: snapshot.submitted,
428
+ canShow: snapshot.canShow,
429
+ type: snapshot.type
430
+ }),
431
+ [askLater, hide, show, snapshot]
432
+ );
433
+ }
434
+ function useChangelog() {
435
+ const ctx = useFeatureDrop();
436
+ const markAllSeen = react.useCallback(() => {
437
+ void ctx.dismissAll();
438
+ }, [ctx]);
439
+ const getByCategory = react.useCallback(
440
+ (category) => {
441
+ return ctx.newFeatures.filter((f) => f.category === category);
442
+ },
443
+ [ctx.newFeatures]
444
+ );
445
+ return react.useMemo(
446
+ () => ({
447
+ features: ctx.manifest,
448
+ newFeatures: ctx.newFeatures,
449
+ newCount: ctx.newCount,
450
+ newFeaturesSorted: ctx.newFeaturesSorted,
451
+ dismiss: ctx.dismiss,
452
+ dismissAll: () => void ctx.dismissAll(),
453
+ isNew: ctx.isNew,
454
+ markAllSeen,
455
+ getByCategory
456
+ }),
457
+ [ctx.manifest, ctx.newFeatures, ctx.newCount, ctx.newFeaturesSorted, ctx.dismiss, ctx.dismissAll, ctx.isNew, markAllSeen, getByCategory]
458
+ );
459
+ }
460
+
461
+ exports.useAdoptionAnalytics = useAdoptionAnalytics;
462
+ exports.useChangelog = useChangelog;
463
+ exports.useChecklist = useChecklist;
464
+ exports.useFeatureDrop = useFeatureDrop;
465
+ exports.useNewCount = useNewCount;
466
+ exports.useNewFeature = useNewFeature;
467
+ exports.useSurvey = useSurvey;
468
+ exports.useTabNotification = useTabNotification;
469
+ exports.useTour = useTour;
470
+ exports.useTourSequencer = useTourSequencer;
471
+ //# sourceMappingURL=react-hooks.cjs.map
472
+ //# sourceMappingURL=react-hooks.cjs.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":["createContext","useContext","useRef","useEffect","useMemo","useState","useCallback","controllers","registryListeners","EMPTY_SNAPSHOT","readSnapshot"],"mappings":"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","file":"react-hooks.cjs","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"]}