featuredrop 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +547 -4
  2. package/dist/admin.cjs +212 -0
  3. package/dist/admin.cjs.map +1 -0
  4. package/dist/admin.d.cts +176 -0
  5. package/dist/admin.d.ts +176 -0
  6. package/dist/admin.js +207 -0
  7. package/dist/admin.js.map +1 -0
  8. package/dist/angular.cjs +296 -0
  9. package/dist/angular.cjs.map +1 -0
  10. package/dist/angular.d.cts +233 -0
  11. package/dist/angular.d.ts +233 -0
  12. package/dist/angular.js +293 -0
  13. package/dist/angular.js.map +1 -0
  14. package/dist/bridges.cjs +401 -0
  15. package/dist/bridges.cjs.map +1 -0
  16. package/dist/bridges.d.cts +194 -0
  17. package/dist/bridges.d.ts +194 -0
  18. package/dist/bridges.js +394 -0
  19. package/dist/bridges.js.map +1 -0
  20. package/dist/ci.cjs +328 -0
  21. package/dist/ci.cjs.map +1 -0
  22. package/dist/ci.d.cts +176 -0
  23. package/dist/ci.d.ts +176 -0
  24. package/dist/ci.js +324 -0
  25. package/dist/ci.js.map +1 -0
  26. package/dist/featuredrop.cjs +1377 -0
  27. package/dist/featuredrop.cjs.map +1 -0
  28. package/dist/flags.cjs +51 -0
  29. package/dist/flags.cjs.map +1 -0
  30. package/dist/flags.d.cts +48 -0
  31. package/dist/flags.d.ts +48 -0
  32. package/dist/flags.js +47 -0
  33. package/dist/flags.js.map +1 -0
  34. package/dist/index.cjs +4734 -70
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.cts +1516 -9
  37. package/dist/index.d.ts +1516 -9
  38. package/dist/index.js +4660 -71
  39. package/dist/index.js.map +1 -1
  40. package/dist/preact.cjs +7790 -0
  41. package/dist/preact.cjs.map +1 -0
  42. package/dist/preact.d.cts +1213 -0
  43. package/dist/preact.d.ts +1213 -0
  44. package/dist/preact.js +7760 -0
  45. package/dist/preact.js.map +1 -0
  46. package/dist/react.cjs +6678 -159
  47. package/dist/react.cjs.map +1 -1
  48. package/dist/react.d.cts +852 -112
  49. package/dist/react.d.ts +852 -112
  50. package/dist/react.js +6657 -156
  51. package/dist/react.js.map +1 -1
  52. package/dist/schema.cjs +292 -0
  53. package/dist/schema.cjs.map +1 -0
  54. package/dist/schema.d.cts +345 -0
  55. package/dist/schema.d.ts +345 -0
  56. package/dist/schema.js +286 -0
  57. package/dist/schema.js.map +1 -0
  58. package/dist/solid.cjs +383 -0
  59. package/dist/solid.cjs.map +1 -0
  60. package/dist/solid.d.cts +246 -0
  61. package/dist/solid.d.ts +246 -0
  62. package/dist/solid.js +376 -0
  63. package/dist/solid.js.map +1 -0
  64. package/dist/svelte.cjs +339 -0
  65. package/dist/svelte.cjs.map +1 -0
  66. package/dist/svelte.js +334 -0
  67. package/dist/svelte.js.map +1 -0
  68. package/dist/testing.cjs +1543 -0
  69. package/dist/testing.cjs.map +1 -0
  70. package/dist/testing.d.cts +361 -0
  71. package/dist/testing.d.ts +361 -0
  72. package/dist/testing.js +1536 -0
  73. package/dist/testing.js.map +1 -0
  74. package/dist/vue.cjs +1094 -0
  75. package/dist/vue.cjs.map +1 -0
  76. package/dist/vue.js +1082 -0
  77. package/dist/vue.js.map +1 -0
  78. package/dist/web-components.cjs +493 -0
  79. package/dist/web-components.cjs.map +1 -0
  80. package/dist/web-components.d.cts +215 -0
  81. package/dist/web-components.d.ts +215 -0
  82. package/dist/web-components.js +487 -0
  83. package/dist/web-components.js.map +1 -0
  84. package/package.json +184 -3
