@burdenoff/website-sdk 2026.1101.1 → 2026.1101.3

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 (276) hide show
  1. package/README.md +52 -11
  2. package/dist/analytics/index.js +19 -69
  3. package/dist/analytics/index.js.map +1 -1
  4. package/dist/analytics/index.mjs +3 -69
  5. package/dist/analytics/index.mjs.map +1 -1
  6. package/dist/avatar-live/index.js +1 -0
  7. package/dist/avatar-live/index.js.map +1 -1
  8. package/dist/avatar-live/index.mjs +1 -0
  9. package/dist/avatar-live/index.mjs.map +1 -1
  10. package/dist/avatar-live-view/index.js +25 -30
  11. package/dist/avatar-live-view/index.js.map +1 -1
  12. package/dist/avatar-live-view/index.mjs +20 -25
  13. package/dist/avatar-live-view/index.mjs.map +1 -1
  14. package/dist/chunk-23H4JUL4.js +41 -0
  15. package/dist/chunk-23H4JUL4.js.map +1 -0
  16. package/dist/chunk-34RX5P75.mjs +25 -0
  17. package/dist/chunk-34RX5P75.mjs.map +1 -0
  18. package/dist/chunk-3LFFY7AZ.js +184 -0
  19. package/dist/chunk-3LFFY7AZ.js.map +1 -0
  20. package/dist/chunk-3X26GE4S.js +2794 -0
  21. package/dist/chunk-3X26GE4S.js.map +1 -0
  22. package/dist/chunk-45GCA4GG.js +85 -0
  23. package/dist/chunk-45GCA4GG.js.map +1 -0
  24. package/dist/chunk-4SAGVLHN.js +876 -0
  25. package/dist/chunk-4SAGVLHN.js.map +1 -0
  26. package/dist/chunk-4U6YAII5.js +218 -0
  27. package/dist/chunk-4U6YAII5.js.map +1 -0
  28. package/dist/chunk-5GMTBOO6.js +21 -0
  29. package/dist/chunk-5GMTBOO6.js.map +1 -0
  30. package/dist/chunk-5JRJ57CL.mjs +329 -0
  31. package/dist/chunk-5JRJ57CL.mjs.map +1 -0
  32. package/dist/chunk-6C2DKIC7.js +4 -0
  33. package/dist/chunk-6C2DKIC7.js.map +1 -0
  34. package/dist/chunk-6IBFBWC5.mjs +17 -0
  35. package/dist/chunk-6IBFBWC5.mjs.map +1 -0
  36. package/dist/chunk-6KY2ZRVI.js +116 -0
  37. package/dist/chunk-6KY2ZRVI.js.map +1 -0
  38. package/dist/chunk-6VJT3SM4.mjs +182 -0
  39. package/dist/chunk-6VJT3SM4.mjs.map +1 -0
  40. package/dist/chunk-6YZUFMK2.mjs +40 -0
  41. package/dist/chunk-6YZUFMK2.mjs.map +1 -0
  42. package/dist/chunk-7A6R7CGG.js +47 -0
  43. package/dist/chunk-7A6R7CGG.js.map +1 -0
  44. package/dist/chunk-7PWL4V2R.js +4 -0
  45. package/dist/chunk-7PWL4V2R.js.map +1 -0
  46. package/dist/chunk-AA33WDYS.js +161 -0
  47. package/dist/chunk-AA33WDYS.js.map +1 -0
  48. package/dist/chunk-AHRGFXV2.js +525 -0
  49. package/dist/chunk-AHRGFXV2.js.map +1 -0
  50. package/dist/chunk-B63XLDZH.mjs +109 -0
  51. package/dist/chunk-B63XLDZH.mjs.map +1 -0
  52. package/dist/chunk-BX2USEWF.mjs +35 -0
  53. package/dist/chunk-BX2USEWF.mjs.map +1 -0
  54. package/dist/chunk-BYSZT35R.js +150 -0
  55. package/dist/chunk-BYSZT35R.js.map +1 -0
  56. package/dist/chunk-C6OJIHNO.js +12 -0
  57. package/dist/chunk-C6OJIHNO.js.map +1 -0
  58. package/dist/chunk-COOELBX7.mjs +63 -0
  59. package/dist/chunk-COOELBX7.mjs.map +1 -0
  60. package/dist/chunk-D2TW7MNC.mjs +2771 -0
  61. package/dist/chunk-D2TW7MNC.mjs.map +1 -0
  62. package/dist/chunk-D5KEZ6V6.mjs +3 -0
  63. package/dist/chunk-D5KEZ6V6.mjs.map +1 -0
  64. package/dist/chunk-E3GUGJMY.js +46 -0
  65. package/dist/chunk-E3GUGJMY.js.map +1 -0
  66. package/dist/chunk-EBZ2HGJ2.mjs +52 -0
  67. package/dist/chunk-EBZ2HGJ2.mjs.map +1 -0
  68. package/dist/chunk-EDLTUOIA.mjs +210 -0
  69. package/dist/chunk-EDLTUOIA.mjs.map +1 -0
  70. package/dist/chunk-EJQSOT4X.js +4 -0
  71. package/dist/chunk-EJQSOT4X.js.map +1 -0
  72. package/dist/chunk-EUPFS6MZ.mjs +2019 -0
  73. package/dist/chunk-EUPFS6MZ.mjs.map +1 -0
  74. package/dist/chunk-F3XWDC3B.js +77 -0
  75. package/dist/chunk-F3XWDC3B.js.map +1 -0
  76. package/dist/chunk-FFADMVIK.mjs +5127 -0
  77. package/dist/chunk-FFADMVIK.mjs.map +1 -0
  78. package/dist/chunk-FJWS7BKT.mjs +3 -0
  79. package/dist/chunk-FJWS7BKT.mjs.map +1 -0
  80. package/dist/chunk-FJZJDUOR.mjs +3 -0
  81. package/dist/chunk-FJZJDUOR.mjs.map +1 -0
  82. package/dist/chunk-GCFLCO2Z.mjs +145 -0
  83. package/dist/chunk-GCFLCO2Z.mjs.map +1 -0
  84. package/dist/chunk-GEXC4LRP.js +61 -0
  85. package/dist/chunk-GEXC4LRP.js.map +1 -0
  86. package/dist/chunk-GTRAXQGC.mjs +3 -0
  87. package/dist/chunk-GTRAXQGC.mjs.map +1 -0
  88. package/dist/chunk-IKOIRUKW.js +270 -0
  89. package/dist/chunk-IKOIRUKW.js.map +1 -0
  90. package/dist/chunk-IXTWOPRN.js +45 -0
  91. package/dist/chunk-IXTWOPRN.js.map +1 -0
  92. package/dist/chunk-JDXOXGOK.js +547 -0
  93. package/dist/chunk-JDXOXGOK.js.map +1 -0
  94. package/dist/chunk-JSLDWYV7.mjs +558 -0
  95. package/dist/chunk-JSLDWYV7.mjs.map +1 -0
  96. package/dist/chunk-JXLSTVBK.js +336 -0
  97. package/dist/chunk-JXLSTVBK.js.map +1 -0
  98. package/dist/chunk-KHYXK7FG.mjs +49 -0
  99. package/dist/chunk-KHYXK7FG.mjs.map +1 -0
  100. package/dist/chunk-KSMBOWQQ.js +2034 -0
  101. package/dist/chunk-KSMBOWQQ.js.map +1 -0
  102. package/dist/chunk-KU2PXHGB.mjs +83 -0
  103. package/dist/chunk-KU2PXHGB.mjs.map +1 -0
  104. package/dist/chunk-KZANIV3S.mjs +37 -0
  105. package/dist/chunk-KZANIV3S.mjs.map +1 -0
  106. package/dist/chunk-L4BP6YOB.js +20 -0
  107. package/dist/chunk-L4BP6YOB.js.map +1 -0
  108. package/dist/chunk-MRKA2OAF.mjs +75 -0
  109. package/dist/chunk-MRKA2OAF.mjs.map +1 -0
  110. package/dist/chunk-N5EC53MH.mjs +80 -0
  111. package/dist/chunk-N5EC53MH.mjs.map +1 -0
  112. package/dist/chunk-NZANQGDM.mjs +34 -0
  113. package/dist/chunk-NZANQGDM.mjs.map +1 -0
  114. package/dist/chunk-O4GS3XPZ.js +10 -0
  115. package/dist/chunk-O4GS3XPZ.js.map +1 -0
  116. package/dist/chunk-O76QDGRM.js +5154 -0
  117. package/dist/chunk-O76QDGRM.js.map +1 -0
  118. package/dist/chunk-OYEUNZJY.js +114 -0
  119. package/dist/chunk-OYEUNZJY.js.map +1 -0
  120. package/dist/chunk-PHXAGQNB.js +74 -0
  121. package/dist/chunk-PHXAGQNB.js.map +1 -0
  122. package/dist/chunk-PIHHPP7J.mjs +16 -0
  123. package/dist/chunk-PIHHPP7J.mjs.map +1 -0
  124. package/dist/chunk-PXRHVVOU.mjs +499 -0
  125. package/dist/chunk-PXRHVVOU.mjs.map +1 -0
  126. package/dist/chunk-QMA6U6AU.js +36 -0
  127. package/dist/chunk-QMA6U6AU.js.map +1 -0
  128. package/dist/chunk-R6J7IULF.js +44 -0
  129. package/dist/chunk-R6J7IULF.js.map +1 -0
  130. package/dist/chunk-R7G6TWIS.mjs +10 -0
  131. package/dist/chunk-R7G6TWIS.mjs.map +1 -0
  132. package/dist/chunk-RUMGFI6L.mjs +544 -0
  133. package/dist/chunk-RUMGFI6L.mjs.map +1 -0
  134. package/dist/chunk-S54JWSJF.js +565 -0
  135. package/dist/chunk-S54JWSJF.js.map +1 -0
  136. package/dist/chunk-SK5UGG6A.js +2041 -0
  137. package/dist/chunk-SK5UGG6A.js.map +1 -0
  138. package/dist/chunk-SKIYDKIS.mjs +70 -0
  139. package/dist/chunk-SKIYDKIS.mjs.map +1 -0
  140. package/dist/chunk-SZNS5IK6.mjs +322 -0
  141. package/dist/chunk-SZNS5IK6.mjs.map +1 -0
  142. package/dist/chunk-T7JIPZAC.js +66 -0
  143. package/dist/chunk-T7JIPZAC.js.map +1 -0
  144. package/dist/chunk-TGYQHO2E.mjs +864 -0
  145. package/dist/chunk-TGYQHO2E.mjs.map +1 -0
  146. package/dist/chunk-TT3MHN42.mjs +8 -0
  147. package/dist/chunk-TT3MHN42.mjs.map +1 -0
  148. package/dist/chunk-UM55U65W.mjs +268 -0
  149. package/dist/chunk-UM55U65W.mjs.map +1 -0
  150. package/dist/chunk-UQ2HALUD.js +72 -0
  151. package/dist/chunk-UQ2HALUD.js.map +1 -0
  152. package/dist/chunk-UXWT7G77.js +82 -0
  153. package/dist/chunk-UXWT7G77.js.map +1 -0
  154. package/dist/chunk-VNS5WDWO.mjs +7 -0
  155. package/dist/chunk-VNS5WDWO.mjs.map +1 -0
  156. package/dist/chunk-VRWZLGR7.js +4 -0
  157. package/dist/chunk-VRWZLGR7.js.map +1 -0
  158. package/dist/chunk-VZLDSNH4.mjs +22 -0
  159. package/dist/chunk-VZLDSNH4.mjs.map +1 -0
  160. package/dist/chunk-W6P3YETJ.js +92 -0
  161. package/dist/chunk-W6P3YETJ.js.map +1 -0
  162. package/dist/chunk-WPKFFQCR.mjs +90 -0
  163. package/dist/chunk-WPKFFQCR.mjs.map +1 -0
  164. package/dist/chunk-WTVTI5TO.mjs +22 -0
  165. package/dist/chunk-WTVTI5TO.mjs.map +1 -0
  166. package/dist/chunk-XJOBZ4RJ.js +44 -0
  167. package/dist/chunk-XJOBZ4RJ.js.map +1 -0
  168. package/dist/chunk-XP2JDNDU.js +325 -0
  169. package/dist/chunk-XP2JDNDU.js.map +1 -0
  170. package/dist/chunk-Y4V5CEWD.mjs +112 -0
  171. package/dist/chunk-Y4V5CEWD.mjs.map +1 -0
  172. package/dist/chunk-YERN2UWM.js +116 -0
  173. package/dist/chunk-YERN2UWM.js.map +1 -0
  174. package/dist/chunk-ZKJQWFAA.mjs +114 -0
  175. package/dist/chunk-ZKJQWFAA.mjs.map +1 -0
  176. package/dist/chunk-ZOON3S3Y.mjs +138 -0
  177. package/dist/chunk-ZOON3S3Y.mjs.map +1 -0
  178. package/dist/chunk-ZP6QRAYC.mjs +2024 -0
  179. package/dist/chunk-ZP6QRAYC.mjs.map +1 -0
  180. package/dist/chunk-ZSTFX7FV.js +9 -0
  181. package/dist/chunk-ZSTFX7FV.js.map +1 -0
  182. package/dist/client/index.js +23 -331
  183. package/dist/client/index.js.map +1 -1
  184. package/dist/client/index.mjs +3 -330
  185. package/dist/client/index.mjs.map +1 -1
  186. package/dist/comparison/index.js +28 -111
  187. package/dist/comparison/index.js.map +1 -1
  188. package/dist/comparison/index.mjs +2 -107
  189. package/dist/comparison/index.mjs.map +1 -1
  190. package/dist/components/contact.js +21 -931
  191. package/dist/components/contact.js.map +1 -1
  192. package/dist/components/contact.mjs +12 -909
  193. package/dist/components/contact.mjs.map +1 -1
  194. package/dist/components/electron-download.js +12 -124
  195. package/dist/components/electron-download.js.map +1 -1
  196. package/dist/components/electron-download.mjs +4 -106
  197. package/dist/components/electron-download.mjs.map +1 -1
  198. package/dist/components/explore-cta.js +8 -112
  199. package/dist/components/explore-cta.js.map +1 -1
  200. package/dist/components/explore-cta.mjs +3 -114
  201. package/dist/components/explore-cta.mjs.map +1 -1
  202. package/dist/components/explore.js +62 -7833
  203. package/dist/components/explore.js.map +1 -1
  204. package/dist/components/explore.mjs +13 -7800
  205. package/dist/components/explore.mjs.map +1 -1
  206. package/dist/components/launch-countdown.js +8 -83
  207. package/dist/components/launch-countdown.js.map +1 -1
  208. package/dist/components/launch-countdown.mjs +3 -85
  209. package/dist/components/launch-countdown.mjs.map +1 -1
  210. package/dist/components/newsletter.js +20 -993
  211. package/dist/components/newsletter.js.map +1 -1
  212. package/dist/components/newsletter.mjs +11 -971
  213. package/dist/components/newsletter.mjs.map +1 -1
  214. package/dist/components/partners.js +24 -1302
  215. package/dist/components/partners.js.map +1 -1
  216. package/dist/components/partners.mjs +15 -1276
  217. package/dist/components/partners.mjs.map +1 -1
  218. package/dist/components/pricing.js +49 -1249
  219. package/dist/components/pricing.js.map +1 -1
  220. package/dist/components/pricing.mjs +8 -1226
  221. package/dist/components/pricing.mjs.map +1 -1
  222. package/dist/components/problems-solved.js +8 -150
  223. package/dist/components/problems-solved.js.map +1 -1
  224. package/dist/components/problems-solved.mjs +4 -133
  225. package/dist/components/problems-solved.mjs.map +1 -1
  226. package/dist/components/resource-links.js +8 -304
  227. package/dist/components/resource-links.js.map +1 -1
  228. package/dist/components/resource-links.mjs +5 -308
  229. package/dist/components/resource-links.mjs.map +1 -1
  230. package/dist/components/social-links.js +8 -343
  231. package/dist/components/social-links.js.map +1 -1
  232. package/dist/components/social-links.mjs +5 -347
  233. package/dist/components/social-links.mjs.map +1 -1
  234. package/dist/components/unsubscribe.js +10 -627
  235. package/dist/components/unsubscribe.js.map +1 -1
  236. package/dist/components/unsubscribe.mjs +6 -611
  237. package/dist/components/unsubscribe.mjs.map +1 -1
  238. package/dist/components/waitlist.js +21 -1001
  239. package/dist/components/waitlist.js.map +1 -1
  240. package/dist/components/waitlist.mjs +12 -982
  241. package/dist/components/waitlist.mjs.map +1 -1
  242. package/dist/components/website-switcher.js +12 -175
  243. package/dist/components/website-switcher.js.map +1 -1
  244. package/dist/components/website-switcher.mjs +4 -157
  245. package/dist/components/website-switcher.mjs.map +1 -1
  246. package/dist/content/index.js +118 -3616
  247. package/dist/content/index.js.map +1 -1
  248. package/dist/content/index.mjs +12 -3567
  249. package/dist/content/index.mjs.map +1 -1
  250. package/dist/hooks/index.js +37 -2321
  251. package/dist/hooks/index.js.map +1 -1
  252. package/dist/hooks/index.mjs +6 -2318
  253. package/dist/hooks/index.mjs.map +1 -1
  254. package/dist/index.d.mts +1 -1
  255. package/dist/index.d.ts +1 -1
  256. package/dist/index.js +4145 -20599
  257. package/dist/index.js.map +1 -1
  258. package/dist/index.mjs +3766 -20567
  259. package/dist/index.mjs.map +1 -1
  260. package/dist/product/index.js +20 -393
  261. package/dist/product/index.js.map +1 -1
  262. package/dist/product/index.mjs +5 -394
  263. package/dist/product/index.mjs.map +1 -1
  264. package/dist/seo/index.js +8 -76
  265. package/dist/seo/index.js.map +1 -1
  266. package/dist/seo/index.mjs +3 -78
  267. package/dist/seo/index.mjs.map +1 -1
  268. package/dist/store/index.js +51 -2650
  269. package/dist/store/index.js.map +1 -1
  270. package/dist/store/index.mjs +13 -2619
  271. package/dist/store/index.mjs.map +1 -1
  272. package/dist/utils/index.js +69 -134
  273. package/dist/utils/index.js.map +1 -1
  274. package/dist/utils/index.mjs +5 -122
  275. package/dist/utils/index.mjs.map +1 -1
  276. package/package.json +2 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/social-links.tsx"],"names":["jsx","Twitter","Linkedin","Github","Facebook","Youtube","Instagram","useProduct","jsxs","Mail"],"mappings":"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","file":"chunk-YERN2UWM.js","sourcesContent":["\"use client\";\n\n/**\n * SocialLinks Component\n *\n * Renders a row of social-media icons sourced from the **product CMS**.\n * The `socialLinks` field on `Product` (and `PublicProduct`) is a free-form\n * JSON record: `{ twitter, github, linkedin, facebook, youtube, instagram,\n * discord, pinterest, … }`. The admin sets per-product URLs in\n * `microfe-product → Products → <product>`; this component reads them via\n * `useProduct()` and renders icons for the platforms we know how to map.\n *\n * Unknown keys are **silently ignored** — the schema accepts any string key\n * so new platforms can be wired in admin without an SDK change, but they\n * won't render until this map grows.\n *\n * Why a component instead of inlining in each product website's Footer\n *\n * - Single source of truth for the platform → icon mapping.\n * - One swap to add a new platform (e.g. Threads, Bluesky) across all 5\n * product websites.\n * - Eliminates the drift today between hard-coded URLs and CMS values\n * (e.g. vibecontrols-website Footer had `twitter.com/vibecontrols`\n * hard-coded while CMS had `twitter.com/burdenoff`).\n *\n * @example\n * ```tsx\n * import { SocialLinks } from '@burdenoff/website-sdk'\n *\n * // Auto-derives from useProduct().product.socialLinks\n * <SocialLinks />\n *\n * // Or pass explicitly\n * <SocialLinks\n * links={{ twitter: 'https://twitter.com/example', github: 'https://github.com/example' }}\n * email=\"contact@example.com\"\n * />\n *\n * // Restrict + reorder\n * <SocialLinks platforms={['github', 'twitter', 'linkedin']} />\n * ```\n */\n\nimport { type ComponentType } from \"react\";\n\nimport {\n Facebook,\n Github,\n Instagram,\n Linkedin,\n Mail,\n Twitter,\n Youtube,\n} from \"lucide-react\";\n\nimport { useProduct } from \"../product/product-provider\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Platform keys the component knows how to render. Extends as we add icons.\n * Note: this is intentionally a small allowlist; the CMS JSON schema is\n * open-ended and the component silently skips unknown keys.\n */\nexport type SocialPlatform =\n | \"twitter\"\n | \"linkedin\"\n | \"github\"\n | \"facebook\"\n | \"youtube\"\n | \"instagram\"\n | \"discord\"\n | \"pinterest\";\n\nexport interface SocialLinksProps {\n /**\n * Map of platform → URL. If unset, reads from `useProduct().product.socialLinks`.\n * Useful for passing static config when there's no `<ProductProvider>` upstream.\n */\n links?: Record<string, string> | null;\n /**\n * Contact email — renders a Mail icon linking to `mailto:…`. If unset,\n * reads from `useProduct().product.contactEmail`. Pass `null` to suppress.\n */\n email?: string | null;\n /**\n * Allowlist + ORDER of platforms to render. Defaults to the natural CMS\n * order (whatever the JSON returns). Use this to pin a stable visual order\n * across products that may have different platform sets.\n */\n platforms?: readonly SocialPlatform[];\n /** Whether to render the Mail icon for `email`. Default: `true` when an email is available. */\n showEmail?: boolean;\n /** Tailwind class controlling icon size. Default: `\"h-5 w-5\"`. */\n iconClassName?: string;\n /** Tailwind class for each `<a>` wrapping link. Defaults to a muted-foreground / hover-foreground transition. */\n itemClassName?: string;\n /** Tailwind class for the outer container. Default: `\"flex space-x-4\"`. */\n className?: string;\n}\n\n// ============================================================================\n// Platform → icon map\n// ============================================================================\n\n/**\n * Custom inline SVG for platforms not covered by lucide-react.\n * Stays minimal — single-path icons keep bundle size tight.\n */\nfunction DiscordIcon({ className }: { className?: string }) {\n return (\n <svg\n role=\"img\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden=\"true\"\n >\n <path d=\"M20.317 4.369A19.79 19.79 0 0 0 16.558 3a.075.075 0 0 0-.079.037c-.34.6-.717 1.382-.98 1.99a18.27 18.27 0 0 0-5.998 0 12.51 12.51 0 0 0-.99-1.99.077.077 0 0 0-.079-.037 19.74 19.74 0 0 0-3.76 1.369.07.07 0 0 0-.032.028C.533 8.045-.32 11.6.099 15.114a.082.082 0 0 0 .031.056 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.027 14.09 14.09 0 0 0 1.226-1.994.076.076 0 0 0-.041-.105 13.1 13.1 0 0 1-1.872-.892.077.077 0 0 1-.008-.128c.126-.094.252-.192.372-.291a.074.074 0 0 1 .078-.01c3.927 1.793 8.18 1.793 12.061 0a.074.074 0 0 1 .079.009c.12.099.246.198.372.292a.077.077 0 0 1-.006.127 12.3 12.3 0 0 1-1.873.892.077.077 0 0 0-.041.106c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.84 19.84 0 0 0 6.002-3.03.077.077 0 0 0 .032-.055c.5-4.063-.838-7.587-3.549-10.717a.061.061 0 0 0-.031-.028zM8.02 13.001c-1.182 0-2.157-1.085-2.157-2.419 0-1.333.956-2.418 2.157-2.418 1.21 0 2.176 1.094 2.157 2.418 0 1.334-.956 2.419-2.157 2.419zm7.974 0c-1.182 0-2.157-1.085-2.157-2.419 0-1.333.956-2.418 2.157-2.418 1.21 0 2.176 1.094 2.157 2.418 0 1.334-.946 2.419-2.157 2.419z\" />\n </svg>\n );\n}\n\nfunction PinterestIcon({ className }: { className?: string }) {\n return (\n <svg\n role=\"img\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden=\"true\"\n >\n <path d=\"M12.017 0C5.396 0 .029 5.367.029 11.987c0 5.079 3.158 9.417 7.618 11.162-.105-.949-.199-2.403.041-3.439.219-.937 1.406-5.957 1.406-5.957s-.359-.72-.359-1.781c0-1.663.967-2.911 2.168-2.911 1.024 0 1.518.769 1.518 1.688 0 1.029-.653 2.567-.992 3.992-.285 1.193.6 2.165 1.775 2.165 2.128 0 3.768-2.245 3.768-5.487 0-2.861-2.063-4.869-5.008-4.869-3.41 0-5.409 2.562-5.409 5.199 0 1.033.394 2.143.889 2.741.099.12.112.225.085.345-.09.375-.293 1.199-.334 1.363-.053.225-.172.271-.401.165-1.495-.69-2.433-2.878-2.433-4.646 0-3.776 2.748-7.252 7.92-7.252 4.158 0 7.392 2.967 7.392 6.923 0 4.135-2.607 7.462-6.233 7.462-1.214 0-2.357-.629-2.75-1.378l-.748 2.853c-.271 1.043-1.002 2.35-1.492 3.146C9.57 23.812 10.763 24 12.017 24c6.624 0 11.99-5.367 11.99-11.987C24.007 5.367 18.641 0 12.017 0z\" />\n </svg>\n );\n}\n\ninterface PlatformDef {\n /** lucide-react component or custom SVG component */\n Icon: ComponentType<{ className?: string }>;\n /** Accessible label for the link */\n label: string;\n /** Optional alias keys that should also resolve to this platform */\n aliases?: readonly string[];\n}\n\nconst PLATFORMS: Record<SocialPlatform, PlatformDef> = {\n twitter: { Icon: Twitter, label: \"Twitter / X\", aliases: [\"x\"] },\n linkedin: { Icon: Linkedin, label: \"LinkedIn\" },\n github: { Icon: Github, label: \"GitHub\" },\n facebook: { Icon: Facebook, label: \"Facebook\" },\n youtube: { Icon: Youtube, label: \"YouTube\" },\n instagram: { Icon: Instagram, label: \"Instagram\" },\n discord: { Icon: DiscordIcon, label: \"Discord\" },\n pinterest: { Icon: PinterestIcon, label: \"Pinterest\" },\n};\n\n/**\n * Build a `key → SocialPlatform` lookup so aliases (e.g. `\"x\"` → `\"twitter\"`)\n * resolve in O(1). Memoised at module scope; never recomputed.\n */\nconst KEY_TO_PLATFORM: Record<string, SocialPlatform> = (() => {\n const map: Record<string, SocialPlatform> = {};\n for (const [key, def] of Object.entries(PLATFORMS) as [\n SocialPlatform,\n PlatformDef,\n ][]) {\n map[key] = key;\n for (const alias of def.aliases ?? []) {\n map[alias] = key;\n }\n }\n return map;\n})();\n\nconst DEFAULT_ITEM_CLASS =\n \"text-muted-foreground hover:text-foreground transition-colors\";\n\n/**\n * Defense-in-depth XSS guard. Only http(s), mailto, and tel URLs are allowed\n * through to a rendered `href`; anything else (notably `javascript:`,\n * `data:`, `vbscript:`) is rejected and the link is silently skipped. Inputs\n * are admin-controlled today, but treating the URL field as untrusted is\n * cheap and matches the wider Burdenoff posture.\n */\nfunction isSafeUrl(value: string): boolean {\n const trimmed = value.trim();\n // Relative URLs (no scheme) are safe — browser treats them as same-origin.\n if (/^[/?#]/.test(trimmed)) return true;\n return /^(https?:|mailto:|tel:)/i.test(trimmed);\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function SocialLinks({\n links: linksProp,\n email: emailProp,\n platforms: platformAllow,\n showEmail,\n iconClassName = \"h-5 w-5\",\n itemClassName = DEFAULT_ITEM_CLASS,\n className = \"flex space-x-4\",\n}: SocialLinksProps) {\n const { product } = useProduct();\n\n // Source of truth: prop > product CMS > empty.\n const links: Record<string, string> = (linksProp ??\n product?.socialLinks ??\n {}) as Record<string, string>;\n // `emailProp === undefined` → fall back to CMS. Explicit `null` from the\n // caller means \"suppress the mail icon entirely\" — don't fall back. This\n // matches the documented prop contract.\n const email: string | null =\n emailProp !== undefined ? emailProp : (product?.contactEmail ?? null);\n\n // Normalise CMS keys → canonical platform names, dedupe, preserve URL.\n // Last-writer-wins on duplicate platforms (alias + canonical both set).\n // Rejects `javascript:`/`data:`/`vbscript:` URLs as a defense-in-depth XSS\n // guard — a compromised admin CMS could otherwise inject an active script\n // into the `href`. Only http(s), mailto, and tel schemes are allowed.\n const resolved = new Map<SocialPlatform, string>();\n for (const [rawKey, url] of Object.entries(links)) {\n if (!url || typeof url !== \"string\") continue;\n if (!isSafeUrl(url)) continue;\n const platform = KEY_TO_PLATFORM[rawKey.toLowerCase()];\n if (!platform) continue; // unknown platform key — silently skip\n resolved.set(platform, url);\n }\n\n // Optional allowlist + ordering. When provided, only render listed\n // platforms, and in that order. Otherwise render in insertion order\n // (which mirrors the JSON object key order from the CMS).\n const orderedPlatforms: SocialPlatform[] = platformAllow\n ? platformAllow.filter((p) => resolved.has(p))\n : Array.from(resolved.keys());\n\n const wantEmail = (showEmail ?? !!email) && !!email;\n\n // Render nothing at all when no links and no email — caller can render an\n // empty parent without an extra falsy-guard.\n if (orderedPlatforms.length === 0 && !wantEmail) return null;\n\n return (\n <div className={className}>\n {orderedPlatforms.map((platform) => {\n const { Icon, label } = PLATFORMS[platform];\n const url = resolved.get(platform)!;\n return (\n <a\n key={platform}\n href={url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n aria-label={label}\n className={itemClassName}\n >\n <Icon className={iconClassName} />\n </a>\n );\n })}\n {wantEmail && (\n <a\n key=\"email\"\n href={`mailto:${email}`}\n aria-label=\"Email\"\n className={itemClassName}\n >\n <Mail className={iconClassName} />\n </a>\n )}\n </div>\n );\n}\n"]}
