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.
- package/.claude-plugin/plugin.json +21 -0
- package/README.md +77 -11
- package/context7.json +15 -0
- package/dist/index.d.cts +89 -1
- package/dist/index.d.ts +89 -1
- package/dist/preact.cjs +19 -10
- package/dist/preact.cjs.map +1 -1
- package/dist/preact.d.cts +93 -1
- package/dist/preact.d.ts +93 -1
- package/dist/preact.js +19 -10
- package/dist/preact.js.map +1 -1
- package/dist/react-hooks.cjs +472 -0
- package/dist/react-hooks.cjs.map +1 -0
- package/dist/react-hooks.d.cts +540 -0
- package/dist/react-hooks.d.ts +540 -0
- package/dist/react-hooks.js +461 -0
- package/dist/react-hooks.js.map +1 -0
- package/dist/react.cjs +19 -10
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +93 -1
- package/dist/react.d.ts +93 -1
- package/dist/react.js +19 -10
- package/dist/react.js.map +1 -1
- package/dist/tailwind.cjs +148 -0
- package/dist/tailwind.cjs.map +1 -0
- package/dist/tailwind.d.cts +38 -0
- package/dist/tailwind.d.ts +38 -0
- package/dist/tailwind.js +146 -0
- package/dist/tailwind.js.map +1 -0
- package/dist/testing.cjs +19 -10
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +90 -0
- package/dist/testing.d.ts +90 -0
- package/dist/testing.js +19 -10
- package/dist/testing.js.map +1 -1
- package/package.json +32 -1
- package/skills/featuredrop-setup/SKILL.md +124 -0
- package/src/ai/claude-skill.md +109 -0
- package/src/ai/cursorrules.txt +13 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "featuredrop",
|
|
3
|
+
"description": "AI-assisted product adoption setup — changelogs, tours, checklists, badges, feedback widgets. Helps Claude Code configure FeatureDrop in any project.",
|
|
4
|
+
"version": "2.5.0",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "GLINR STUDIOS",
|
|
7
|
+
"url": "https://glincker.com"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://featuredrop.dev",
|
|
10
|
+
"repository": "https://github.com/GLINCKER/featuredrop",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"feature-discovery",
|
|
14
|
+
"changelog",
|
|
15
|
+
"product-tours",
|
|
16
|
+
"onboarding",
|
|
17
|
+
"saas",
|
|
18
|
+
"react",
|
|
19
|
+
"developer-tools"
|
|
20
|
+
]
|
|
21
|
+
}
|
package/README.md
CHANGED
|
@@ -223,18 +223,84 @@ npx featuredrop migrate --from launchnotes --input launchnotes-export.json
|
|
|
223
223
|
|
|
224
224
|
---
|
|
225
225
|
|
|
226
|
-
##
|
|
226
|
+
## Headless Hooks (for shadcn / custom UI)
|
|
227
227
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
228
|
+
Don't want our components? Use hooks — **data + actions, zero JSX**:
|
|
229
|
+
|
|
230
|
+
```tsx
|
|
231
|
+
import { useChangelog } from 'featuredrop/react/hooks'
|
|
232
|
+
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'
|
|
233
|
+
import { Badge } from '@/components/ui/badge'
|
|
234
|
+
|
|
235
|
+
function MyChangelog() {
|
|
236
|
+
const { newFeatures, newCount, dismiss, markAllSeen } = useChangelog()
|
|
237
|
+
|
|
238
|
+
return (
|
|
239
|
+
<Sheet onOpenChange={() => markAllSeen()}>
|
|
240
|
+
<SheetTrigger>
|
|
241
|
+
What's New {newCount > 0 && <Badge>{newCount}</Badge>}
|
|
242
|
+
</SheetTrigger>
|
|
243
|
+
<SheetContent>
|
|
244
|
+
{newFeatures.map(f => (
|
|
245
|
+
<div key={f.id} onClick={() => dismiss(f.id)}>
|
|
246
|
+
<h3>{f.label}</h3>
|
|
247
|
+
<p>{f.description}</p>
|
|
248
|
+
</div>
|
|
249
|
+
))}
|
|
250
|
+
</SheetContent>
|
|
251
|
+
</Sheet>
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
| Hook | Import | Returns |
|
|
257
|
+
|---|---|---|
|
|
258
|
+
| `useFeatureDrop()` | `featuredrop/react/hooks` | Full context: features, count, dismiss, throttle controls |
|
|
259
|
+
| `useNewFeature(key)` | `featuredrop/react/hooks` | `{ isNew, feature, dismiss }` |
|
|
260
|
+
| `useNewCount()` | `featuredrop/react/hooks` | Current unread badge count |
|
|
261
|
+
| `useChangelog()` | `featuredrop/react/hooks` | `{ features, newFeatures, newCount, dismiss, dismissAll, markAllSeen, getByCategory }` |
|
|
262
|
+
| `useTour(id)` | `featuredrop/react/hooks` | Imperative tour controls and step snapshot |
|
|
263
|
+
| `useTourSequencer(sequence)` | `featuredrop/react/hooks` | Ordered multi-tour orchestration |
|
|
264
|
+
| `useChecklist(id)` | `featuredrop/react/hooks` | Checklist progress + task controls |
|
|
265
|
+
| `useSurvey(id)` | `featuredrop/react/hooks` | Survey controls: `show`, `hide`, `askLater` |
|
|
266
|
+
| `useTabNotification()` | `featuredrop/react/hooks` | Browser tab title count: `"(3) My App"` |
|
|
267
|
+
|
|
268
|
+
> **When to use hooks vs components:** If your project uses shadcn/ui, Radix, or any custom design system, use hooks from `featuredrop/react/hooks`. If you want out-of-the-box UI, use components from `featuredrop/react`.
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## AI-Native
|
|
273
|
+
|
|
274
|
+
FeatureDrop is built for the AI coding era. Your AI assistant already knows how to use it.
|
|
275
|
+
|
|
276
|
+
### Claude Code Plugin
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
# Install the plugin — Claude Code learns FeatureDrop's API automatically
|
|
280
|
+
/plugin install featuredrop
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Then just ask: *"Add a changelog widget with auto-expiring badges to my app"* — Claude handles the rest.
|
|
284
|
+
|
|
285
|
+
### Cursor / Copilot
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
# Auto-detect your IDE and copy the right context files
|
|
289
|
+
npx featuredrop ai-setup
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Tailwind Plugin
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
// tailwind.config.ts
|
|
296
|
+
import { featureDropPlugin } from 'featuredrop/tailwind'
|
|
297
|
+
|
|
298
|
+
export default {
|
|
299
|
+
plugins: [featureDropPlugin()],
|
|
300
|
+
// Adds: fd-badge, fd-badge-dot, fd-badge-count, animations, CSS variables
|
|
301
|
+
// Auto dark mode, reduced-motion support
|
|
302
|
+
}
|
|
303
|
+
```
|
|
238
304
|
|
|
239
305
|
---
|
|
240
306
|
|
package/context7.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://context7.com/schema/context7.json",
|
|
3
|
+
"projectTitle": "FeatureDrop",
|
|
4
|
+
"description": "Open-source product adoption toolkit — changelogs, badges, tours, checklists, hotspots, feedback widgets, surveys. Zero dependencies, < 3 kB core, 8 framework adapters, 12 storage adapters. Free Beamer/Pendo alternative.",
|
|
5
|
+
"folders": ["apps/docs/pages/docs", "skills", "README.md"],
|
|
6
|
+
"excludeFolders": ["node_modules", "dist", "coverage", ".git", "examples", "docs-local", ".claude"],
|
|
7
|
+
"excludeFiles": ["CHANGELOG.md", "CODE_OF_CONDUCT.md", "_app.tsx", "_meta.json"],
|
|
8
|
+
"rules": [
|
|
9
|
+
"Always use subpath imports: featuredrop/react, featuredrop/react/hooks, featuredrop/adapters",
|
|
10
|
+
"Prefer hooks from featuredrop/react/hooks for custom design systems (shadcn, Radix, etc.)",
|
|
11
|
+
"Features are defined in a JSON manifest with { id, label, description, releasedAt, showNewUntil? }",
|
|
12
|
+
"Core bundle is under 3 kB gzipped with zero production dependencies",
|
|
13
|
+
"All components support headless mode via render props for full customization"
|
|
14
|
+
]
|
|
15
|
+
}
|
package/dist/index.d.cts
CHANGED
|
@@ -232,6 +232,94 @@ interface AnalyticsCallbacks {
|
|
|
232
232
|
/** Fired when all features are dismissed at once */
|
|
233
233
|
onAllDismissed?: () => void;
|
|
234
234
|
}
|
|
235
|
+
/** Display format hint from the engine */
|
|
236
|
+
type DisplayFormat = "badge" | "toast" | "modal" | "banner" | "inline" | "spotlight";
|
|
237
|
+
/** Interaction type tracked by the engine */
|
|
238
|
+
type InteractionType = "seen" | "dismissed" | "clicked" | "completed" | "snoozed" | "hovered" | "expanded";
|
|
239
|
+
/** Timing decision returned by the engine */
|
|
240
|
+
interface TimingDecision {
|
|
241
|
+
/** Whether to show the announcement now */
|
|
242
|
+
show: boolean;
|
|
243
|
+
/** Reason for the decision */
|
|
244
|
+
reason: string;
|
|
245
|
+
/** Suggested delay in ms if not showing now */
|
|
246
|
+
delayMs?: number;
|
|
247
|
+
/** Confidence level (0-1) */
|
|
248
|
+
confidence: number;
|
|
249
|
+
}
|
|
250
|
+
/** Format recommendation from the engine */
|
|
251
|
+
interface FormatRecommendation {
|
|
252
|
+
/** Recommended display format */
|
|
253
|
+
primary: DisplayFormat;
|
|
254
|
+
/** Fallback format if primary component isn't used */
|
|
255
|
+
fallback: DisplayFormat;
|
|
256
|
+
/** Reason for the recommendation */
|
|
257
|
+
reason: string;
|
|
258
|
+
}
|
|
259
|
+
/** Adoption score breakdown */
|
|
260
|
+
interface AdoptionScore {
|
|
261
|
+
/** Overall score (0-100) */
|
|
262
|
+
score: number;
|
|
263
|
+
/** Letter grade */
|
|
264
|
+
grade: "A" | "B" | "C" | "D" | "F";
|
|
265
|
+
/** Score breakdown */
|
|
266
|
+
breakdown: {
|
|
267
|
+
/** % of features the user has explored */
|
|
268
|
+
featuresExplored: number;
|
|
269
|
+
/** Rate of dismissals (lower is better) */
|
|
270
|
+
dismissRate: number;
|
|
271
|
+
/** Rate of tour/checklist completion */
|
|
272
|
+
completionRate: number;
|
|
273
|
+
/** Whether engagement is rising, stable, or declining */
|
|
274
|
+
engagementTrend: "rising" | "stable" | "declining";
|
|
275
|
+
};
|
|
276
|
+
/** Actionable recommendations */
|
|
277
|
+
recommendations: string[];
|
|
278
|
+
}
|
|
279
|
+
/** Per-feature adoption status */
|
|
280
|
+
interface FeatureAdoptionStatus {
|
|
281
|
+
featureId: string;
|
|
282
|
+
status: "unseen" | "seen" | "explored" | "adopted" | "dismissed";
|
|
283
|
+
firstSeen?: string;
|
|
284
|
+
lastInteraction?: string;
|
|
285
|
+
interactionCount: number;
|
|
286
|
+
}
|
|
287
|
+
/** Delivery context passed to the engine for timing decisions */
|
|
288
|
+
interface DeliveryContext {
|
|
289
|
+
/** Current route/path */
|
|
290
|
+
currentPath: string;
|
|
291
|
+
/** Seconds since session start */
|
|
292
|
+
sessionAge: number;
|
|
293
|
+
/** Dismissals in last 5 minutes */
|
|
294
|
+
recentDismissals: number;
|
|
295
|
+
/** Feature priority */
|
|
296
|
+
featurePriority: FeaturePriority;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Plugin interface for the FeatureDrop engine.
|
|
300
|
+
*
|
|
301
|
+
* The open-source library defines this interface.
|
|
302
|
+
* The proprietary @featuredrop/engine implements it.
|
|
303
|
+
* Users can also build their own engine implementation.
|
|
304
|
+
*
|
|
305
|
+
* The free library works perfectly without any engine.
|
|
306
|
+
*/
|
|
307
|
+
interface FeatureDropEngine {
|
|
308
|
+
/** Decide whether to show a feature announcement now */
|
|
309
|
+
shouldShow(featureId: string, context: DeliveryContext): TimingDecision;
|
|
310
|
+
/** Recommend the best display format for a feature */
|
|
311
|
+
recommendFormat(featureId: string): FormatRecommendation;
|
|
312
|
+
/** Get the user's overall adoption score */
|
|
313
|
+
getAdoptionScore(): AdoptionScore;
|
|
314
|
+
/** Track a user interaction with a feature */
|
|
315
|
+
trackInteraction(featureId: string, type: InteractionType): void;
|
|
316
|
+
/** Get adoption status for a specific feature */
|
|
317
|
+
getFeatureAdoption(featureId: string): FeatureAdoptionStatus;
|
|
318
|
+
/** Initialize the engine (called by provider on mount) */
|
|
319
|
+
initialize?(): void;
|
|
320
|
+
/** Cleanup resources (called by provider on unmount) */
|
|
321
|
+
destroy?(): void;
|
|
322
|
+
}
|
|
235
323
|
|
|
236
324
|
/**
|
|
237
325
|
* Default audience matching logic.
|
|
@@ -337,4 +425,4 @@ declare class MemoryAdapter implements StorageAdapter {
|
|
|
337
425
|
dismissAll(now: Date): Promise<void>;
|
|
338
426
|
}
|
|
339
427
|
|
|
340
|
-
export { type AnalyticsCallbacks, type AudienceMatchFn, type AudienceRule, type DismissalState, type FeatureCTA, type FeatureDependencies, type FeatureDependencyState, type FeatureDropAnimationPreset, type FeatureEntry, type FeatureFlagBridge, type FeatureManifest, type FeaturePriority, type FeatureTrigger, type FeatureType, type FeatureVariant, LocalStorageAdapter, type LocalStorageAdapterOptions, MemoryAdapter, type ServerStorageAdapter, type StorageAdapter, type TriggerContext, type UserContext, createManifest, getFeatureById, getNewFeatureCount, getNewFeatures, getNewFeaturesByCategory, getNewFeaturesSorted, hasNewFeature, isNew, matchesAudience };
|
|
428
|
+
export { type AdoptionScore, type AnalyticsCallbacks, type AudienceMatchFn, type AudienceRule, type DeliveryContext, type DismissalState, type DisplayFormat, type FeatureAdoptionStatus, type FeatureCTA, type FeatureDependencies, type FeatureDependencyState, type FeatureDropAnimationPreset, type FeatureDropEngine, type FeatureEntry, type FeatureFlagBridge, type FeatureManifest, type FeaturePriority, type FeatureTrigger, type FeatureType, type FeatureVariant, type FormatRecommendation, type InteractionType, LocalStorageAdapter, type LocalStorageAdapterOptions, MemoryAdapter, type ServerStorageAdapter, type StorageAdapter, type TimingDecision, type TriggerContext, type UserContext, createManifest, getFeatureById, getNewFeatureCount, getNewFeatures, getNewFeaturesByCategory, getNewFeaturesSorted, hasNewFeature, isNew, matchesAudience };
|
package/dist/index.d.ts
CHANGED
|
@@ -232,6 +232,94 @@ interface AnalyticsCallbacks {
|
|
|
232
232
|
/** Fired when all features are dismissed at once */
|
|
233
233
|
onAllDismissed?: () => void;
|
|
234
234
|
}
|
|
235
|
+
/** Display format hint from the engine */
|
|
236
|
+
type DisplayFormat = "badge" | "toast" | "modal" | "banner" | "inline" | "spotlight";
|
|
237
|
+
/** Interaction type tracked by the engine */
|
|
238
|
+
type InteractionType = "seen" | "dismissed" | "clicked" | "completed" | "snoozed" | "hovered" | "expanded";
|
|
239
|
+
/** Timing decision returned by the engine */
|
|
240
|
+
interface TimingDecision {
|
|
241
|
+
/** Whether to show the announcement now */
|
|
242
|
+
show: boolean;
|
|
243
|
+
/** Reason for the decision */
|
|
244
|
+
reason: string;
|
|
245
|
+
/** Suggested delay in ms if not showing now */
|
|
246
|
+
delayMs?: number;
|
|
247
|
+
/** Confidence level (0-1) */
|
|
248
|
+
confidence: number;
|
|
249
|
+
}
|
|
250
|
+
/** Format recommendation from the engine */
|
|
251
|
+
interface FormatRecommendation {
|
|
252
|
+
/** Recommended display format */
|
|
253
|
+
primary: DisplayFormat;
|
|
254
|
+
/** Fallback format if primary component isn't used */
|
|
255
|
+
fallback: DisplayFormat;
|
|
256
|
+
/** Reason for the recommendation */
|
|
257
|
+
reason: string;
|
|
258
|
+
}
|
|
259
|
+
/** Adoption score breakdown */
|
|
260
|
+
interface AdoptionScore {
|
|
261
|
+
/** Overall score (0-100) */
|
|
262
|
+
score: number;
|
|
263
|
+
/** Letter grade */
|
|
264
|
+
grade: "A" | "B" | "C" | "D" | "F";
|
|
265
|
+
/** Score breakdown */
|
|
266
|
+
breakdown: {
|
|
267
|
+
/** % of features the user has explored */
|
|
268
|
+
featuresExplored: number;
|
|
269
|
+
/** Rate of dismissals (lower is better) */
|
|
270
|
+
dismissRate: number;
|
|
271
|
+
/** Rate of tour/checklist completion */
|
|
272
|
+
completionRate: number;
|
|
273
|
+
/** Whether engagement is rising, stable, or declining */
|
|
274
|
+
engagementTrend: "rising" | "stable" | "declining";
|
|
275
|
+
};
|
|
276
|
+
/** Actionable recommendations */
|
|
277
|
+
recommendations: string[];
|
|
278
|
+
}
|
|
279
|
+
/** Per-feature adoption status */
|
|
280
|
+
interface FeatureAdoptionStatus {
|
|
281
|
+
featureId: string;
|
|
282
|
+
status: "unseen" | "seen" | "explored" | "adopted" | "dismissed";
|
|
283
|
+
firstSeen?: string;
|
|
284
|
+
lastInteraction?: string;
|
|
285
|
+
interactionCount: number;
|
|
286
|
+
}
|
|
287
|
+
/** Delivery context passed to the engine for timing decisions */
|
|
288
|
+
interface DeliveryContext {
|
|
289
|
+
/** Current route/path */
|
|
290
|
+
currentPath: string;
|
|
291
|
+
/** Seconds since session start */
|
|
292
|
+
sessionAge: number;
|
|
293
|
+
/** Dismissals in last 5 minutes */
|
|
294
|
+
recentDismissals: number;
|
|
295
|
+
/** Feature priority */
|
|
296
|
+
featurePriority: FeaturePriority;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Plugin interface for the FeatureDrop engine.
|
|
300
|
+
*
|
|
301
|
+
* The open-source library defines this interface.
|
|
302
|
+
* The proprietary @featuredrop/engine implements it.
|
|
303
|
+
* Users can also build their own engine implementation.
|
|
304
|
+
*
|
|
305
|
+
* The free library works perfectly without any engine.
|
|
306
|
+
*/
|
|
307
|
+
interface FeatureDropEngine {
|
|
308
|
+
/** Decide whether to show a feature announcement now */
|
|
309
|
+
shouldShow(featureId: string, context: DeliveryContext): TimingDecision;
|
|
310
|
+
/** Recommend the best display format for a feature */
|
|
311
|
+
recommendFormat(featureId: string): FormatRecommendation;
|
|
312
|
+
/** Get the user's overall adoption score */
|
|
313
|
+
getAdoptionScore(): AdoptionScore;
|
|
314
|
+
/** Track a user interaction with a feature */
|
|
315
|
+
trackInteraction(featureId: string, type: InteractionType): void;
|
|
316
|
+
/** Get adoption status for a specific feature */
|
|
317
|
+
getFeatureAdoption(featureId: string): FeatureAdoptionStatus;
|
|
318
|
+
/** Initialize the engine (called by provider on mount) */
|
|
319
|
+
initialize?(): void;
|
|
320
|
+
/** Cleanup resources (called by provider on unmount) */
|
|
321
|
+
destroy?(): void;
|
|
322
|
+
}
|
|
235
323
|
|
|
236
324
|
/**
|
|
237
325
|
* Default audience matching logic.
|
|
@@ -337,4 +425,4 @@ declare class MemoryAdapter implements StorageAdapter {
|
|
|
337
425
|
dismissAll(now: Date): Promise<void>;
|
|
338
426
|
}
|
|
339
427
|
|
|
340
|
-
export { type AnalyticsCallbacks, type AudienceMatchFn, type AudienceRule, type DismissalState, type FeatureCTA, type FeatureDependencies, type FeatureDependencyState, type FeatureDropAnimationPreset, type FeatureEntry, type FeatureFlagBridge, type FeatureManifest, type FeaturePriority, type FeatureTrigger, type FeatureType, type FeatureVariant, LocalStorageAdapter, type LocalStorageAdapterOptions, MemoryAdapter, type ServerStorageAdapter, type StorageAdapter, type TriggerContext, type UserContext, createManifest, getFeatureById, getNewFeatureCount, getNewFeatures, getNewFeaturesByCategory, getNewFeaturesSorted, hasNewFeature, isNew, matchesAudience };
|
|
428
|
+
export { type AdoptionScore, type AnalyticsCallbacks, type AudienceMatchFn, type AudienceRule, type DeliveryContext, type DismissalState, type DisplayFormat, type FeatureAdoptionStatus, type FeatureCTA, type FeatureDependencies, type FeatureDependencyState, type FeatureDropAnimationPreset, type FeatureDropEngine, type FeatureEntry, type FeatureFlagBridge, type FeatureManifest, type FeaturePriority, type FeatureTrigger, type FeatureType, type FeatureVariant, type FormatRecommendation, type InteractionType, LocalStorageAdapter, type LocalStorageAdapterOptions, MemoryAdapter, type ServerStorageAdapter, type StorageAdapter, type TimingDecision, type TriggerContext, type UserContext, createManifest, getFeatureById, getNewFeatureCount, getNewFeatures, getNewFeaturesByCategory, getNewFeaturesSorted, hasNewFeature, isNew, matchesAudience };
|
package/dist/preact.cjs
CHANGED
|
@@ -1099,6 +1099,7 @@ function FeatureDropProvider({
|
|
|
1099
1099
|
locale = "en",
|
|
1100
1100
|
animation = "normal",
|
|
1101
1101
|
translations: translationOverrides,
|
|
1102
|
+
engine,
|
|
1102
1103
|
children
|
|
1103
1104
|
}) {
|
|
1104
1105
|
const analyticsRef = react.useRef(analytics);
|
|
@@ -1155,20 +1156,20 @@ function FeatureDropProvider({
|
|
|
1155
1156
|
seenFeatureIds: readIdSet(SEEN_FEATURES_STORAGE_KEY),
|
|
1156
1157
|
clickedFeatureIds: readIdSet(CLICKED_FEATURES_STORAGE_KEY),
|
|
1157
1158
|
triggerContext: (() => {
|
|
1158
|
-
const
|
|
1159
|
-
if (!
|
|
1160
|
-
|
|
1161
|
-
return
|
|
1159
|
+
const engine2 = triggerEngineRef.current;
|
|
1160
|
+
if (!engine2) return void 0;
|
|
1161
|
+
engine2.setElapsedMs(Date.now() - sessionStartedAtRef.current);
|
|
1162
|
+
return engine2.getContext();
|
|
1162
1163
|
})(),
|
|
1163
1164
|
flagBridge
|
|
1164
1165
|
})
|
|
1165
1166
|
);
|
|
1166
1167
|
const recompute = react.useCallback(() => {
|
|
1167
|
-
const
|
|
1168
|
+
const engine2 = triggerEngineRef.current;
|
|
1168
1169
|
let triggerContext;
|
|
1169
|
-
if (
|
|
1170
|
-
|
|
1171
|
-
triggerContext =
|
|
1170
|
+
if (engine2) {
|
|
1171
|
+
engine2.setElapsedMs(Date.now() - sessionStartedAtRef.current);
|
|
1172
|
+
triggerContext = engine2.getContext();
|
|
1172
1173
|
}
|
|
1173
1174
|
setFeatureState(
|
|
1174
1175
|
computeFeatureState({
|
|
@@ -1205,6 +1206,12 @@ function FeatureDropProvider({
|
|
|
1205
1206
|
react.useEffect(() => {
|
|
1206
1207
|
recompute();
|
|
1207
1208
|
}, [recompute]);
|
|
1209
|
+
react.useEffect(() => {
|
|
1210
|
+
engine?.initialize?.();
|
|
1211
|
+
return () => {
|
|
1212
|
+
engine?.destroy?.();
|
|
1213
|
+
};
|
|
1214
|
+
}, [engine]);
|
|
1208
1215
|
const hasTimeTriggers = react.useMemo(
|
|
1209
1216
|
() => resolvedManifest.some((feature) => feature.trigger?.type === "time"),
|
|
1210
1217
|
[resolvedManifest]
|
|
@@ -1487,7 +1494,8 @@ function FeatureDropProvider({
|
|
|
1487
1494
|
trackUsageEvent,
|
|
1488
1495
|
trackTriggerEvent,
|
|
1489
1496
|
trackMilestone,
|
|
1490
|
-
setTriggerPath
|
|
1497
|
+
setTriggerPath,
|
|
1498
|
+
engine: engine ?? null
|
|
1491
1499
|
}),
|
|
1492
1500
|
[
|
|
1493
1501
|
resolvedManifest,
|
|
@@ -1521,7 +1529,8 @@ function FeatureDropProvider({
|
|
|
1521
1529
|
trackUsageEvent,
|
|
1522
1530
|
trackTriggerEvent,
|
|
1523
1531
|
trackMilestone,
|
|
1524
|
-
setTriggerPath
|
|
1532
|
+
setTriggerPath,
|
|
1533
|
+
engine
|
|
1525
1534
|
]
|
|
1526
1535
|
);
|
|
1527
1536
|
return /* @__PURE__ */ jsxRuntime.jsx(FeatureDropContext.Provider, { value, children });
|