package/README.md CHANGED
@@ -87,6 +87,14 @@ export const FEATURES = createManifest([
87
87
  priority: 'critical',
88
88
  cta: { label: 'Try it', url: '/journal' },
89
89
  },
90
+ {
91
+ id: 'scheduled-reports',
92
+ label: 'Scheduled Reports',
93
+ releasedAt: '2026-02-23T00:00:00Z',
94
+ showNewUntil: '2026-03-23T00:00:00Z',
95
+ dependsOn: { clicked: ['ai-journal'] }, // progressive rollout
96
+ trigger: { type: 'page', match: '/reports/*' }, // contextual trigger
97
+ },
90
98
  ])
91
99
  ```
92
100
 
@@ -101,6 +109,10 @@ const storage = new LocalStorageAdapter({
101
109
  })
102
110
  ```
103
111
 
112
+ For offline-heavy apps, use `IndexedDBAdapter` to persist the same state with IndexedDB + local fallback.
113
+ It also supports queued dismiss sync (`flushQueue`) + remote state reconciliation (`syncFromRemote`) for offline-first flows.
114
+ For remote/state sync, `RemoteAdapter` now includes retry + circuit-breaker protection and a monitoring-friendly `isHealthy()` check.
115
+
104
116
  **3. Check what's new:**
105
117
 