@@ -0,0 +1,114 @@
1
+ import { useProduct } from './chunk-GCFLCO2Z.mjs';
2
+ import { Instagram, Youtube, Facebook, Github, Linkedin, Twitter, Mail } from 'lucide-react';
3
+ import { jsx, jsxs } from 'react/jsx-runtime';
4
+
5
+ function DiscordIcon({ className }) {
6
+ return /* @__PURE__ */ jsx(
7
+ "svg",
8
+ {
9
+ role: "img",
10
+ viewBox: "0 0 24 24",
11
+ fill: "currentColor",
12
+ xmlns: "http://www.w3.org/2000/svg",
13
+ className,
14
+ "aria-hidden": "true",
15
+ children: /* @__PURE__ */ jsx("path", { d: "M20.317 4.369A19.79 19.79 0 0 0 16.558 3a.075.075 0 0 0-.079.037c-.34.6-.717 1.382-.98 1.99a18.27 18.27 0 0 0-5.998 0 12.51 12.51 0 0 0-.99-1.99.077.077 0 0 0-.079-.037 19.74 19.74 0 0 0-3.76 1.369.07.07 0 0 0-.032.028C.533 8.045-.32 11.6.099 15.114a.082.082 0 0 0 .031.056 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.027 14.09 14.09 0 0 0 1.226-1.994.076.076 0 0 0-.041-.105 13.1 13.1 0 0 1-1.872-.892.077.077 0 0 1-.008-.128c.126-.094.252-.192.372-.291a.074.074 0 0 1 .078-.01c3.927 1.793 8.18 1.793 12.061 0a.074.074 0 0 1 .079.009c.12.099.246.198.372.292a.077.077 0 0 1-.006.127 12.3 12.3 0 0 1-1.873.892.077.077 0 0 0-.041.106c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.84 19.84 0 0 0 6.002-3.03.077.077 0 0 0 .032-.055c.5-4.063-.838-7.587-3.549-10.717a.061.061 0 0 0-.031-.028zM8.02 13.001c-1.182 0-2.157-1.085-2.157-2.419 0-1.333.956-2.418 2.157-2.418 1.21 0 2.176 1.094 2.157 2.418 0 1.334-.956 2.419-2.157 2.419zm7.974 0c-1.182 0-2.157-1.085-2.157-2.419 0-1.333.956-2.418 2.157-2.418 1.21 0 2.176 1.094 2.157 2.418 0 1.334-.946 2.419-2.157 2.419z" })
16
+ }
17
+ );
18
+ }
19
+ function PinterestIcon({ className }) {
20
+ return /* @__PURE__ */ jsx(
21
+ "svg",
22
+ {
23
+ role: "img",
24
+ viewBox: "0 0 24 24",
25
+ fill: "currentColor",
26
+ xmlns: "http://www.w3.org/2000/svg",
27
+ className,
28
+ "aria-hidden": "true",
29
+ children: /* @__PURE__ */ jsx("path", { d: "M12.017 0C5.396 0 .029 5.367.029 11.987c0 5.079 3.158 9.417 7.618 11.162-.105-.949-.199-2.403.041-3.439.219-.937 1.406-5.957 1.406-5.957s-.359-.72-.359-1.781c0-1.663.967-2.911 2.168-2.911 1.024 0 1.518.769 1.518 1.688 0 1.029-.653 2.567-.992 3.992-.285 1.193.6 2.165 1.775 2.165 2.128 0 3.768-2.245 3.768-5.487 0-2.861-2.063-4.869-5.008-4.869-3.41 0-5.409 2.562-5.409 5.199 0 1.033.394 2.143.889 2.741.099.12.112.225.085.345-.09.375-.293 1.199-.334 1.363-.053.225-.172.271-.401.165-1.495-.69-2.433-2.878-2.433-4.646 0-3.776 2.748-7.252 7.92-7.252 4.158 0 7.392 2.967 7.392 6.923 0 4.135-2.607 7.462-6.233 7.462-1.214 0-2.357-.629-2.75-1.378l-.748 2.853c-.271 1.043-1.002 2.35-1.492 3.146C9.57 23.812 10.763 24 12.017 24c6.624 0 11.99-5.367 11.99-11.987C24.007 5.367 18.641 0 12.017 0z" })
30
+ }
31
+ );
32
+ }
33
+ var PLATFORMS = {
34
+ twitter: { Icon: Twitter, label: "Twitter / X", aliases: ["x"] },
35
+ linkedin: { Icon: Linkedin, label: "LinkedIn" },
36
+ github: { Icon: Github, label: "GitHub" },
37
+ facebook: { Icon: Facebook, label: "Facebook" },
38
+ youtube: { Icon: Youtube, label: "YouTube" },
39
+ instagram: { Icon: Instagram, label: "Instagram" },
40
+ discord: { Icon: DiscordIcon, label: "Discord" },
41
+ pinterest: { Icon: PinterestIcon, label: "Pinterest" }
42
+ };
43
+ var KEY_TO_PLATFORM = (() => {
44
+ const map = {};
45
+ for (const [key, def] of Object.entries(PLATFORMS)) {
46
+ map[key] = key;
47
+ for (const alias of def.aliases ?? []) {
48
+ map[alias] = key;
49
+ }
50
+ }
51
+ return map;
52
+ })();
53
+ var DEFAULT_ITEM_CLASS = "text-muted-foreground hover:text-foreground transition-colors";
54
+ function isSafeUrl(value) {
55
+ const trimmed = value.trim();
56
+ if (/^[/?#]/.test(trimmed)) return true;
57
+ return /^(https?:|mailto:|tel:)/i.test(trimmed);
58
+ }
59
+ function SocialLinks({
60
+ links: linksProp,
61
+ email: emailProp,
62
+ platforms: platformAllow,
63
+ showEmail,
64
+ iconClassName = "h-5 w-5",
65
+ itemClassName = DEFAULT_ITEM_CLASS,
66
+ className = "flex space-x-4"
67
+ }) {
68
+ const { product } = useProduct();
69
+ const links = linksProp ?? product?.socialLinks ?? {};
70
+ const email = emailProp !== void 0 ? emailProp : product?.contactEmail ?? null;
71
+ const resolved = /* @__PURE__ */ new Map();
72
+ for (const [rawKey, url] of Object.entries(links)) {
73
+ if (!url || typeof url !== "string") continue;
74
+ if (!isSafeUrl(url)) continue;
75
+ const platform = KEY_TO_PLATFORM[rawKey.toLowerCase()];
76
+ if (!platform) continue;
77
+ resolved.set(platform, url);
78
+ }
79
+ const orderedPlatforms = platformAllow ? platformAllow.filter((p) => resolved.has(p)) : Array.from(resolved.keys());
80
+ const wantEmail = (showEmail ?? !!email) && !!email;
81
+ if (orderedPlatforms.length === 0 && !wantEmail) return null;
82
+ return /* @__PURE__ */ jsxs("div", { className, children: [
83
+ orderedPlatforms.map((platform) => {
84
+ const { Icon, label } = PLATFORMS[platform];
85
+ const url = resolved.get(platform);
86
+ return /* @__PURE__ */ jsx(
87
+ "a",
88
+ {
89
+ href: url,
90
+ target: "_blank",
91
+ rel: "noopener noreferrer",
92
+ "aria-label": label,
93
+ className: itemClassName,
94
+ children: /* @__PURE__ */ jsx(Icon, { className: iconClassName })
95
+ },
96
+ platform
97
+ );
98
+ }),
99
+ wantEmail && /* @__PURE__ */ jsx(
100
+ "a",
101
+ {
102
+ href: `mailto:${email}`,
103
+ "aria-label": "Email",
104
+ className: itemClassName,
105
+ children: /* @__PURE__ */ jsx(Mail, { className: iconClassName })
106
+ },
107
+ "email"
108
+ )
109
+ ] });
110
+ }
111
+
112
+ export { SocialLinks };
113
+ //# sourceMappingURL=chunk-ZKJQWFAA.mjs.map
114
+ //# sourceMappingURL=chunk-ZKJQWFAA.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/social-links.tsx"],"names":[],"mappings":"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","file":"chunk-ZKJQWFAA.mjs","sourcesContent":["\"use client\";\n\n/**\n * SocialLinks Component\n *\n * Renders a row of social-media icons sourced from the **product CMS**.\n * The `socialLinks` field on `Product` (and `PublicProduct`) is a free-form\n * JSON record: `{ twitter, github, linkedin, facebook, youtube, instagram,\n * discord, pinterest, … }`. The admin sets per-product URLs in\n * `microfe-product → Products → <product>`; this component reads them via\n * `useProduct()` and renders icons for the platforms we know how to map.\n *\n * Unknown keys are **silently ignored** — the schema accepts any string key\n * so new platforms can be wired in admin without an SDK change, but they\n * won't render until this map grows.\n *\n * Why a component instead of inlining in each product website's Footer\n *\n * - Single source of truth for the platform → icon mapping.\n * - One swap to add a new platform (e.g. Threads, Bluesky) across all 5\n * product websites.\n * - Eliminates the drift today between hard-coded URLs and CMS values\n * (e.g. vibecontrols-website Footer had `twitter.com/vibecontrols`\n * hard-coded while CMS had `twitter.com/burdenoff`).\n *\n * @example\n * ```tsx\n * import { SocialLinks } from '@burdenoff/website-sdk'\n *\n * // Auto-derives from useProduct().product.socialLinks\n * <SocialLinks />\n *\n * // Or pass explicitly\n * <SocialLinks\n * links={{ twitter: 'https://twitter.com/example', github: 'https://github.com/example' }}\n * email=\"contact@example.com\"\n * />\n *\n * // Restrict + reorder\n * <SocialLinks platforms={['github', 'twitter', 'linkedin']} />\n * ```\n */\n\nimport { type ComponentType } from \"react\";\n\nimport {\n Facebook,\n Github,\n Instagram,\n Linkedin,\n Mail,\n Twitter,\n Youtube,\n} from \"lucide-react\";\n\nimport { useProduct } from \"../product/product-provider\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Platform keys the component knows how to render. Extends as we add icons.\n * Note: this is intentionally a small allowlist; the CMS JSON schema is\n * open-ended and the component silently skips unknown keys.\n */\nexport type SocialPlatform =\n | \"twitter\"\n | \"linkedin\"\n | \"github\"\n | \"facebook\"\n | \"youtube\"\n | \"instagram\"\n | \"discord\"\n | \"pinterest\";\n\nexport interface SocialLinksProps {\n /**\n * Map of platform → URL. If unset, reads from `useProduct().product.socialLinks`.\n * Useful for passing static config when there's no `<ProductProvider>` upstream.\n */\n links?: Record<string, string> | null;\n /**\n * Contact email — renders a Mail icon linking to `mailto:…`. If unset,\n * reads from `useProduct().product.contactEmail`. Pass `null` to suppress.\n */\n email?: string | null;\n /**\n * Allowlist + ORDER of platforms to render. Defaults to the natural CMS\n * order (whatever the JSON returns). Use this to pin a stable visual order\n * across products that may have different platform sets.\n */\n platforms?: readonly SocialPlatform[];\n /** Whether to render the Mail icon for `email`. Default: `true` when an email is available. */\n showEmail?: boolean;\n /** Tailwind class controlling icon size. Default: `\"h-5 w-5\"`. */\n iconClassName?: string;\n /** Tailwind class for each `<a>` wrapping link. Defaults to a muted-foreground / hover-foreground transition. */\n itemClassName?: string;\n /** Tailwind class for the outer container. Default: `\"flex space-x-4\"`. */\n className?: string;\n}\n\n// ============================================================================\n// Platform → icon map\n// ============================================================================\n\n/**\n * Custom inline SVG for platforms not covered by lucide-react.\n * Stays minimal — single-path icons keep bundle size tight.\n */\nfunction DiscordIcon({ className }: { className?: string }) {\n return (\n <svg\n role=\"img\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden=\"true\"\n >\n <path d=\"M20.317 4.369A19.79 19.79 0 0 0 16.558 3a.075.075 0 0 0-.079.037c-.34.6-.717 1.382-.98 1.99a18.27 18.27 0 0 0-5.998 0 12.51 12.51 0 0 0-.99-1.99.077.077 0 0 0-.079-.037 19.74 19.74 0 0 0-3.76 1.369.07.07 0 0 0-.032.028C.533 8.045-.32 11.6.099 15.114a.082.082 0 0 0 .031.056 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.027 14.09 14.09 0 0 0 1.226-1.994.076.076 0 0 0-.041-.105 13.1 13.1 0 0 1-1.872-.892.077.077 0 0 1-.008-.128c.126-.094.252-.192.372-.291a.074.074 0 0 1 .078-.01c3.927 1.793 8.18 1.793 12.061 0a.074.074 0 0 1 .079.009c.12.099.246.198.372.292a.077.077 0 0 1-.006.127 12.3 12.3 0 0 1-1.873.892.077.077 0 0 0-.041.106c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.84 19.84 0 0 0 6.002-3.03.077.077 0 0 0 .032-.055c.5-4.063-.838-7.587-3.549-10.717a.061.061 0 0 0-.031-.028zM8.02 13.001c-1.182 0-2.157-1.085-2.157-2.419 0-1.333.956-2.418 2.157-2.418 1.21 0 2.176 1.094 2.157 2.418 0 1.334-.956 2.419-2.157 2.419zm7.974 0c-1.182 0-2.157-1.085-2.157-2.419 0-1.333.956-2.418 2.157-2.418 1.21 0 2.176 1.094 2.157 2.418 0 1.334-.946 2.419-2.157 2.419z\" />\n </svg>\n );\n}\n\nfunction PinterestIcon({ className }: { className?: string }) {\n return (\n <svg\n role=\"img\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden=\"true\"\n >\n <path d=\"M12.017 0C5.396 0 .029 5.367.029 11.987c0 5.079 3.158 9.417 7.618 11.162-.105-.949-.199-2.403.041-3.439.219-.937 1.406-5.957 1.406-5.957s-.359-.72-.359-1.781c0-1.663.967-2.911 2.168-2.911 1.024 0 1.518.769 1.518 1.688 0 1.029-.653 2.567-.992 3.992-.285 1.193.6 2.165 1.775 2.165 2.128 0 3.768-2.245 3.768-5.487 0-2.861-2.063-4.869-5.008-4.869-3.41 0-5.409 2.562-5.409 5.199 0 1.033.394 2.143.889 2.741.099.12.112.225.085.345-.09.375-.293 1.199-.334 1.363-.053.225-.172.271-.401.165-1.495-.69-2.433-2.878-2.433-4.646 0-3.776 2.748-7.252 7.92-7.252 4.158 0 7.392 2.967 7.392 6.923 0 4.135-2.607 7.462-6.233 7.462-1.214 0-2.357-.629-2.75-1.378l-.748 2.853c-.271 1.043-1.002 2.35-1.492 3.146C9.57 23.812 10.763 24 12.017 24c6.624 0 11.99-5.367 11.99-11.987C24.007 5.367 18.641 0 12.017 0z\" />\n </svg>\n );\n}\n\ninterface PlatformDef {\n /** lucide-react component or custom SVG component */\n Icon: ComponentType<{ className?: string }>;\n /** Accessible label for the link */\n label: string;\n /** Optional alias keys that should also resolve to this platform */\n aliases?: readonly string[];\n}\n\nconst PLATFORMS: Record<SocialPlatform, PlatformDef> = {\n twitter: { Icon: Twitter, label: \"Twitter / X\", aliases: [\"x\"] },\n linkedin: { Icon: Linkedin, label: \"LinkedIn\" },\n github: { Icon: Github, label: \"GitHub\" },\n facebook: { Icon: Facebook, label: \"Facebook\" },\n youtube: { Icon: Youtube, label: \"YouTube\" },\n instagram: { Icon: Instagram, label: \"Instagram\" },\n discord: { Icon: DiscordIcon, label: \"Discord\" },\n pinterest: { Icon: PinterestIcon, label: \"Pinterest\" },\n};\n\n/**\n * Build a `key → SocialPlatform` lookup so aliases (e.g. `\"x\"` → `\"twitter\"`)\n * resolve in O(1). Memoised at module scope; never recomputed.\n */\nconst KEY_TO_PLATFORM: Record<string, SocialPlatform> = (() => {\n const map: Record<string, SocialPlatform> = {};\n for (const [key, def] of Object.entries(PLATFORMS) as [\n SocialPlatform,\n PlatformDef,\n ][]) {\n map[key] = key;\n for (const alias of def.aliases ?? []) {\n map[alias] = key;\n }\n }\n return map;\n})();\n\nconst DEFAULT_ITEM_CLASS =\n \"text-muted-foreground hover:text-foreground transition-colors\";\n\n/**\n * Defense-in-depth XSS guard. Only http(s), mailto, and tel URLs are allowed\n * through to a rendered `href`; anything else (notably `javascript:`,\n * `data:`, `vbscript:`) is rejected and the link is silently skipped. Inputs\n * are admin-controlled today, but treating the URL field as untrusted is\n * cheap and matches the wider Burdenoff posture.\n */\nfunction isSafeUrl(value: string): boolean {\n const trimmed = value.trim();\n // Relative URLs (no scheme) are safe — browser treats them as same-origin.\n if (/^[/?#]/.test(trimmed)) return true;\n return /^(https?:|mailto:|tel:)/i.test(trimmed);\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function SocialLinks({\n links: linksProp,\n email: emailProp,\n platforms: platformAllow,\n showEmail,\n iconClassName = \"h-5 w-5\",\n itemClassName = DEFAULT_ITEM_CLASS,\n className = \"flex space-x-4\",\n}: SocialLinksProps) {\n const { product } = useProduct();\n\n // Source of truth: prop > product CMS > empty.\n const links: Record<string, string> = (linksProp ??\n product?.socialLinks ??\n {}) as Record<string, string>;\n // `emailProp === undefined` → fall back to CMS. Explicit `null` from the\n // caller means \"suppress the mail icon entirely\" — don't fall back. This\n // matches the documented prop contract.\n const email: string | null =\n emailProp !== undefined ? emailProp : (product?.contactEmail ?? null);\n\n // Normalise CMS keys → canonical platform names, dedupe, preserve URL.\n // Last-writer-wins on duplicate platforms (alias + canonical both set).\n // Rejects `javascript:`/`data:`/`vbscript:` URLs as a defense-in-depth XSS\n // guard — a compromised admin CMS could otherwise inject an active script\n // into the `href`. Only http(s), mailto, and tel schemes are allowed.\n const resolved = new Map<SocialPlatform, string>();\n for (const [rawKey, url] of Object.entries(links)) {\n if (!url || typeof url !== \"string\") continue;\n if (!isSafeUrl(url)) continue;\n const platform = KEY_TO_PLATFORM[rawKey.toLowerCase()];\n if (!platform) continue; // unknown platform key — silently skip\n resolved.set(platform, url);\n }\n\n // Optional allowlist + ordering. When provided, only render listed\n // platforms, and in that order. Otherwise render in insertion order\n // (which mirrors the JSON object key order from the CMS).\n const orderedPlatforms: SocialPlatform[] = platformAllow\n ? platformAllow.filter((p) => resolved.has(p))\n : Array.from(resolved.keys());\n\n const wantEmail = (showEmail ?? !!email) && !!email;\n\n // Render nothing at all when no links and no email — caller can render an\n // empty parent without an extra falsy-guard.\n if (orderedPlatforms.length === 0 && !wantEmail) return null;\n\n return (\n <div className={className}>\n {orderedPlatforms.map((platform) => {\n const { Icon, label } = PLATFORMS[platform];\n const url = resolved.get(platform)!;\n return (\n <a\n key={platform}\n href={url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n aria-label={label}\n className={itemClassName}\n >\n <Icon className={iconClassName} />\n </a>\n );\n })}\n {wantEmail && (\n <a\n key=\"email\"\n href={`mailto:${email}`}\n aria-label=\"Email\"\n className={itemClassName}\n >\n <Mail className={iconClassName} />\n </a>\n )}\n </div>\n );\n}\n"]}
@@ -0,0 +1,138 @@
1
+ import { Input } from './chunk-34RX5P75.mjs';
2
+ import { cn } from './chunk-R7G6TWIS.mjs';
3
+ import { useState, useMemo } from 'react';
4
+ import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
5
+ import { Globe, ChevronDown, Search } from 'lucide-react';
6
+ import { jsxs, jsx } from 'react/jsx-runtime';
7
+
8
+ function normalizeSearch(value) {
9
+ return value.toLowerCase().trim();
10
+ }
11
+ function resolveWebsiteProductLogo(product) {
12
+ if (product.logoUrl) return product.logoUrl;
13
+ return `${product.websiteUrl.replace(/\/+$/, "")}/logo-icon.png`;
14
+ }
15
+ function filterProducts(products, query) {
16
+ const normalized = normalizeSearch(query);
17
+ if (!normalized) return products;
18
+ return products.filter((p) => normalizeSearch(p.name).includes(normalized));
19
+ }
20
+ function WebsiteSwitcher({
21
+ products,
22
+ triggerLabel = "Explore products",
23
+ searchPlaceholder = "Find a product\u2026",
24
+ emptyText = "No products match your search.",
25
+ className
26
+ }) {
27
+ const [open, setOpen] = useState(false);
28
+ const [query, setQuery] = useState("");
29
+ const [failedLogos, setFailedLogos] = useState(
30
+ () => /* @__PURE__ */ new Set()
31
+ );
32
+ const filtered = useMemo(
33
+ () => filterProducts(products, query),
34
+ [products, query]
35
+ );
36
+ return /* @__PURE__ */ jsxs(DropdownMenu.Root, { open, onOpenChange: setOpen, children: [
37
+ /* @__PURE__ */ jsxs(
38
+ DropdownMenu.Trigger,
39
+ {
40
+ className: cn(
41
+ "inline-flex items-center gap-1.5 text-sm font-medium transition-colors",
42
+ "text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring rounded-md",
43
+ className
44
+ ),
45
+ "aria-label": triggerLabel,
46
+ children: [
47
+ /* @__PURE__ */ jsx(Globe, { className: "h-4 w-4", "aria-hidden": "true" }),
48
+ /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: triggerLabel }),
49
+ /* @__PURE__ */ jsx(
50
+ ChevronDown,
51
+ {
52
+ className: cn(
53
+ "h-3.5 w-3.5 transition-transform duration-200",
54
+ open && "rotate-180"
55
+ ),
56
+ "aria-hidden": "true"
57
+ }
58
+ )
59
+ ]
60
+ }
61
+ ),
62
+ /* @__PURE__ */ jsx(DropdownMenu.Portal, { children: /* @__PURE__ */ jsxs(
63
+ DropdownMenu.Content,
64
+ {
65
+ className: "z-50 min-w-[16rem] max-w-[20rem] rounded-lg border border-border/60 bg-popover p-2 shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2",
66
+ sideOffset: 8,
67
+ align: "end",
68
+ avoidCollisions: true,
69
+ children: [
70
+ /* @__PURE__ */ jsxs("div", { className: "px-2 pb-2 pt-1", children: [
71
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground mb-2", children: "Burdenoff ecosystem" }),
72
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
73
+ /* @__PURE__ */ jsx(
74
+ Search,
75
+ {
76
+ className: "absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground",
77
+ "aria-hidden": "true"
78
+ }
79
+ ),
80
+ /* @__PURE__ */ jsx(
81
+ Input,
82
+ {
83
+ type: "search",
84
+ value: query,
85
+ onChange: (e) => setQuery(e.target.value),
86
+ placeholder: searchPlaceholder,
87
+ className: "h-8 pl-8 text-sm",
88
+ "aria-label": searchPlaceholder,
89
+ onKeyDown: (e) => {
90
+ if (e.key === "Escape") {
91
+ setQuery("");
92
+ }
93
+ }
94
+ }
95
+ )
96
+ ] })
97
+ ] }),
98
+ /* @__PURE__ */ jsx(DropdownMenu.Separator, { className: "my-1 h-px bg-border/60" }),
99
+ /* @__PURE__ */ jsx("div", { className: "max-h-[min(60vh,24rem)] overflow-y-auto py-1", children: filtered.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-3 py-4 text-center text-sm text-muted-foreground", children: emptyText }) : filtered.map((product) => /* @__PURE__ */ jsx(DropdownMenu.Item, { asChild: true, children: /* @__PURE__ */ jsxs(
100
+ "a",
101
+ {
102
+ href: product.websiteUrl,
103
+ target: "_blank",
104
+ rel: "noopener noreferrer",
105
+ className: cn(
106
+ "flex items-center gap-3 rounded-md px-2 py-2 text-sm outline-none",
107
+ "text-foreground hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground"
108
+ ),
109
+ children: [
110
+ failedLogos.has(product.slug) ? /* @__PURE__ */ jsx("span", { className: "flex h-5 w-5 shrink-0 items-center justify-center rounded bg-primary/10 text-[10px] font-bold text-primary", children: product.name.charAt(0) }) : /* @__PURE__ */ jsx(
111
+ "img",
112
+ {
113
+ src: resolveWebsiteProductLogo(product),
114
+ alt: "",
115
+ "aria-hidden": "true",
116
+ loading: "lazy",
117
+ className: "h-5 w-5 shrink-0 rounded object-contain",
118
+ onError: () => setFailedLogos((prev) => {
119
+ const next = new Set(prev);
120
+ next.add(product.slug);
121
+ return next;
122
+ })
123
+ }
124
+ ),
125
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: product.name })
126
+ ]
127
+ }
128
+ ) }, product.slug)) }),
129
+ /* @__PURE__ */ jsx(DropdownMenu.Arrow, { className: "fill-popover" })
130
+ ]
131
+ }
132
+ ) })
133
+ ] });
134
+ }
135
+
136
+ export { WebsiteSwitcher, resolveWebsiteProductLogo };
137
+ //# sourceMappingURL=chunk-ZOON3S3Y.mjs.map
138
+ //# sourceMappingURL=chunk-ZOON3S3Y.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/website-switcher.tsx"],"names":[],"mappings":"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","file":"chunk-ZOON3S3Y.mjs","sourcesContent":["\"use client\";\n\n/**\n * WebsiteSwitcher Component\n *\n * A dropdown menu for navigating between Burdenoff ecosystem product websites.\n * Includes a search filter and renders each product with its logo and name.\n *\n * @example\n * ```tsx\n * import { WebsiteSwitcher } from '@burdenoff/website-sdk'\n *\n * <WebsiteSwitcher\n * products={[\n * { slug: 'vibecontrols', name: 'VibeControls', logoUrl: '/vibe.svg', websiteUrl: 'https://vibecontrols.com' },\n * { slug: 'fluidgrids', name: 'FluidGrids', logoUrl: '/fg.svg', websiteUrl: 'https://fluidgrids.com' },\n * ]}\n * />\n * ```\n */\n\nimport { useMemo, useState } from \"react\";\n\nimport * as DropdownMenu from \"@radix-ui/react-dropdown-menu\";\nimport { ChevronDown, Globe, Search } from \"lucide-react\";\n\nimport { Input } from \"../ui/input\";\nimport { cn } from \"../utils/cn\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface WebsiteSwitcherProduct {\n /** Stable identifier for the product. */\n slug: string;\n /** Human-readable product name. */\n name: string;\n /** URL to the product logo (absolute or relative). */\n logoUrl?: string | null;\n /** Product marketing site URL. */\n websiteUrl: string;\n}\n\nexport interface WebsiteSwitcherProps {\n /** All Burdenoff ecosystem products to display. */\n products: WebsiteSwitcherProduct[];\n /** Accessible label for the trigger. */\n triggerLabel?: string;\n /** Placeholder text for the search input. */\n searchPlaceholder?: string;\n /** Empty-state text when filtering returns no results. */\n emptyText?: string;\n /** Optional additional CSS class for the trigger. */\n className?: string;\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction normalizeSearch(value: string): string {\n return value.toLowerCase().trim();\n}\n\n/**\n * Resolve the logo URL for an ecosystem product.\n *\n * Prefers an explicit `logoUrl`; otherwise derives the canonical marketing-site\n * brand mark (`/logo-icon.png`), which every product website publishes. Callers\n * should fall back to the initial chip if the image fails to load.\n */\nexport function resolveWebsiteProductLogo(\n product: WebsiteSwitcherProduct,\n): string {\n if (product.logoUrl) return product.logoUrl;\n return `${product.websiteUrl.replace(/\\/+$/, \"\")}/logo-icon.png`;\n}\n\nfunction filterProducts(\n products: WebsiteSwitcherProduct[],\n query: string,\n): WebsiteSwitcherProduct[] {\n const normalized = normalizeSearch(query);\n if (!normalized) return products;\n return products.filter((p) => normalizeSearch(p.name).includes(normalized));\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function WebsiteSwitcher({\n products,\n triggerLabel = \"Explore products\",\n searchPlaceholder = \"Find a product…\",\n emptyText = \"No products match your search.\",\n className,\n}: WebsiteSwitcherProps) {\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n // Slugs whose logo image failed to load — fall back to the initial chip.\n const [failedLogos, setFailedLogos] = useState<ReadonlySet<string>>(\n () => new Set<string>(),\n );\n\n const filtered = useMemo(\n () => filterProducts(products, query),\n [products, query],\n );\n\n return (\n <DropdownMenu.Root open={open} onOpenChange={setOpen}>\n <DropdownMenu.Trigger\n className={cn(\n \"inline-flex items-center gap-1.5 text-sm font-medium transition-colors\",\n \"text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring rounded-md\",\n className,\n )}\n aria-label={triggerLabel}\n >\n <Globe className=\"h-4 w-4\" aria-hidden=\"true\" />\n <span className=\"hidden sm:inline\">{triggerLabel}</span>\n <ChevronDown\n className={cn(\n \"h-3.5 w-3.5 transition-transform duration-200\",\n open && \"rotate-180\",\n )}\n aria-hidden=\"true\"\n />\n </DropdownMenu.Trigger>\n\n <DropdownMenu.Portal>\n <DropdownMenu.Content\n className=\"z-50 min-w-[16rem] max-w-[20rem] rounded-lg border border-border/60 bg-popover p-2 shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2\"\n sideOffset={8}\n align=\"end\"\n avoidCollisions\n >\n <div className=\"px-2 pb-2 pt-1\">\n <p className=\"text-xs font-medium text-muted-foreground mb-2\">\n Burdenoff ecosystem\n </p>\n <div className=\"relative\">\n <Search\n className=\"absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground\"\n aria-hidden=\"true\"\n />\n <Input\n type=\"search\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder={searchPlaceholder}\n className=\"h-8 pl-8 text-sm\"\n aria-label={searchPlaceholder}\n onKeyDown={(e) => {\n if (e.key === \"Escape\") {\n setQuery(\"\");\n }\n }}\n />\n </div>\n </div>\n\n <DropdownMenu.Separator className=\"my-1 h-px bg-border/60\" />\n\n <div className=\"max-h-[min(60vh,24rem)] overflow-y-auto py-1\">\n {filtered.length === 0 ? (\n <div className=\"px-3 py-4 text-center text-sm text-muted-foreground\">\n {emptyText}\n </div>\n ) : (\n filtered.map((product) => (\n <DropdownMenu.Item key={product.slug} asChild>\n <a\n href={product.websiteUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={cn(\n \"flex items-center gap-3 rounded-md px-2 py-2 text-sm outline-none\",\n \"text-foreground hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground\",\n )}\n >\n {failedLogos.has(product.slug) ? (\n <span className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded bg-primary/10 text-[10px] font-bold text-primary\">\n {product.name.charAt(0)}\n </span>\n ) : (\n <img\n src={resolveWebsiteProductLogo(product)}\n alt=\"\"\n aria-hidden=\"true\"\n loading=\"lazy\"\n className=\"h-5 w-5 shrink-0 rounded object-contain\"\n onError={() =>\n setFailedLogos((prev) => {\n const next = new Set(prev);\n next.add(product.slug);\n return next;\n })\n }\n />\n )}\n <span className=\"truncate\">{product.name}</span>\n </a>\n </DropdownMenu.Item>\n ))\n )}\n </div>\n\n <DropdownMenu.Arrow className=\"fill-popover\" />\n </DropdownMenu.Content>\n </DropdownMenu.Portal>\n </DropdownMenu.Root>\n );\n}\n"]}