106
118
  ```ts
@@ -112,6 +124,359 @@ hasNewFeature(FEATURES, '/journal', storage) // true/false
112
124
 
113
125
  Works with **any framework**. Zero React dependency for vanilla use.
114
126
 
127
+ ## Adoption Analytics
128
+
129
+ Pipe structured adoption events to PostHog/Amplitude/Mixpanel/Segment or your own endpoint.
130
+
131
+ ```ts
132
+ import { AnalyticsCollector, PostHogAdapter } from 'featuredrop'
133
+
134
+ const collector = new AnalyticsCollector({
135
+ adapter: new PostHogAdapter(posthog),
136
+ batchSize: 20,
137
+ flushInterval: 10_000,
138
+ sampleRate: 1,
139
+ })
140
+ ```
141
+
142
+ ```tsx
143
+ <FeatureDropProvider
144
+ manifest={FEATURES}
145
+ storage={storage}
146
+ collector={collector}
147
+ >
148
+ <App />
149
+ </FeatureDropProvider>
150
+ ```
151
+
152
+ ## A/B Announcement Variants
153
+
154
+ Run deterministic per-user announcement variants with weighted splits.
155
+
156
+ ```ts
157
+ {
158
+ id: 'ai-journal',
159
+ label: 'AI Decision Journal',
160
+ variants: {
161
+ control: { description: 'Track decisions with AI-powered insights.' },
162
+ treatment: { description: 'Never second-guess decisions again.' },
163
+ },
164
+ variantSplit: [50, 50],
165
+ }
166
+ ```
167
+
168
+ ```tsx
169
+ <FeatureDropProvider
170
+ manifest={FEATURES}
171
+ storage={storage}
172
+ variantKey={user.id} // stable key for deterministic assignment
173
+ >
174
+ <App />
175
+ </FeatureDropProvider>
176
+ ```
177
+
178
+ ## Theme Engine
179
+
180
+ Theme all featuredrop components with CSS variables (no CSS-in-JS runtime).
181
+
182
+ ```tsx
183
+ import { ThemeProvider, ChangelogWidget } from 'featuredrop/react'
184
+
185
+ <ThemeProvider theme="dark">
186
+ <ChangelogWidget />
187
+ </ThemeProvider>
188
+ ```
189
+
190
+ ```tsx
191
+ import { createTheme } from 'featuredrop'
192
+ import { ThemeProvider, ChangelogPage } from 'featuredrop/react'
193
+
194
+ const myTheme = createTheme({
195
+ colors: { primary: '#7c3aed' },
196
+ radii: { lg: '16px' },
197
+ })
198
+
199
+ <ThemeProvider theme={myTheme}>
200
+ <ChangelogPage />
201
+ </ThemeProvider>
202
+ ```
203
+
204
+ Presets: `light`, `dark`, `auto`, `minimal`, `vibrant`.
205
+ You can also pass `theme` directly to `ChangelogWidget` and `ChangelogPage` for component-scoped overrides.
206
+
207
+ ## Internationalization
208
+
209
+ Use built-in locale packs or supply partial overrides:
210
+
211
+ ```tsx
212
+ <FeatureDropProvider
213
+ manifest={FEATURES}
214
+ storage={storage}
215
+ locale="fr"
216
+ translations={{
217
+ submit: 'Envoyer maintenant',
218
+ }}
219
+ >
220
+ <App />
221
+ </FeatureDropProvider>
222
+ ```
223
+
224
+ Built-in locales: `en`, `es`, `fr`, `de`, `pt`, `zh-cn`, `ja`, `ko`, `ar`, `hi`.
225
+ Locale helpers include locale-aware date formatting, pluralized count copy, and RTL direction support for Arabic.
226
+ Use `formatRelativeTimeForLocale()` for localized "x days ago" labels, or set `dateFormat="relative"` on `ChangelogWidget` / `ChangelogPage`.
227
+
228
+ ## Animation Presets
229
+
230
+ Choose built-in motion levels globally:
231
+
232
+ ```tsx
233
+ <FeatureDropProvider manifest={FEATURES} storage={storage} animation="subtle">
234
+ <App />
235
+ </FeatureDropProvider>
236
+ ```
237
+
238
+ Available presets: `none`, `subtle`, `normal`, `playful`.
239
+ Reduced-motion users automatically receive `none`.
240
+ Built-in `ChangelogWidget`, `AnnouncementModal`, and `Toast` include enter/exit animations, and `Tour`, `Survey`, `FeedbackWidget`, `Spotlight`, `SpotlightChain`, and `Hotspot` follow the same preset for enter/pulse motion.
241
+
242
+ ## Custom Renderer Protocol
243
+
244
+ Need full UI control? Use a UI-agnostic renderer with state/actions/computed helpers:
245
+
246
+ ```ts
247
+ import { createChangelogRenderer } from 'featuredrop'
248
+
249
+ const renderer = createChangelogRenderer({ manifest: FEATURES, storage })
250
+ renderer.subscribe((state) => console.log(state.newCount))
251
+ renderer.actions.dismiss('ai-journal')
252
+ ```
253
+
254
+ ## Accessibility
255
+
256
+ featuredrop components include keyboard navigation, focus trapping/return for dialogs, ARIA live regions, and reduced-motion support.
257
+
258
+ Automated accessibility regression checks run in CI via `axe-core` + `vitest-axe` (`src/__tests__/accessibility-axe.test.tsx`).
259
+
260
+ ## Changelog-as-Code
261
+
262
+ Manage announcements as markdown files in your repo and compile to a manifest:
263
+
264
+ ```bash
265
+ npx featuredrop init --format markdown
266
+ npx featuredrop add --label "AI Journal" --category ai --description "Track decisions with AI."
267
+ npx featuredrop build --pattern "features/**/*.md" --out featuredrop.manifest.json
268
+ npx featuredrop validate --pattern "features/**/*.md"
269
+ npx featuredrop stats --pattern "features/**/*.md"
270
+ npx featuredrop doctor --pattern "features/**/*.md"
271
+ npx featuredrop generate-rss --pattern "features/**/*.md" --out featuredrop.rss.xml
272
+ npx featuredrop generate-changelog --pattern "features/**/*.md" --out CHANGELOG.generated.md
273
+ npx featuredrop migrate --from beamer --input beamer-export.json --out featuredrop.manifest.json
274
+ npx featuredrop migrate --from headway --input headway-export.json --out featuredrop.manifest.json
275
+ npx featuredrop migrate --from announcekit --input announcekit-export.json --out featuredrop.manifest.json
276
+ npx featuredrop migrate --from canny --input canny-export.json --out featuredrop.manifest.json
277
+ npx featuredrop migrate --from launchnotes --input launchnotes-export.json --out featuredrop.manifest.json
278
+ ```
279
+
280
+ Example feature file:
281
+
282
+ ```md
283
+ ---
284
+ id: ai-journal
285
+ label: AI Journal
286
+ type: feature
287
+ category: ai
288
+ releasedAt: 2026-02-15T00:00:00Z
289
+ showNewUntil: 2026-03-15T00:00:00Z
290
+ cta:
291
+ label: Try it now
292
+ url: /journal
293
+ ---
294
+ Track decisions and outcomes with AI-powered insights.
295
+ ```
296
+
297
+ ## CMS Adapters
298
+
299
+ Pull releases from your CMS and map them into `FeatureEntry[]`:
300
+
301
+ ```ts
302
+ import { ContentfulAdapter, SanityAdapter, StrapiAdapter, NotionAdapter, MarkdownAdapter } from 'featuredrop'
303
+
304
+ const contentful = new ContentfulAdapter({
305
+ spaceId: process.env.CONTENTFUL_SPACE!,
306
+ accessToken: process.env.CONTENTFUL_TOKEN!,
307
+ contentType: 'featureRelease',
308
+ })
309
+
310
+ const entries = await contentful.load()
311
+ ```
312
+
313
+ Each adapter accepts optional `fieldMapping` overrides so you can map your content model fields without reshaping server responses.
314
+
315
+ ## Notification Bridges
316
+
317
+ Use framework-agnostic bridge helpers to fan out release notifications:
318
+
319
+ ```ts
320
+ import {
321
+ SlackBridge,
322
+ DiscordBridge,
323
+ EmailDigestGenerator,
324
+ WebhookBridge,
325
+ RSSFeedGenerator,
326
+ } from 'featuredrop/bridges'
327
+
328
+ await SlackBridge.notify(feature, { webhookUrl: process.env.SLACK_WEBHOOK! })
329
+ await DiscordBridge.notify(feature, { webhookUrl: process.env.DISCORD_WEBHOOK! })
330
+ await WebhookBridge.post(feature, { url: 'https://api.example.com/hooks/features' })
331
+
332
+ const html = EmailDigestGenerator.generate(features, { title: 'Weekly updates' })
333
+ const rss = RSSFeedGenerator.generate(features, { title: 'Product Updates' })
334
+ ```
335
+
336
+ ## CI Utilities
337
+
338
+ Use `featuredrop/ci` for manifest diffing in pull-request checks:
339
+
340
+ ```ts
341
+ import { diffManifest, generateChangelogDiff, validateManifestForCI } from 'featuredrop/ci'
342
+
343
+ const diff = diffManifest(beforeManifest, afterManifest)
344
+ const summary = generateChangelogDiff(diff, { includeFieldChanges: true })
345
+ const validation = validateManifestForCI(afterManifest)
346
+ ```
347
+
348
+ Run bundle budget checks after `pnpm build`:
349
+
350
+ ```bash
351
+ pnpm size-check
352
+ ```
353
+
354
+ ## Feature Flag Bridges
355
+
356
+ Gate announcement visibility behind flags via `feature.flagKey`:
357
+
358
+ ```ts
359
+ import { createFlagBridge, LaunchDarklyBridge, PostHogBridge } from 'featuredrop/flags'
360
+
361
+ const bridge = createFlagBridge({
362
+ isEnabled: (flagKey) => myFlagService.isOn(flagKey),
363
+ })
364
+
365
+ // Provider + feature entry
366
+ <FeatureDropProvider manifest={FEATURES} storage={storage} flagBridge={bridge} />
367
+ // { id: 'ai-journal', flagKey: 'ai-journal-enabled', ... }
368
+
369
+ // Or vendor bridges
370
+ const ld = new LaunchDarklyBridge(ldClient)
371
+ const ph = new PostHogBridge(posthog)
372
+ ```
373
+
374
+ ## Multi-Product Support
375
+
376
+ Scope visibility by product using `FeatureDropProvider` + `feature.product`:
377
+
378
+ ```tsx
379
+ <FeatureDropProvider manifest={FEATURES} storage={storage} product="askverdict" />
380
+ // feature.product can be "askverdict", "other-product", or "*" for shared entries
381
+ ```
382
+
383
+ ## Admin Components
384
+
385
+ Use optional admin UI primitives via `featuredrop/admin`:
386
+
387
+ ```tsx
388
+ import {
389
+ ManifestEditor,
390
+ ScheduleCalendar,
391
+ PreviewPanel,
392
+ AudienceBuilder,
393
+ } from 'featuredrop/admin'
394
+
395
+ <ManifestEditor features={features} onSave={saveManifest} />
396
+ <ScheduleCalendar features={features} onSchedule={scheduleFeature} />
397
+ <PreviewPanel feature={selectedFeature} components={['badge', 'changelog', 'toast']} />
398
+ <AudienceBuilder segments={['free', 'pro']} roles={['admin', 'viewer']} onSave={saveAudience} />
399
+ ```
400
+
401
+ ## Resilience
402
+
403
+ Built-in React exports are wrapped in internal error boundaries. If a featuredrop component throws, it unmounts itself (no host app crash) and optionally reports through provider `onError`.
404
+
405
+ ## Schema Validation
406
+
407
+ Validate manifest JSON in CI or tooling pipelines.
408
+
409
+ ```ts
410
+ import {
411
+ featureEntrySchema,
412
+ featureEntryJsonSchema,
413
+ validateManifest,
414
+ } from 'featuredrop/schema'
415
+
416
+ featureEntrySchema.parse({
417
+ id: 'ai-journal',
418
+ label: 'AI Journal',
419
+ releasedAt: '2026-02-15T00:00:00Z',
420
+ showNewUntil: '2026-03-15T00:00:00Z',
421
+ })
422
+
423
+ const result = validateManifest(data)
424
+ if (!result.valid) {
425
+ throw new Error(result.errors.map((e) => `${e.path}: ${e.message}`).join("; "))
426
+ }
427
+
428
+ console.log(featureEntryJsonSchema.properties.id.type) // "string"
429
+ ```
430
+
431
+ ## Testing Utilities
432
+
433
+ Use `featuredrop/testing` to speed up unit and component tests.
434
+
435
+ ```tsx
436
+ import { render, screen } from '@testing-library/react'
437
+ import { useNewCount } from 'featuredrop/react'
438
+ import { createMockManifest, createMockStorage, createTestProvider } from 'featuredrop/testing'
439
+
440
+ const manifest = createMockManifest([{ label: 'AI Journal', releasedAt: 'today', showNewUntil: '+14d' }])
441
+ const storage = createMockStorage()
442
+ const Wrapper = createTestProvider({ manifest, storage })
443
+
444
+ function Count() {
445
+ return <span>{useNewCount()}</span>
446
+ }
447
+
448
+ render(<Count />, { wrapper: Wrapper })
449
+ expect(screen.getByText('1')).toBeInTheDocument()
450
+ ```
451
+
452
+ ## Playground & Online Demos
453
+
454
+ Use the lightweight component playground for quick UI iteration:
455
+
456
+ ```bash
457
+ pnpm --dir examples/sandbox-react install
458
+ pnpm playground
459
+ pnpm playground:build
460
+ ```
461
+
462
+ One-click editable demos:
463
+
464
+ - React sandbox source: `examples/sandbox-react`
465
+ - StackBlitz: https://stackblitz.com/github/GLINCKER/featuredrop/tree/main/examples/sandbox-react
466
+ - CodeSandbox: https://codesandbox.io/p/sandbox/github/GLINCKER/featuredrop/tree/main/examples/sandbox-react
467
+
468
+ ## Docs Site (Nextra + Vercel)
469
+
470
+ The repo now includes a docs app scaffold at `apps/docs` for a fast validation launch on Vercel.
471
+
472
+ ```bash
473
+ pnpm docs:install
474
+ pnpm docs:dev
475
+ pnpm docs:build
476
+ ```
477
+
478
+ The docs app is static-export ready, so it can also deploy on Cloudflare Pages or GitHub Pages.
479
+
115
480
  ## Components
116
481
 
117
482
  Everything you'd expect from Beamer or Headway — but free, self-hosted, and headless-first.
@@ -129,6 +494,9 @@ import { ChangelogWidget } from 'featuredrop/react'
129
494
  // Or modal / popover variant
130
495
  <ChangelogWidget variant="modal" title="Release Notes" />
131
496
 
497
+ // Enable emoji reactions on entries
498
+ <ChangelogWidget showReactions />
499
+
132
500
  // Fully headless
133
501
  <ChangelogWidget>
134
502
  {({ isOpen, toggle, features, count, dismissAll }) => (
@@ -177,6 +545,124 @@ import { Toast } from 'featuredrop/react'
177
545
  <Toast featureIds={["ai-journal"]} autoDismissMs={5000} />
178
546
  ```
179
547
 
548
+ ### Product Tours
549
+
550
+ Guided, multi-step onboarding with keyboard navigation and persistence.
551
+
552
+ ```tsx
553
+ import { Tour, useTour } from 'featuredrop/react'
554
+
555
+ <Tour id="onboarding" steps={steps} />
556
+ const { startTour, nextStep, skipTour } = useTour('onboarding')
557
+ ```
558
+
559
+ ### Onboarding Checklist
560
+
561
+ Task-based onboarding that can trigger tours, links, or callbacks.
562
+
563
+ ```tsx
564
+ import { Checklist, useChecklist } from 'featuredrop/react'
565
+
566
+ <Checklist id="getting-started" tasks={tasks} />
567
+ const { completeTask, progress } = useChecklist('getting-started')
568
+ ```
569
+
570
+ ### Feedback Widget
571
+
572
+ Collect lightweight in-app feedback with optional emoji and screenshots.
573
+
574
+ ```tsx
575
+ import { FeedbackWidget } from 'featuredrop/react'
576
+
577
+ <FeedbackWidget
578
+ featureId="ai-journal"
579
+ onSubmit={async (payload) => {
580
+ await fetch('/api/feedback', { method: 'POST', body: JSON.stringify(payload) })
581
+ }}
582
+ showEmoji
583
+ showScreenshot
584
+ rateLimit="1-per-feature"
585
+ />
586
+ ```
587
+
588
+ ### Survey (NPS / CSAT / CES / Custom)
589
+
590
+ Run micro-surveys with trigger rules and imperative controls.
591
+
592
+ ```tsx
593
+ import { Survey, useSurvey } from 'featuredrop/react'
594
+
595
+ <Survey
596
+ id="nps-main"
597
+ type="nps"
598
+ prompt="How likely are you to recommend us?"
599
+ triggerRules={{ minDaysSinceSignup: 7, signupAt: user.createdAt }}
600
+ onSubmit={saveSurvey}
601
+ />
602
+
603
+ const { show } = useSurvey('nps-main')
604
+ ```
605
+
606
+ ### Feature Request Voting
607
+
608
+ Capture and rank product requests with local persistence and optional webhook sync.
609
+
610
+ ```tsx
611
+ import { FeatureRequestButton, FeatureRequestForm } from 'featuredrop/react'
612
+
613
+ <FeatureRequestButton featureId="dark-mode" requestTitle="Dark mode" />
614
+
615
+ <FeatureRequestForm
616
+ categories={['UI', 'Performance', 'Integration', 'Other']}
617
+ onSubmit={async (request) => {
618
+ await fetch('/api/requests', { method: 'POST', body: JSON.stringify(request) })
619
+ }}
620
+ />
621
+ ```
622
+
623
+ ### Hotspots & Tooltips
624
+
625
+ Persistent contextual hints attached to specific UI targets.
626
+
627
+ ```tsx
628
+ import { Hotspot, TooltipGroup } from 'featuredrop/react'
629
+
630
+ <TooltipGroup maxVisible={1}>
631
+ <Hotspot id="export-help" target="#export-btn" frequency="once">
632
+ Export supports CSV, PDF, and Excel.
633
+ </Hotspot>
634
+ </TooltipGroup>
635
+ ```
636
+
637
+ ### Announcement Modal
638
+
639
+ Priority-based modal announcements with optional slide carousel.
640
+
641
+ ```tsx
642
+ import { AnnouncementModal } from 'featuredrop/react'
643
+
644
+ <AnnouncementModal
645
+ feature={criticalFeature}
646
+ trigger="auto"
647
+ frequency="once"
648
+ />
649
+ ```
650
+
651
+ ### Spotlight Chain
652
+
653
+ Lightweight chained spotlights for "here are 3 new things" flows.
654
+
655
+ ```tsx
656
+ import { SpotlightChain } from 'featuredrop/react'
657
+
658
+ <SpotlightChain
659
+ steps={[
660
+ { target: '#sidebar', content: 'New navigation' },
661
+ { target: '#search', content: 'Global search' },
662
+ ]}
663
+ />
664
+ ```
665
+
180
666
  ### NewBadge
181
667
 
182
668
  Headless badge component with variants. Zero CSS framework dependency.
@@ -211,7 +697,27 @@ npm install featuredrop react # react is an optional peer dep
211
697
  ```tsx
212
698
  import { FeatureDropProvider } from 'featuredrop/react'
213
699
 
214
- <FeatureDropProvider manifest={FEATURES} storage={storage}>
700
+ <FeatureDropProvider manifest={FEATURES} storage={storage} appVersion="2.5.1">
701
+ <App />
702
+ </FeatureDropProvider>
703
+ ```
704
+
705
+ **Throttling + quiet mode:**
706
+
707
+ ```tsx
708
+ <FeatureDropProvider
709
+ manifest={FEATURES}
710
+ storage={storage}
711
+ throttle={{
712
+ maxSimultaneousBadges: 3,
713
+ maxSimultaneousSpotlights: 1,
714
+ maxToastsPerSession: 3,
715
+ minTimeBetweenModals: 30000,
716
+ minTimeBetweenTours: 86400000,
717
+ sessionCooldown: 5000,
718
+ respectDoNotDisturb: true,
719
+ }}
720
+ >
215
721
  <App />
216
722
  </FeatureDropProvider>
217
723
  ```
@@ -248,11 +754,28 @@ import { ChangelogWidget } from 'featuredrop/react'
248
754
  | `useNewFeature(key)` | Single nav item: `{ isNew, feature, dismiss }` |
249
755
  | `useNewCount()` | Just the badge count |
250
756
  | `useTabNotification()` | Updates browser tab title with count |
757
+ | `useTour(id)` | Imperative tour controls and current step snapshot |
758
+ | `useTourSequencer(sequence)` | Ordered multi-tour orchestration by feature readiness |
759
+ | `useChecklist(id)` | Imperative checklist controls + progress |
760
+ | `useSurvey(id)` | Imperative survey controls (`show`, `hide`, `askLater`) + state |
251
761
  | `<NewBadge />` | Headless badge: `pill`, `dot`, or `count` variant |
252
- | `<ChangelogWidget />` | Full changelog feed with trigger button |
762
+ | `<ChangelogWidget />` | Full changelog feed with trigger button + optional reactions |
763
+ | `<ChangelogPage />` | Full-page changelog with filters/search/pagination |
253
764
  | `<Spotlight />` | Pulsing beacon attached to DOM elements |
254
765
  | `<Banner />` | Announcement banner with variants |
255
766
  | `<Toast />` | Stackable toast notifications |
767
+ | `<Tour />` | Multi-step guided product tour |
768
+ | `<Checklist />` | Onboarding task checklist |
769
+ | `<FeedbackWidget />` | In-app feedback form with category/emoji/screenshot support |
770
+ | `<Survey />` | NPS/CSAT/CES/custom survey engine with trigger rules |
771
+ | `<FeatureRequestButton />` | Per-feature voting button with persisted vote guard |
772
+ | `<FeatureRequestForm />` | Request capture form + sortable request list |
773
+ | `<Hotspot />` / `<TooltipGroup />` | Contextual tooltips with visibility caps |
774
+ | `<AnnouncementModal />` | Priority/frequency-gated modal announcements |
775
+ | `<SpotlightChain />` | Lightweight chained spotlight walkthrough |
776
+
777
+ `useFeatureDrop()` also exposes queue/throttle controls: `queuedFeatures`, `totalNewCount`, `quietMode`, `setQuietMode`, `markFeatureSeen`, `markFeatureClicked`, toast-slot helpers, modal/tour pacing checks, and spotlight slot controls.
778
+ It also exposes trigger runtime helpers: `trackUsageEvent`, `trackTriggerEvent`, `trackMilestone`, and `setTriggerPath`.
256
779
 
257
780
  **Analytics integration:**
258
781
 
@@ -260,6 +783,7 @@ import { ChangelogWidget } from 'featuredrop/react'
260
783
  <FeatureDropProvider
261
784
  manifest={FEATURES}
262
785
  storage={storage}
786
+ appVersion="2.5.1" // optional semver for version-pinned features
263
787
  analytics={{
264
788
  onFeatureSeen: (f) => posthog.capture('feature_seen', { id: f.id }),
265
789
  onFeatureDismissed: (f) => posthog.capture('feature_dismissed', { id: f.id }),
@@ -311,7 +835,12 @@ Read the full [Architecture doc](docs/ARCHITECTURE.md) for cross-device sync flo
311
835
  | **Price** | **Free** | $59-399/mo | $49-249/mo | $79-299/mo | $79+ |
312
836
  | Auto-expiring badges | Yes | - | - | - | - |
313
837
  | Changelog widget | Yes | Yes | Yes | Yes | Yes |
838
+ | Product tours | Yes | - | - | - | - |
839
+ | Onboarding checklists | Yes | - | - | - | - |
314
840
  | Spotlight/beacon | Yes | - | - | - | - |
841
+ | Hotspot tooltips | Yes | - | - | - | - |
842
+ | Announcement modal | Yes | - | - | - | - |
843
+ | Spotlight chaining | Yes | - | - | - | - |
315
844
  | Toast notifications | Yes | - | - | - | - |
316
845
  | Announcement banner | Yes | - | - | - | - |
317
846
  | Tab title notification | Yes | - | - | - | - |
@@ -334,20 +863,34 @@ Read the full [Architecture doc](docs/ARCHITECTURE.md) for cross-device sync flo
334
863
  |-----------|--------|--------|
335
864
  | React / Next.js | Stable | `featuredrop/react` |
336
865
  | Vanilla JS | Stable | `featuredrop` |
337
- | Vue 3 | Planned | `featuredrop/vue` |
338
- | Svelte 5 | Planned | `featuredrop/svelte` |
866
+ | SolidJS | Preview | `featuredrop/solid` |
867
+ | Preact (compat) | Preview | `featuredrop/preact` |
868
+ | Web Components | Preview | `featuredrop/web-components` |
869
+ | Angular | Preview | `featuredrop/angular` |
870
+ | Vue 3 | Preview | `featuredrop/vue` |
871
+ | Svelte 5 | Preview (store bindings) | `featuredrop/svelte` |
339
872
 
340
873
  ## Documentation
341
874
 
875
+ - [Docs App Source](apps/docs/) — Next.js + Nextra site scaffold (`/`, `/docs`, `/playground`)
876
+ - [Docs Site Guide](docs/DOCS_SITE.md) — Local commands and Vercel deployment setup
342
877
  - [API Reference](docs/API.md) — All functions, adapters, hooks, components
878
+ - [Recipes](docs/RECIPES.md) — Copy-paste integration patterns (sidebar badge, tours, migration, A/B tests)
343
879
  - [Architecture](docs/ARCHITECTURE.md) — Three-check algorithm, cross-device sync, custom adapters
344
880
  - [Next.js Example](examples/nextjs/) — Full App Router integration
345
881
  - [Vanilla Example](examples/vanilla/) — Plain HTML, zero build step
882
+ - [React Sandbox](examples/sandbox-react/) — Interactive local/online playground
346
883
 
347
884
  ## Contributing
348
885
 
349
886
  See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, commit conventions, and how releases work.
350
887
 
888
+ ## Security
889
+
890
+ - Report vulnerabilities privately via [SECURITY.md](SECURITY.md).
891
+ - CI includes CodeQL static analysis on pull requests and `main`.
892
+ - `pnpm security-check` scans runtime source for unsafe execution/rendering patterns.
893
+
351
894
  ## License
352
895
 
353
896
  MIT &copy; [Glincker](https://glincker.com)