@burdenoff/website-sdk 2026.1101.1 → 2026.1101.2

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 +42 -8
  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 +10 -30
  11. package/dist/avatar-live-view/index.js.map +1 -1
  12. package/dist/avatar-live-view/index.mjs +4 -24
  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
@@ -1,2655 +1,56 @@
1
1
  'use strict';
2
2
 
3
- var React = require('react');
4
- var jsxRuntime = require('react/jsx-runtime');
5
- var reactHelmetAsync = require('react-helmet-async');
6
- require('isomorphic-dompurify');
7
- var ReactMarkdown = require('react-markdown');
8
- var rehypeSanitize = require('rehype-sanitize');
9
- var remarkGfm = require('remark-gfm');
10
- require('lucide-react');
11
- var reactSlot = require('@radix-ui/react-slot');
12
- var classVarianceAuthority = require('class-variance-authority');
13
- var clsx = require('clsx');
14
- var tailwindMerge = require('tailwind-merge');
15
-
16
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
17
-
18
- function _interopNamespace(e) {
19
- if (e && e.__esModule) return e;
20
- var n = Object.create(null);
21
- if (e) {
22
- Object.keys(e).forEach(function (k) {
23
- if (k !== 'default') {
24
- var d = Object.getOwnPropertyDescriptor(e, k);
25
- Object.defineProperty(n, k, d.get ? d : {
26
- enumerable: true,
27
- get: function () { return e[k]; }
28
- });
29
- }
30
- });
31
- }
32
- n.default = e;
33
- return Object.freeze(n);
34
- }
35
-
36
- var React__namespace = /*#__PURE__*/_interopNamespace(React);
37
- var ReactMarkdown__default = /*#__PURE__*/_interopDefault(ReactMarkdown);
38
- var rehypeSanitize__default = /*#__PURE__*/_interopDefault(rehypeSanitize);
39
- var remarkGfm__default = /*#__PURE__*/_interopDefault(remarkGfm);
40
-
41
- var __defProp = Object.defineProperty;
42
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
43
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
44
- function formatPrice(price, currency, pricingModel) {
45
- if (pricingModel === "FREE" || !price || price === 0) return "Free";
46
- const curr = currency || "USD";
47
- try {
48
- return new Intl.NumberFormat("en-US", {
49
- style: "currency",
50
- currency: curr
51
- }).format(price);
52
- } catch {
53
- return `${curr} ${price.toFixed(2)}`;
54
- }
55
- }
56
- function formatDownloads(n) {
57
- if (!n) return "0";
58
- if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
59
- if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
60
- return n.toString();
61
- }
62
- function StarRating({ rating }) {
63
- return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex items-center gap-0.5", children: [1, 2, 3, 4, 5].map((star) => /* @__PURE__ */ jsxRuntime.jsx(
64
- "svg",
65
- {
66
- viewBox: "0 0 12 12",
67
- className: `w-3 h-3 ${star <= Math.round(rating) ? "text-amber-400 fill-current" : "text-muted-foreground/30 fill-current"}`,
68
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 1l1.545 3.09L11 4.59l-2.5 2.41.59 3.41L6 8.79l-3.09 1.61L3.5 7 1 4.59l3.455-.5L6 1z" })
69
- },
70
- star
71
- )) });
72
- }
73
- function TypeBadge({ type }) {
74
- const labels = {
75
- APP: "App",
76
- NODE: "Node",
77
- WORKFLOW: "Workflow",
78
- TEMPLATE: "Template",
79
- INTEGRATION: "Integration",
80
- EXTENSION: "Extension",
81
- THEME: "Theme"
82
- };
83
- return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block text-xs px-2 py-0.5 rounded-full bg-primary/10 text-foreground font-medium", children: labels[type] ?? type });
84
- }
85
- function ProductCard({ product, onClick, href }) {
86
- const priceLabel = formatPrice(
87
- product.price,
88
- product.currency,
89
- product.pricingModel
90
- );
91
- const inner = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "group bg-card border border-border rounded-xl p-4 flex flex-col gap-3 transition-all duration-200 hover:shadow-md hover:border-primary/30 cursor-pointer h-full", children: [
92
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-2", children: [
93
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-12 h-12 rounded-lg bg-muted flex-shrink-0 overflow-hidden", children: product.icon ? /* @__PURE__ */ jsxRuntime.jsx(
94
- "img",
95
- {
96
- src: product.icon,
97
- alt: "",
98
- "aria-hidden": "true",
99
- className: "w-full h-full object-cover"
100
- }
101
- ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center bg-gradient-to-br from-primary/15 to-primary/5", children: /* @__PURE__ */ jsxRuntime.jsxs(
102
- "svg",
103
- {
104
- viewBox: "0 0 32 32",
105
- className: "w-8 h-8",
106
- fill: "none",
107
- xmlns: "http://www.w3.org/2000/svg",
108
- "aria-hidden": "true",
109
- children: [
110
- /* @__PURE__ */ jsxRuntime.jsx(
111
- "polygon",
112
- {
113
- points: "16,4 28,10 16,16 4,10",
114
- className: "fill-primary/30",
115
- stroke: "currentColor",
116
- strokeWidth: "0.6",
117
- strokeOpacity: "0.35"
118
- }
119
- ),
120
- /* @__PURE__ */ jsxRuntime.jsx(
121
- "polygon",
122
- {
123
- points: "4,10 16,16 16,28 4,22",
124
- className: "fill-primary/15",
125
- stroke: "currentColor",
126
- strokeWidth: "0.6",
127
- strokeOpacity: "0.35"
128
- }
129
- ),
130
- /* @__PURE__ */ jsxRuntime.jsx(
131
- "polygon",
132
- {
133
- points: "28,10 16,16 16,28 28,22",
134
- className: "fill-primary/20",
135
- stroke: "currentColor",
136
- strokeWidth: "0.6",
137
- strokeOpacity: "0.35"
138
- }
139
- )
140
- ]
141
- }
142
- ) }) }),
143
- /* @__PURE__ */ jsxRuntime.jsx(TypeBadge, { type: product.type })
144
- ] }),
145
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
146
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-semibold text-foreground text-sm leading-snug line-clamp-1 group-hover:text-primary transition-colors", children: product.name }),
147
- product.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-xs mt-1 line-clamp-2 leading-relaxed", children: product.description })
148
- ] }),
149
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
150
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1.5", children: product.rating != null && product.rating > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
151
- /* @__PURE__ */ jsxRuntime.jsx(StarRating, { rating: product.rating }),
152
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: product.rating.toFixed(1) })
153
- ] }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: "No reviews yet" }) }),
154
- /* @__PURE__ */ jsxRuntime.jsx(
155
- "span",
156
- {
157
- className: `text-xs font-semibold ${priceLabel === "Free" ? "text-emerald-700 dark:text-emerald-400" : "text-foreground"}`,
158
- children: priceLabel
159
- }
160
- )
161
- ] })
162
- ] });
163
- if (href) {
164
- return /* @__PURE__ */ jsxRuntime.jsx("a", { href, onClick, className: "block h-full no-underline", children: inner });
165
- }
166
- return /* @__PURE__ */ jsxRuntime.jsx(
167
- "button",
168
- {
169
- type: "button",
170
- onClick,
171
- className: "block w-full text-left h-full",
172
- children: inner
173
- }
174
- );
175
- }
176
-
177
- // src/store/queries.ts
178
- var STORE_PRODUCT_FIELDS = `
179
- fragment StoreProductFields on StoreProduct {
180
- id name slug type subType nature status pricingModel
181
- price currency description icon bannerUrl screenshots
182
- category tags featured downloads viewCount rating reviewCount
183
- publisherId authorName license compatibleProducts
184
- }
185
- `;
186
- var HOME_GROUPED_PRODUCTS_QUERY = `
187
- ${STORE_PRODUCT_FIELDS}
188
- query HomeGroupedProducts($compatibleWith: String) {
189
- homeGroupedProducts(compatibleWith: $compatibleWith) {
190
- featured { ...StoreProductFields }
191
- mostDownloaded { ...StoreProductFields }
192
- recentlyAdded { ...StoreProductFields }
193
- freeItems { ...StoreProductFields }
194
- }
195
- }
196
- `;
197
- var BROWSE_STORE_QUERY = `
198
- ${STORE_PRODUCT_FIELDS}
199
- query BrowseStore($input: BrowseStoreInput!) {
200
- browseStore(input: $input) {
201
- products { ...StoreProductFields }
202
- totalCount
203
- hasMore
204
- nextCursor
205
- facets {
206
- types { value count }
207
- categories { value count }
208
- pricingModels { value count }
209
- priceRange { min max }
210
- }
211
- }
212
- }
213
- `;
214
- var STORE_PRODUCT_DETAILS_QUERY = `
215
- ${STORE_PRODUCT_FIELDS}
216
- query StoreProductDetails($input: StoreProductDetailsInput!) {
217
- storeProductDetails(input: $input) {
218
- product { ...StoreProductFields longDescription videoUrls minPlatformVersion }
219
- publisher { id name email websiteUrl logoUrl bio isVerified tier }
220
- reviews { id rating title comment verifiedPurchase helpful createdAt }
221
- reviewsCount
222
- relatedProducts { ...StoreProductFields }
223
- ratingDistribution { fiveStars fourStars threeStars twoStars oneStar }
224
- }
225
- }
226
- `;
227
- var CATEGORIES_QUERY = `
228
- query Categories {
229
- categories {
230
- id name slug description icon parentId
231
- }
232
- }
233
- `;
234
-
235
- // src/client/security.ts
236
- var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
237
- var RESERVED_HEADERS = /* @__PURE__ */ new Set([
238
- "authorization",
239
- "cookie",
240
- "x-actor-id",
241
- "x-actor-type",
242
- "x-consent-granted-by",
243
- "x-consent-scope",
244
- "x-consent-granted-at",
245
- "x-consent-expires-at"
246
- ]);
247
- function validateGatewayUrl(value) {
248
- let url;
249
- try {
250
- url = new URL(value);
251
- } catch {
252
- throw new Error("Gateway URL must be a valid absolute URL");
253
- }
254
- if (url.protocol === "https:") {
255
- return url.toString();
256
- }
257
- if (url.protocol === "http:" && LOOPBACK_HOSTS.has(url.hostname.toLowerCase())) {
258
- return url.toString();
259
- }
260
- throw new Error("Gateway URL must use HTTPS unless it targets localhost");
261
- }
262
- function sanitizeHeaders(headers) {
263
- if (!headers) return void 0;
264
- return Object.fromEntries(
265
- Object.entries(headers).filter(
266
- ([key]) => !RESERVED_HEADERS.has(key.toLowerCase())
267
- )
268
- );
269
- }
270
- var CF_ACCESS_GATED_HOSTS = /* @__PURE__ */ new Set([
271
- "alphagraphql.burdenoff.com",
272
- "alphagraphqlworkspaces.burdenoff.com"
273
- ]);
274
- function cfAccessHeaders(gatewayUrl, cfAccess) {
275
- const clientId = cfAccess?.clientId?.trim();
276
- const clientSecret = cfAccess?.clientSecret?.trim();
277
- if (!clientId || !clientSecret) return {};
278
- let host;
279
- try {
280
- host = new URL(gatewayUrl).hostname.toLowerCase();
281
- } catch {
282
- return {};
283
- }
284
- if (!CF_ACCESS_GATED_HOSTS.has(host)) return {};
285
- return {
286
- "CF-Access-Client-Id": clientId,
287
- "CF-Access-Client-Secret": clientSecret
288
- };
289
- }
290
-
291
- // src/client/graphql-client.ts
292
- var _WebSDKClient = class _WebSDKClient {
293
- constructor(config) {
294
- __publicField(this, "config");
295
- __publicField(this, "queryCache", /* @__PURE__ */ new Map());
296
- __publicField(this, "inflight", /* @__PURE__ */ new Map());
297
- this.config = {
298
- ...config,
299
- gatewayUrl: validateGatewayUrl(config.gatewayUrl),
300
- headers: sanitizeHeaders(config.headers)
301
- };
302
- }
303
- getConfig() {
304
- return this.config;
305
- }
306
- /**
307
- * Execute a GraphQL query (with deduplication and caching).
308
- *
309
- * Pass `{ cache: false }` to bypass the cache entirely — required for poll
310
- * loops, where every tick must reach the network.
311
- */
312
- async query(query, variables, options) {
313
- if (options?.cache === false) {
314
- return this.request(query, variables, true, options.timeoutMs);
315
- }
316
- const cacheKey = JSON.stringify({ query, variables });
317
- const cached = this.queryCache.get(cacheKey);
318
- if (cached) {
319
- if (Date.now() - cached.ts < _WebSDKClient.CACHE_TTL) {
320
- return cached.data;
321
- }
322
- this.queryCache.delete(cacheKey);
323
- }
324
- const existing = this.inflight.get(cacheKey);
325
- if (existing) {
326
- return existing;
327
- }
328
- const promise = this.request(query, variables, true, options?.timeoutMs).then((result) => {
329
- if (result.data && !result.errors?.length) {
330
- if (this.queryCache.size >= _WebSDKClient.MAX_CACHE_ENTRIES) {
331
- const oldestEntry = this.queryCache.keys().next().value;
332
- if (oldestEntry) {
333
- this.queryCache.delete(oldestEntry);
334
- }
335
- }
336
- this.queryCache.set(cacheKey, { data: result, ts: Date.now() });
337
- }
338
- return result;
339
- }).finally(() => {
340
- this.inflight.delete(cacheKey);
341
- });
342
- this.inflight.set(cacheKey, promise);
343
- return promise;
344
- }
345
- /**
346
- * Execute a GraphQL mutation
347
- */
348
- async mutate(mutation, variables, options) {
349
- return this.request(mutation, variables, false, options?.timeoutMs);
350
- }
351
- /** HTTP statuses worth another attempt — transient server/edge conditions. */
352
- static isRetriableStatus(status) {
353
- return status === 429 || status === 408 || status >= 500 && status < 600;
354
- }
355
- static sleep(ms) {
356
- return new Promise((resolve) => setTimeout(resolve, ms));
357
- }
358
- /**
359
- * Perform the HTTP call with a hard timeout. Every outcome resolves to a
360
- * GraphQLResponse — callers never hang, so a UI's `loading` state always
361
- * settles into data or a renderable error.
362
- */
363
- async attempt(query, variables, timeoutMs) {
364
- const controller = new AbortController();
365
- const timer = setTimeout(() => controller.abort(), timeoutMs);
366
- try {
367
- const response = await fetch(this.config.gatewayUrl, {
368
- method: "POST",
369
- headers: {
370
- "Content-Type": "application/json",
371
- "x-product-id": this.config.productId,
372
- ...this.config.headers,
373
- ...cfAccessHeaders(this.config.gatewayUrl, this.config.cfAccess)
374
- },
375
- body: JSON.stringify({ query, variables }),
376
- signal: controller.signal
377
- });
378
- if (!response.ok) {
379
- console.error(
380
- "[WebSDK] GraphQL request failed",
381
- response.status,
382
- response.statusText
383
- );
384
- return {
385
- res: {
386
- errors: [
387
- {
388
- message: `HTTP ${response.status}: ${response.statusText}`,
389
- extensions: { code: `HTTP_${response.status}` }
390
- }
391
- ]
392
- },
393
- retriable: _WebSDKClient.isRetriableStatus(response.status)
394
- };
395
- }
396
- return {
397
- res: await response.json(),
398
- retriable: false
399
- };
400
- } catch (error) {
401
- const aborted = error instanceof Error && (error.name === "AbortError" || controller.signal.aborted);
402
- const message = aborted ? `Request timed out after ${timeoutMs}ms` : error instanceof Error ? error.message : "Network error";
403
- console.error("[WebSDK] Network error", message);
404
- return {
405
- res: {
406
- errors: [
407
- {
408
- message,
409
- extensions: { code: aborted ? "TIMEOUT" : "NETWORK_ERROR" }
410
- }
411
- ]
412
- },
413
- // Transport-level failures are worth one more try; a genuine outage
414
- // just fails twice quickly and still renders an error state.
415
- retriable: true
416
- };
417
- } finally {
418
- clearTimeout(timer);
419
- }
420
- }
421
- async request(query, variables, retry = true, timeoutOverrideMs) {
422
- const timeoutMs = timeoutOverrideMs ?? this.config.timeoutMs ?? _WebSDKClient.DEFAULT_TIMEOUT_MS;
423
- const maxRetries = retry ? this.config.maxRetries ?? _WebSDKClient.DEFAULT_MAX_RETRIES : 0;
424
- let last;
425
- for (let i = 0; i <= maxRetries; i++) {
426
- const { res, retriable } = await this.attempt(
427
- query,
428
- variables,
429
- timeoutMs
430
- );
431
- if (!retriable || i === maxRetries) return res;
432
- last = res;
433
- await _WebSDKClient.sleep(
434
- _WebSDKClient.RETRY_BASE_DELAY_MS * (i + 1) + Math.random() * 200
435
- );
436
- }
437
- return last;
438
- }
439
- };
440
- __publicField(_WebSDKClient, "CACHE_TTL", 5 * 60 * 1e3);
441
- // 5 minutes
442
- __publicField(_WebSDKClient, "MAX_CACHE_ENTRIES", 100);
443
- __publicField(_WebSDKClient, "DEFAULT_TIMEOUT_MS", 12e3);
444
- __publicField(_WebSDKClient, "DEFAULT_MAX_RETRIES", 1);
445
- __publicField(_WebSDKClient, "RETRY_BASE_DELAY_MS", 400);
446
- var WebSDKContext = React.createContext(null);
447
- function useWebSDK() {
448
- const client = React.useContext(WebSDKContext);
449
- if (!client) {
450
- throw new Error("useWebSDK must be used within a WebSDKProvider");
451
- }
452
- return client;
453
- }
454
- function canonicalFromLocation() {
455
- if (typeof window === "undefined" || !window.location) return void 0;
456
- const { origin, pathname } = window.location;
457
- if (!origin || origin === "null") return void 0;
458
- const path = pathname === "/" ? "/" : pathname.replace(/\/+$/, "");
459
- return `${origin}${path}`;
460
- }
461
- function resolveImageUrl(image, base) {
462
- if (!image || /^https?:\/\//i.test(image)) return image;
463
- if (!base) return image;
464
- try {
465
- return new URL(image, base).toString();
466
- } catch {
467
- return image;
468
- }
469
- }
470
- function serializeJsonLd(data) {
471
- return JSON.stringify(data).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
472
- }
473
- function PageHead({
474
- title,
475
- description,
476
- productName,
477
- keywords,
478
- image,
479
- url,
480
- favicon,
481
- robots = "index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1",
482
- locale = "en_US",
483
- twitterHandle,
484
- themeColor = "#000000",
485
- author,
486
- structuredData,
487
- additionalMeta
488
- }) {
489
- const fullTitle = productName && !title.includes(productName) ? `${title} | ${productName}` : title;
490
- const resolvedUrl = url ?? canonicalFromLocation();
491
- const fullImageUrl = resolveImageUrl(image, resolvedUrl);
492
- const structuredDataItems = structuredData ? Array.isArray(structuredData) ? structuredData : [structuredData] : [];
493
- return /* @__PURE__ */ jsxRuntime.jsxs(reactHelmetAsync.Helmet, { children: [
494
- /* @__PURE__ */ jsxRuntime.jsx("title", { children: fullTitle }),
495
- /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "title", content: fullTitle }),
496
- description ? /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "description", content: description }) : null,
497
- keywords ? /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "keywords", content: keywords }) : null,
498
- author ? /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "author", content: author }) : null,
499
- /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "robots", content: robots }),
500
- resolvedUrl ? /* @__PURE__ */ jsxRuntime.jsx("link", { rel: "canonical", href: resolvedUrl }) : null,
501
- favicon ? /* @__PURE__ */ jsxRuntime.jsx("link", { rel: "icon", href: favicon }) : null,
502
- /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:type", content: "website" }),
503
- /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:title", content: fullTitle }),
504
- description ? /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:description", content: description }) : null,
505
- fullImageUrl ? /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:image", content: fullImageUrl }) : null,
506
- resolvedUrl ? /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:url", content: resolvedUrl }) : null,
507
- /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:locale", content: locale }),
508
- productName ? /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:site_name", content: productName }) : null,
509
- /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:card", content: "summary_large_image" }),
510
- /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:title", content: fullTitle }),
511
- description ? /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:description", content: description }) : null,
512
- fullImageUrl ? /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:image", content: fullImageUrl }) : null,
513
- twitterHandle ? /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:creator", content: twitterHandle }) : null,
514
- /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "theme-color", content: themeColor }),
515
- structuredDataItems.map((data, index) => /* @__PURE__ */ jsxRuntime.jsx("script", { type: "application/ld+json", children: serializeJsonLd(data) }, `ld-${index}`)),
516
- additionalMeta?.map((meta, index) => /* @__PURE__ */ jsxRuntime.jsx(
517
- "meta",
518
- {
519
- ...meta.name ? { name: meta.name } : {},
520
- ...meta.property ? { property: meta.property } : {},
521
- content: meta.content
522
- },
523
- `meta-${index}`
524
- ))
525
- ] });
526
- }
527
- function dedupAcross(lists) {
528
- const seen = /* @__PURE__ */ new Set();
529
- return lists.map((list) => {
530
- const out = [];
531
- for (const p of list) {
532
- if (!seen.has(p.id)) {
533
- seen.add(p.id);
534
- out.push(p);
535
- }
536
- }
537
- return out;
538
- });
539
- }
540
- function FeaturedHeroCard({
541
- product,
542
- onNavigate
543
- }) {
544
- const priceLabel = formatPrice(
545
- product.price,
546
- product.currency,
547
- product.pricingModel
548
- );
549
- return /* @__PURE__ */ jsxRuntime.jsxs(
550
- "button",
551
- {
552
- type: "button",
553
- onClick: () => onNavigate(`/store/${product.slug}`),
554
- className: "group w-full text-left h-full rounded-2xl border border-border bg-card overflow-hidden hover:border-primary/40 transition-all duration-300 hover:shadow-xl hover:shadow-primary/10 flex flex-col",
555
- children: [
556
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative h-44 sm:h-52 bg-gradient-to-br from-primary/20 via-primary/8 to-background overflow-hidden flex-shrink-0", children: [
557
- product.bannerUrl ? /* @__PURE__ */ jsxRuntime.jsx(
558
- "img",
559
- {
560
- src: product.bannerUrl,
561
- alt: "",
562
- "aria-hidden": "true",
563
- className: "w-full h-full object-cover"
564
- }
565
- ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
566
- /* @__PURE__ */ jsxRuntime.jsxs(
567
- "svg",
568
- {
569
- className: "absolute inset-0 w-full h-full opacity-[0.07]",
570
- xmlns: "http://www.w3.org/2000/svg",
571
- "aria-hidden": "true",
572
- children: [
573
- /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsx(
574
- "pattern",
575
- {
576
- id: "fg-hero-grid",
577
- width: "36",
578
- height: "36",
579
- patternUnits: "userSpaceOnUse",
580
- children: /* @__PURE__ */ jsxRuntime.jsx(
581
- "path",
582
- {
583
- d: "M 36 0 L 0 0 0 36",
584
- fill: "none",
585
- stroke: "currentColor",
586
- strokeWidth: "1"
587
- }
588
- )
589
- }
590
- ) }),
591
- /* @__PURE__ */ jsxRuntime.jsx("rect", { width: "100%", height: "100%", fill: "url(#fg-hero-grid)" })
592
- ]
593
- }
594
- ),
595
- /* @__PURE__ */ jsxRuntime.jsx(
596
- "div",
597
- {
598
- className: "absolute inset-0 flex items-center justify-center",
599
- "aria-hidden": "true",
600
- children: /* @__PURE__ */ jsxRuntime.jsxs(
601
- "svg",
602
- {
603
- viewBox: "0 0 64 64",
604
- className: "w-20 h-20 opacity-25",
605
- fill: "none",
606
- xmlns: "http://www.w3.org/2000/svg",
607
- children: [
608
- /* @__PURE__ */ jsxRuntime.jsx(
609
- "polygon",
610
- {
611
- points: "32,8 56,20 32,32 8,20",
612
- className: "fill-primary/40",
613
- stroke: "currentColor",
614
- strokeWidth: "1",
615
- strokeOpacity: "0.5"
616
- }
617
- ),
618
- /* @__PURE__ */ jsxRuntime.jsx(
619
- "polygon",
620
- {
621
- points: "8,20 32,32 32,56 8,44",
622
- className: "fill-primary/20",
623
- stroke: "currentColor",
624
- strokeWidth: "1",
625
- strokeOpacity: "0.5"
626
- }
627
- ),
628
- /* @__PURE__ */ jsxRuntime.jsx(
629
- "polygon",
630
- {
631
- points: "56,20 32,32 32,56 56,44",
632
- className: "fill-primary/30",
633
- stroke: "currentColor",
634
- strokeWidth: "1",
635
- strokeOpacity: "0.5"
636
- }
637
- )
638
- ]
639
- }
640
- )
641
- }
642
- )
643
- ] }),
644
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute top-3 left-3 text-xs font-semibold px-2.5 py-1 rounded-full border border-primary/30 bg-primary/10 text-foreground shadow-sm", children: "Featured" }),
645
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-4 left-4 w-14 h-14 rounded-xl shadow-lg overflow-hidden border-2 border-background", children: product.icon ? /* @__PURE__ */ jsxRuntime.jsx(
646
- "img",
647
- {
648
- src: product.icon,
649
- alt: "",
650
- "aria-hidden": "true",
651
- className: "w-full h-full object-cover"
652
- }
653
- ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full bg-gradient-to-br from-primary/25 to-primary/10 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs(
654
- "svg",
655
- {
656
- viewBox: "0 0 32 32",
657
- className: "w-8 h-8",
658
- fill: "none",
659
- xmlns: "http://www.w3.org/2000/svg",
660
- "aria-hidden": "true",
661
- children: [
662
- /* @__PURE__ */ jsxRuntime.jsx(
663
- "polygon",
664
- {
665
- points: "16,4 28,10 16,16 4,10",
666
- className: "fill-primary/40",
667
- stroke: "currentColor",
668
- strokeWidth: "0.6",
669
- strokeOpacity: "0.4"
670
- }
671
- ),
672
- /* @__PURE__ */ jsxRuntime.jsx(
673
- "polygon",
674
- {
675
- points: "4,10 16,16 16,28 4,22",
676
- className: "fill-primary/20",
677
- stroke: "currentColor",
678
- strokeWidth: "0.6",
679
- strokeOpacity: "0.4"
680
- }
681
- ),
682
- /* @__PURE__ */ jsxRuntime.jsx(
683
- "polygon",
684
- {
685
- points: "28,10 16,16 16,28 28,22",
686
- className: "fill-primary/30",
687
- stroke: "currentColor",
688
- strokeWidth: "0.6",
689
- strokeOpacity: "0.4"
690
- }
691
- )
692
- ]
693
- }
694
- ) }) })
695
- ] }),
696
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-5 flex flex-col flex-1", children: [
697
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3 mb-2", children: [
698
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-base font-bold text-foreground group-hover:text-primary transition-colors line-clamp-1", children: product.name }),
699
- /* @__PURE__ */ jsxRuntime.jsx(
700
- "span",
701
- {
702
- className: `text-sm font-bold flex-shrink-0 ${priceLabel === "Free" ? "text-emerald-700 dark:text-emerald-400" : "text-foreground"}`,
703
- children: priceLabel
704
- }
705
- )
706
- ] }),
707
- product.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground line-clamp-2 leading-relaxed flex-1", children: product.description }),
708
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 flex items-center gap-2", children: [
709
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs px-2 py-0.5 rounded-full bg-primary/10 text-primary font-medium", children: product.type }),
710
- product.authorName && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground truncate", children: [
711
- "by ",
712
- product.authorName
713
- ] })
714
- ] })
715
- ] })
716
- ]
717
- }
718
- );
719
- }
720
- function ScrollRow({
721
- products,
722
- onProductClick
723
- }) {
724
- const ref = React.useRef(null);
725
- function scroll(dir) {
726
- ref.current?.scrollBy({
727
- left: dir === "r" ? 272 : -272,
728
- behavior: "smooth"
729
- });
730
- }
731
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative group/row", children: [
732
- /* @__PURE__ */ jsxRuntime.jsx(
733
- "button",
734
- {
735
- type: "button",
736
- onClick: () => scroll("l"),
737
- "aria-label": "Scroll left",
738
- className: "absolute left-0 top-1/2 -translate-y-1/2 -translate-x-4 z-10 w-8 h-8 rounded-full bg-background border border-border shadow-md items-center justify-center text-muted-foreground hover:text-primary hover:border-primary/40 transition-all hidden sm:flex opacity-0 group-hover/row:opacity-100",
739
- children: /* @__PURE__ */ jsxRuntime.jsx(
740
- "svg",
741
- {
742
- viewBox: "0 0 16 16",
743
- className: "w-4 h-4",
744
- fill: "none",
745
- stroke: "currentColor",
746
- strokeWidth: "1.5",
747
- strokeLinecap: "round",
748
- strokeLinejoin: "round",
749
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M10 3L6 8l4 5" })
750
- }
751
- )
752
- }
753
- ),
754
- /* @__PURE__ */ jsxRuntime.jsx(
755
- "div",
756
- {
757
- ref,
758
- className: "flex gap-3 overflow-x-auto pb-1 snap-x snap-mandatory",
759
- style: { scrollbarWidth: "none", msOverflowStyle: "none" },
760
- children: products.map((product) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0 w-56 snap-start", children: /* @__PURE__ */ jsxRuntime.jsx(
761
- ProductCard,
762
- {
763
- product,
764
- onClick: () => onProductClick(product)
765
- }
766
- ) }, product.id))
767
- }
768
- ),
769
- /* @__PURE__ */ jsxRuntime.jsx(
770
- "button",
771
- {
772
- type: "button",
773
- onClick: () => scroll("r"),
774
- "aria-label": "Scroll right",
775
- className: "absolute right-0 top-1/2 -translate-y-1/2 translate-x-4 z-10 w-8 h-8 rounded-full bg-background border border-border shadow-md items-center justify-center text-muted-foreground hover:text-primary hover:border-primary/40 transition-all hidden sm:flex opacity-0 group-hover/row:opacity-100",
776
- children: /* @__PURE__ */ jsxRuntime.jsx(
777
- "svg",
778
- {
779
- viewBox: "0 0 16 16",
780
- className: "w-4 h-4",
781
- fill: "none",
782
- stroke: "currentColor",
783
- strokeWidth: "1.5",
784
- strokeLinecap: "round",
785
- strokeLinejoin: "round",
786
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 3l4 5-4 5" })
787
- }
788
- )
789
- }
790
- )
791
- ] });
792
- }
793
- function SectionHead({
794
- title,
795
- hint,
796
- browseHref,
797
- onNavigate
798
- }) {
799
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-end justify-between mb-5 gap-4", children: [
800
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
801
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl font-bold text-foreground", children: title }),
802
- hint && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mt-0.5", children: hint })
803
- ] }),
804
- /* @__PURE__ */ jsxRuntime.jsx(
805
- "button",
806
- {
807
- type: "button",
808
- onClick: () => onNavigate(browseHref),
809
- className: "text-sm text-primary hover:underline font-medium flex-shrink-0 underline-offset-2",
810
- children: "See all \u2192"
811
- }
812
- )
813
- ] });
814
- }
815
- var DEFAULT_TYPE_FILTERS = [
816
- { type: "NODE", label: "Nodes" },
817
- { type: "WORKFLOW", label: "Workflows" },
818
- { type: "TEMPLATE", label: "Templates" },
819
- { type: "INTEGRATION", label: "Integrations" },
820
- { type: "APP", label: "Apps" },
821
- { type: "EXTENSION", label: "Extensions" }
822
- ];
823
- function TypeFilters({
824
- browsePath,
825
- onNavigate,
826
- typeFilters
827
- }) {
828
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2 justify-center", children: typeFilters.map((f) => /* @__PURE__ */ jsxRuntime.jsx(
829
- "button",
830
- {
831
- type: "button",
832
- onClick: () => onNavigate(`${browsePath}?type=${f.type}`),
833
- className: "px-4 py-1.5 rounded-full border border-border bg-background text-sm text-foreground hover:border-primary/50 hover:bg-primary/5 hover:text-primary transition-all duration-200",
834
- children: f.label
835
- },
836
- f.type
837
- )) });
838
- }
839
- function HeroCardSkeleton() {
840
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-2xl border border-border bg-card overflow-hidden animate-pulse h-full", children: [
841
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-44 sm:h-52 bg-muted" }),
842
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-5 space-y-3", children: [
843
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-5 bg-muted rounded w-1/2" }),
844
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 bg-muted rounded w-3/4" }),
845
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 bg-muted rounded w-5/6" }),
846
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-5 bg-muted rounded w-16 mt-2" })
847
- ] })
848
- ] });
849
- }
850
- function GridCardSkeleton() {
851
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-xl border border-border bg-card p-4 h-36 animate-pulse", children: [
852
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3 mb-3", children: [
853
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-12 h-12 rounded-lg bg-muted flex-shrink-0" }),
854
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 space-y-2 pt-1", children: [
855
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 bg-muted rounded w-3/4" }),
856
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 bg-muted rounded w-1/2" })
857
- ] })
858
- ] }),
859
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 bg-muted rounded w-full mb-2" }),
860
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 bg-muted rounded w-5/6" })
861
- ] });
862
- }
863
- function ScrollCardSkeleton() {
864
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-shrink-0 w-56 rounded-xl border border-border bg-card p-4 h-36 animate-pulse", children: [
865
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3 mb-3", children: [
866
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-12 h-12 rounded-lg bg-muted flex-shrink-0" }),
867
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 space-y-2 pt-1", children: [
868
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 bg-muted rounded w-3/4" }),
869
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 bg-muted rounded w-1/2" })
870
- ] })
871
- ] }),
872
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 bg-muted rounded w-full mb-2" }),
873
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 bg-muted rounded w-5/6" })
874
- ] });
875
- }
876
- function StoreHomePage({
877
- compatibleWith,
878
- appLoginUrl,
879
- onNavigate,
880
- terminology,
881
- productName,
882
- heroTitle,
883
- heroSubtitle,
884
- browsePath = "/store/browse",
885
- seoTitle,
886
- seoDescription,
887
- seoKeywords
888
- }) {
889
- const client = useWebSDK();
890
- const [grouped, setGrouped] = React.useState(null);
891
- const [loading, setLoading] = React.useState(true);
892
- const [error, setError] = React.useState(null);
893
- const [retryKey, setRetryKey] = React.useState(0);
894
- React.useEffect(() => {
895
- let cancelled = false;
896
- let autoRetryTimer = null;
897
- async function load() {
898
- setLoading(true);
899
- try {
900
- const res = await client.query(
901
- HOME_GROUPED_PRODUCTS_QUERY,
902
- { compatibleWith: compatibleWith ?? null }
903
- );
904
- if (cancelled) return;
905
- if (res.errors?.length) throw new Error(res.errors[0].message);
906
- if (!res.data?.homeGroupedProducts) throw new Error("No data");
907
- setGrouped(res.data.homeGroupedProducts);
908
- setError(null);
909
- } catch {
910
- if (!cancelled) {
911
- if (retryKey < 1) {
912
- autoRetryTimer = setTimeout(() => {
913
- if (!cancelled) setRetryKey(1);
914
- }, 2e3);
915
- } else {
916
- setError("Failed to load store. Please try again.");
917
- }
918
- }
919
- } finally {
920
- if (!cancelled) setLoading(false);
921
- }
922
- }
923
- load();
924
- return () => {
925
- cancelled = true;
926
- if (autoRetryTimer) clearTimeout(autoRetryTimer);
927
- };
928
- }, [client, compatibleWith, retryKey]);
929
- function navigate(path) {
930
- if (onNavigate) {
931
- onNavigate(path);
932
- return;
933
- }
934
- window.location.href = path;
935
- }
936
- function handleProductClick(product) {
937
- navigate(`/store/${product.slug}`);
938
- }
939
- const deduped = grouped ? dedupAcross([grouped.featured, grouped.mostDownloaded, grouped.freeItems]) : [[], [], []];
940
- const featuredItems = deduped[0] ?? [];
941
- const popularItems = deduped[1] ?? [];
942
- const freeItems = deduped[2] ?? [];
943
- const hasNoItems = !loading && !error && featuredItems.length === 0 && popularItems.length === 0 && freeItems.length === 0;
944
- const resolvedTitle = heroTitle ?? `${productName ? `${productName} ` : ""}Store`;
945
- const resolvedSubtitle = heroSubtitle ?? "Discover workflows, nodes, integrations, and templates built by the community.";
946
- const typeFilters = terminology?.typeFilters ?? DEFAULT_TYPE_FILTERS;
947
- const searchPlaceholder = terminology?.searchPlaceholder ?? "Search nodes, workflows, templates\u2026";
948
- const emptyStateDescription = terminology?.emptyStateDescription ?? "As the community ships workflows, templates, and integrations, they will show up right here.";
949
- const publisherCtaDescription = terminology?.publisherCtaDescription ?? `Share your workflows, nodes, and integrations with ${productName ? "the " : ""}community. Earn from your work or contribute for free.`;
950
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full", children: [
951
- /* @__PURE__ */ jsxRuntime.jsx(
952
- PageHead,
953
- {
954
- title: seoTitle ?? `Store${productName ? ` \u2013 ${productName}` : ""}`,
955
- description: seoDescription ?? resolvedSubtitle,
956
- keywords: seoKeywords,
957
- productName
958
- }
959
- ),
960
- /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "relative overflow-hidden pt-14 pb-12 sm:pt-20 sm:pb-16", children: [
961
- /* @__PURE__ */ jsxRuntime.jsx(
962
- "div",
963
- {
964
- className: "absolute inset-0 bg-gradient-to-b from-background via-primary/[0.06] to-background pointer-events-none",
965
- "aria-hidden": "true"
966
- }
967
- ),
968
- /* @__PURE__ */ jsxRuntime.jsxs(
969
- "svg",
970
- {
971
- className: "absolute inset-0 w-full h-full opacity-[0.035] pointer-events-none",
972
- xmlns: "http://www.w3.org/2000/svg",
973
- "aria-hidden": "true",
974
- children: [
975
- /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsx(
976
- "pattern",
977
- {
978
- id: "store-home-grid",
979
- width: "40",
980
- height: "40",
981
- patternUnits: "userSpaceOnUse",
982
- children: /* @__PURE__ */ jsxRuntime.jsx(
983
- "path",
984
- {
985
- d: "M 40 0 L 0 0 0 40",
986
- fill: "none",
987
- stroke: "currentColor",
988
- strokeWidth: "1"
989
- }
990
- )
991
- }
992
- ) }),
993
- /* @__PURE__ */ jsxRuntime.jsx("rect", { width: "100%", height: "100%", fill: "url(#store-home-grid)" })
994
- ]
995
- }
996
- ),
997
- /* @__PURE__ */ jsxRuntime.jsx(
998
- "div",
999
- {
1000
- className: "absolute -top-16 left-1/3 w-80 h-80 rounded-full bg-primary/10 blur-3xl pointer-events-none",
1001
- "aria-hidden": "true"
1002
- }
1003
- ),
1004
- /* @__PURE__ */ jsxRuntime.jsx(
1005
- "div",
1006
- {
1007
- className: "absolute top-8 right-1/4 w-56 h-56 rounded-full bg-primary/8 blur-2xl pointer-events-none",
1008
- "aria-hidden": "true"
1009
- }
1010
- ),
1011
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 text-center", children: [
1012
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-2 px-3 py-1 rounded-full border border-primary/30 bg-primary/10 text-xs font-semibold text-foreground mb-5", children: [
1013
- /* @__PURE__ */ jsxRuntime.jsx(
1014
- "span",
1015
- {
1016
- className: "w-1.5 h-1.5 rounded-full bg-primary flex-shrink-0",
1017
- "aria-hidden": "true"
1018
- }
1019
- ),
1020
- "Community Marketplace"
1021
- ] }),
1022
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-4xl sm:text-5xl md:text-6xl font-extrabold tracking-tight text-foreground mb-4 leading-[1.08]", children: resolvedTitle }),
1023
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg text-muted-foreground max-w-2xl mx-auto mb-8 leading-relaxed", children: resolvedSubtitle }),
1024
- /* @__PURE__ */ jsxRuntime.jsxs(
1025
- "form",
1026
- {
1027
- className: "max-w-xl mx-auto flex gap-2 mb-7",
1028
- onSubmit: (e) => {
1029
- e.preventDefault();
1030
- const q = new FormData(e.currentTarget).get("q");
1031
- navigate(
1032
- q.trim() ? `${browsePath}?q=${encodeURIComponent(q.trim())}` : browsePath
1033
- );
1034
- },
1035
- children: [
1036
- /* @__PURE__ */ jsxRuntime.jsx(
1037
- "input",
1038
- {
1039
- name: "q",
1040
- type: "search",
1041
- placeholder: searchPlaceholder,
1042
- className: "flex-1 h-12 rounded-xl border border-border bg-background px-4 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/40 shadow-sm"
1043
- }
1044
- ),
1045
- /* @__PURE__ */ jsxRuntime.jsx(
1046
- "button",
1047
- {
1048
- type: "submit",
1049
- className: "h-12 px-6 rounded-xl bg-primary text-primary-foreground text-sm font-semibold hover:opacity-90 transition-opacity shadow-sm",
1050
- children: "Search"
1051
- }
1052
- )
1053
- ]
1054
- }
1055
- ),
1056
- /* @__PURE__ */ jsxRuntime.jsx(
1057
- TypeFilters,
1058
- {
1059
- browsePath,
1060
- onNavigate: navigate,
1061
- typeFilters
1062
- }
1063
- )
1064
- ] })
1065
- ] }),
1066
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pb-20 space-y-14 pt-4", children: [
1067
- (loading || error || featuredItems.length > 0) && /* @__PURE__ */ jsxRuntime.jsxs("section", { children: [
1068
- /* @__PURE__ */ jsxRuntime.jsx(
1069
- SectionHead,
1070
- {
1071
- title: "Featured",
1072
- hint: "Hand-picked highlights from the community",
1073
- browseHref: `${browsePath}?featured=true`,
1074
- onNavigate: navigate
1075
- }
1076
- ),
1077
- loading ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-[1fr_2fr] gap-4 items-start", children: [
1078
- /* @__PURE__ */ jsxRuntime.jsx(HeroCardSkeleton, {}),
1079
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-4", children: [...Array(4)].map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(GridCardSkeleton, {}, i)) })
1080
- ] }) : error ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "py-16 text-center", children: [
1081
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground mb-4", children: error }),
1082
- /* @__PURE__ */ jsxRuntime.jsx(
1083
- "button",
1084
- {
1085
- type: "button",
1086
- onClick: () => setRetryKey((k) => k + 1),
1087
- className: "text-sm text-primary hover:underline",
1088
- children: "Retry"
1089
- }
1090
- )
1091
- ] }) : featuredItems.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-[1fr_2fr] gap-4 items-start", children: [
1092
- /* @__PURE__ */ jsxRuntime.jsx(
1093
- FeaturedHeroCard,
1094
- {
1095
- product: featuredItems[0],
1096
- onNavigate: navigate
1097
- }
1098
- ),
1099
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-4", children: featuredItems.slice(1, 5).map((product) => /* @__PURE__ */ jsxRuntime.jsx(
1100
- ProductCard,
1101
- {
1102
- product,
1103
- onClick: () => handleProductClick(product)
1104
- },
1105
- product.id
1106
- )) })
1107
- ] }) : null
1108
- ] }),
1109
- (loading || popularItems.length > 0) && /* @__PURE__ */ jsxRuntime.jsxs("section", { children: [
1110
- /* @__PURE__ */ jsxRuntime.jsx(
1111
- SectionHead,
1112
- {
1113
- title: "Most Popular",
1114
- hint: "Top downloads across the community",
1115
- browseHref: `${browsePath}?sortBy=DOWNLOADS`,
1116
- onNavigate: navigate
1117
- }
1118
- ),
1119
- loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-3 overflow-hidden", children: [...Array(5)].map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(ScrollCardSkeleton, {}, i)) }) : /* @__PURE__ */ jsxRuntime.jsx(
1120
- ScrollRow,
1121
- {
1122
- products: popularItems,
1123
- onProductClick: handleProductClick
1124
- }
1125
- )
1126
- ] }),
1127
- (loading || freeItems.length > 0) && /* @__PURE__ */ jsxRuntime.jsxs("section", { children: [
1128
- /* @__PURE__ */ jsxRuntime.jsx(
1129
- SectionHead,
1130
- {
1131
- title: "Free to Use",
1132
- hint: "Zero cost, full power \u2014 community-contributed freebies",
1133
- browseHref: `${browsePath}?pricingModel=FREE`,
1134
- onNavigate: navigate
1135
- }
1136
- ),
1137
- loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4", children: [...Array(6)].map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(GridCardSkeleton, {}, i)) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4", children: freeItems.slice(0, 6).map((product) => /* @__PURE__ */ jsxRuntime.jsx(
1138
- ProductCard,
1139
- {
1140
- product,
1141
- onClick: () => handleProductClick(product)
1142
- },
1143
- product.id
1144
- )) })
1145
- ] }),
1146
- hasNoItems && /* @__PURE__ */ jsxRuntime.jsxs(
1147
- "section",
1148
- {
1149
- "data-store": "empty-state",
1150
- className: "rounded-2xl border border-border bg-card/50 px-6 py-14 sm:py-16 text-center",
1151
- children: [
1152
- /* @__PURE__ */ jsxRuntime.jsx(
1153
- "div",
1154
- {
1155
- className: "mx-auto mb-5 flex h-14 w-14 items-center justify-center rounded-2xl bg-primary/10",
1156
- "aria-hidden": "true",
1157
- children: /* @__PURE__ */ jsxRuntime.jsx(
1158
- "svg",
1159
- {
1160
- viewBox: "0 0 24 24",
1161
- fill: "none",
1162
- stroke: "currentColor",
1163
- strokeWidth: "1.5",
1164
- className: "h-7 w-7 text-primary",
1165
- children: /* @__PURE__ */ jsxRuntime.jsx(
1166
- "path",
1167
- {
1168
- strokeLinecap: "round",
1169
- strokeLinejoin: "round",
1170
- d: "M3.75 9h16.5M4.5 9l1.2 9.3a1.5 1.5 0 0 0 1.49 1.2h9.62a1.5 1.5 0 0 0 1.49-1.2L19.5 9M8.25 9V6.75a3.75 3.75 0 0 1 7.5 0V9"
1171
- }
1172
- )
1173
- }
1174
- )
1175
- }
1176
- ),
1177
- /* @__PURE__ */ jsxRuntime.jsxs("h2", { className: "text-xl sm:text-2xl font-bold text-foreground mb-2", children: [
1178
- "The ",
1179
- productName ?? "store",
1180
- " catalogue is just getting started"
1181
- ] }),
1182
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm sm:text-base text-muted-foreground max-w-xl mx-auto mb-7", children: [
1183
- "No items have been published for ",
1184
- productName ?? "this product",
1185
- " ",
1186
- "yet. ",
1187
- emptyStateDescription
1188
- ] }),
1189
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-center gap-3", children: [
1190
- /* @__PURE__ */ jsxRuntime.jsx(
1191
- "button",
1192
- {
1193
- type: "button",
1194
- onClick: () => navigate(browsePath),
1195
- className: "inline-flex items-center gap-2 rounded-xl bg-primary px-5 py-2.5 text-sm font-semibold text-primary-foreground transition-colors hover:bg-primary/90",
1196
- children: "Browse the full store"
1197
- }
1198
- ),
1199
- /* @__PURE__ */ jsxRuntime.jsx(
1200
- "a",
1201
- {
1202
- href: appLoginUrl,
1203
- className: "inline-flex items-center gap-2 rounded-xl border border-border px-5 py-2.5 text-sm font-semibold text-foreground transition-colors hover:border-primary/40 hover:text-primary",
1204
- children: "Become a publisher"
1205
- }
1206
- )
1207
- ] })
1208
- ]
1209
- }
1210
- )
1211
- ] }),
1212
- /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "relative overflow-hidden border-t border-border", children: [
1213
- /* @__PURE__ */ jsxRuntime.jsx(
1214
- "div",
1215
- {
1216
- className: "absolute inset-0 bg-gradient-to-br from-primary/8 via-background to-primary/5 pointer-events-none",
1217
- "aria-hidden": "true"
1218
- }
1219
- ),
1220
- /* @__PURE__ */ jsxRuntime.jsx(
1221
- "div",
1222
- {
1223
- className: "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-80 h-40 rounded-full bg-primary/12 blur-3xl pointer-events-none",
1224
- "aria-hidden": "true"
1225
- }
1226
- ),
1227
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative max-w-2xl mx-auto px-4 py-16 text-center", children: [
1228
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-2 px-3 py-1 rounded-full border border-primary/30 bg-primary/10 text-xs font-semibold text-foreground mb-5", children: [
1229
- /* @__PURE__ */ jsxRuntime.jsx(
1230
- "span",
1231
- {
1232
- className: "w-1.5 h-1.5 rounded-full bg-primary flex-shrink-0",
1233
- "aria-hidden": "true"
1234
- }
1235
- ),
1236
- "Become a publisher"
1237
- ] }),
1238
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl sm:text-3xl font-bold text-foreground mb-3", children: "Built something useful?" }),
1239
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground mb-8 max-w-lg mx-auto leading-relaxed", children: publisherCtaDescription }),
1240
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col sm:flex-row gap-3 justify-center", children: [
1241
- /* @__PURE__ */ jsxRuntime.jsx(
1242
- "a",
1243
- {
1244
- href: appLoginUrl,
1245
- target: "_blank",
1246
- rel: "noopener noreferrer",
1247
- className: "inline-flex items-center justify-center h-11 px-7 rounded-lg bg-primary text-primary-foreground text-sm font-semibold hover:opacity-90 transition-opacity",
1248
- children: "Submit to Store"
1249
- }
1250
- ),
1251
- /* @__PURE__ */ jsxRuntime.jsx(
1252
- "button",
1253
- {
1254
- type: "button",
1255
- onClick: () => navigate(browsePath),
1256
- className: "inline-flex items-center justify-center h-11 px-7 rounded-lg border border-border bg-background text-sm font-medium text-foreground hover:border-primary/40 hover:bg-primary/5 transition-all",
1257
- children: "Browse the store"
1258
- }
1259
- )
1260
- ] }),
1261
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-4 text-xs text-muted-foreground", children: [
1262
- "By submitting a listing you agree to the",
1263
- " ",
1264
- /* @__PURE__ */ jsxRuntime.jsx(
1265
- "a",
1266
- {
1267
- href: "https://burdenoff.com/developer-agreement",
1268
- target: "_blank",
1269
- rel: "noopener noreferrer",
1270
- className: "underline hover:text-foreground",
1271
- children: "Marketplace & Developer Agreement"
1272
- }
1273
- ),
1274
- "."
1275
- ] })
1276
- ] })
1277
- ] })
1278
- ] });
1279
- }
1280
- var SORT_OPTIONS = [
1281
- { value: "RELEVANCE", label: "Relevance" },
1282
- { value: "NEWEST", label: "Newest" },
1283
- { value: "DOWNLOADS", label: "Most Downloaded" },
1284
- { value: "RATING", label: "Top Rated" },
1285
- { value: "PRICE_LOW", label: "Price: Low to High" },
1286
- { value: "PRICE_HIGH", label: "Price: High to Low" },
1287
- { value: "NAME_ASC", label: "Name A\u2013Z" }
1288
- ];
1289
- var DEFAULT_TYPE_OPTIONS = [
1290
- { type: "APP", label: "Apps" },
1291
- { type: "NODE", label: "Nodes" },
1292
- { type: "WORKFLOW", label: "Workflows" },
1293
- { type: "TEMPLATE", label: "Templates" },
1294
- { type: "INTEGRATION", label: "Integrations" },
1295
- { type: "EXTENSION", label: "Extensions" },
1296
- { type: "THEME", label: "Themes" }
1297
- ];
1298
- function buildTypeOptions(typeFilters) {
1299
- return [{ type: "", label: "All types" }, ...typeFilters];
1300
- }
1301
- var PRICING_OPTIONS = [
1302
- { value: "", label: "All pricing" },
1303
- { value: "FREE", label: "Free" },
1304
- { value: "ONE_TIME", label: "Paid (one-time)" },
1305
- { value: "SUBSCRIPTION", label: "Subscription" }
1306
- ];
1307
- function parseUrlParams() {
1308
- if (typeof window === "undefined")
1309
- return {
1310
- q: "",
1311
- type: "",
1312
- pricingModel: "",
1313
- category: "",
1314
- sortBy: "RELEVANCE",
1315
- featured: false
1316
- };
1317
- const p = new URLSearchParams(window.location.search);
1318
- return {
1319
- q: p.get("q") ?? "",
1320
- type: p.get("type") ?? "",
1321
- pricingModel: p.get("pricingModel") ?? "",
1322
- category: p.get("category") ?? "",
1323
- sortBy: p.get("sortBy") ?? (p.get("q") ? "RELEVANCE" : "NEWEST"),
1324
- featured: p.get("featured") === "true"
1325
- };
1326
- }
1327
- function FacetList({
1328
- title,
1329
- facets,
1330
- selected,
1331
- onSelect
1332
- }) {
1333
- if (facets.length === 0) return null;
1334
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1335
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-2", children: title }),
1336
- /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-1", children: facets.map((f) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsxs(
1337
- "button",
1338
- {
1339
- type: "button",
1340
- onClick: () => onSelect(selected === f.value ? "" : f.value),
1341
- className: `w-full text-left text-sm px-2 py-1 rounded flex items-center justify-between gap-2 transition-colors ${selected === f.value ? "bg-primary/10 text-primary font-medium" : "text-muted-foreground hover:text-foreground hover:bg-muted/50"}`,
1342
- children: [
1343
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: f.value }),
1344
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs flex-shrink-0", children: f.count })
1345
- ]
1346
- }
1347
- ) }, f.value)) })
1348
- ] });
1349
- }
1350
- function StoreBrowsePage({
1351
- compatibleWith,
1352
- onNavigate,
1353
- terminology,
1354
- productName,
1355
- seoTitle,
1356
- seoDescription
1357
- }) {
1358
- const client = useWebSDK();
1359
- const typeFilters = terminology?.typeFilters ?? DEFAULT_TYPE_OPTIONS;
1360
- const typeOptions = buildTypeOptions(typeFilters);
1361
- const browseDescription = terminology?.browseDescription ?? "Browse all apps, workflows, nodes, and templates.";
1362
- const initial = parseUrlParams();
1363
- const [search, setSearch] = React.useState(initial.q);
1364
- const [inputValue, setInputValue] = React.useState(initial.q);
1365
- const [type, setType] = React.useState(initial.type);
1366
- const [pricingModel, setPricingModel] = React.useState(initial.pricingModel);
1367
- const [category, setCategory] = React.useState(initial.category);
1368
- const [sortBy, setSortBy] = React.useState(initial.sortBy);
1369
- const [featured, setFeatured] = React.useState(initial.featured);
1370
- const [offset, setOffset] = React.useState(0);
1371
- const [result, setResult] = React.useState(null);
1372
- const [allProducts, setAllProducts] = React.useState(
1373
- []
1374
- );
1375
- const [categories, setCategories] = React.useState([]);
1376
- const [loading, setLoading] = React.useState(true);
1377
- const [loadingMore, setLoadingMore] = React.useState(false);
1378
- const [filtersOpen, setFiltersOpen] = React.useState(false);
1379
- const searchRef = React.useRef(null);
1380
- const PAGE_SIZE = 24;
1381
- React.useEffect(() => {
1382
- client.query(CATEGORIES_QUERY, {}).then((r) => {
1383
- if (r.data?.categories) setCategories(r.data.categories);
1384
- }).catch(() => {
1385
- });
1386
- }, [client]);
1387
- React.useEffect(() => {
1388
- let cancelled = false;
1389
- setOffset(0);
1390
- setLoading(true);
1391
- setAllProducts([]);
1392
- client.query(BROWSE_STORE_QUERY, {
1393
- input: {
1394
- filter: {
1395
- search: search || void 0,
1396
- type: type || void 0,
1397
- pricingModel: pricingModel || void 0,
1398
- category: category || void 0,
1399
- compatibleWith: compatibleWith ?? void 0,
1400
- featured: featured || void 0
1401
- },
1402
- sortBy,
1403
- pagination: { limit: PAGE_SIZE, offset: 0 }
1404
- }
1405
- }).then((r) => {
1406
- if (cancelled) return;
1407
- if (r.data?.browseStore) {
1408
- setResult(r.data.browseStore);
1409
- setAllProducts(r.data.browseStore.products);
1410
- }
1411
- }).catch(() => {
1412
- }).finally(() => {
1413
- if (!cancelled) setLoading(false);
1414
- });
1415
- return () => {
1416
- cancelled = true;
1417
- };
1418
- }, [
1419
- client,
1420
- search,
1421
- type,
1422
- pricingModel,
1423
- category,
1424
- sortBy,
1425
- compatibleWith,
1426
- featured
1427
- ]);
1428
- function loadMore() {
1429
- const nextOffset = offset + PAGE_SIZE;
1430
- setLoadingMore(true);
1431
- client.query(BROWSE_STORE_QUERY, {
1432
- input: {
1433
- filter: {
1434
- search: search || void 0,
1435
- type: type || void 0,
1436
- pricingModel: pricingModel || void 0,
1437
- category: category || void 0,
1438
- compatibleWith: compatibleWith ?? void 0,
1439
- featured: featured || void 0
1440
- },
1441
- sortBy,
1442
- pagination: { limit: PAGE_SIZE, offset: nextOffset }
1443
- }
1444
- }).then((r) => {
1445
- if (r.data?.browseStore) {
1446
- setResult(r.data.browseStore);
1447
- setAllProducts((prev) => [...prev, ...r.data.browseStore.products]);
1448
- setOffset(nextOffset);
1449
- }
1450
- }).catch(() => {
1451
- }).finally(() => setLoadingMore(false));
1452
- }
1453
- function navigate(path) {
1454
- if (onNavigate) {
1455
- onNavigate(path);
1456
- return;
1457
- }
1458
- window.location.href = path;
1459
- }
1460
- function handleSearchSubmit(e) {
1461
- e.preventDefault();
1462
- setSearch(inputValue);
1463
- setSortBy(inputValue ? "RELEVANCE" : "NEWEST");
1464
- }
1465
- function resetFilters() {
1466
- setSearch("");
1467
- setInputValue("");
1468
- setType("");
1469
- setPricingModel("");
1470
- setCategory("");
1471
- setSortBy("NEWEST");
1472
- setFeatured(false);
1473
- searchRef.current?.focus();
1474
- }
1475
- const hasActiveFilters = !!(search || type || pricingModel || category || featured);
1476
- const facetTypes = result?.facets?.types ?? [];
1477
- const facetCategories = result?.facets?.categories ?? [];
1478
- const sidebarContent = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6", children: [
1479
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1480
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-2", children: "Type" }),
1481
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1", children: typeOptions.map((opt) => /* @__PURE__ */ jsxRuntime.jsx(
1482
- "button",
1483
- {
1484
- type: "button",
1485
- onClick: () => setType(opt.type),
1486
- className: `w-full text-left text-sm px-2 py-1.5 rounded transition-colors ${type === opt.type ? "bg-primary/10 text-primary font-medium" : "text-muted-foreground hover:text-foreground hover:bg-muted/50"}`,
1487
- children: opt.label
1488
- },
1489
- opt.type
1490
- )) })
1491
- ] }),
1492
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1493
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-2", children: "Pricing" }),
1494
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1", children: PRICING_OPTIONS.map((opt) => /* @__PURE__ */ jsxRuntime.jsx(
1495
- "button",
1496
- {
1497
- type: "button",
1498
- onClick: () => setPricingModel(opt.value),
1499
- className: `w-full text-left text-sm px-2 py-1.5 rounded transition-colors ${pricingModel === opt.value ? "bg-primary/10 text-primary font-medium" : "text-muted-foreground hover:text-foreground hover:bg-muted/50"}`,
1500
- children: opt.label
1501
- },
1502
- opt.value
1503
- )) })
1504
- ] }),
1505
- (facetCategories.length > 0 || categories.length > 0) && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1506
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-2", children: "Category" }),
1507
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
1508
- /* @__PURE__ */ jsxRuntime.jsx(
1509
- "button",
1510
- {
1511
- type: "button",
1512
- onClick: () => setCategory(""),
1513
- className: `w-full text-left text-sm px-2 py-1.5 rounded transition-colors ${!category ? "bg-primary/10 text-primary font-medium" : "text-muted-foreground hover:text-foreground hover:bg-muted/50"}`,
1514
- children: "All categories"
1515
- }
1516
- ),
1517
- (facetCategories.length > 0 ? facetCategories : categories.slice(0, 12).map((c) => ({ value: c.slug, count: 0 }))).map((f) => {
1518
- const cat = categories.find((c) => c.slug === f.value);
1519
- return /* @__PURE__ */ jsxRuntime.jsxs(
1520
- "button",
1521
- {
1522
- type: "button",
1523
- onClick: () => setCategory(category === f.value ? "" : f.value),
1524
- className: `w-full text-left text-sm px-2 py-1.5 rounded flex items-center justify-between gap-2 transition-colors ${category === f.value ? "bg-primary/10 text-primary font-medium" : "text-muted-foreground hover:text-foreground hover:bg-muted/50"}`,
1525
- children: [
1526
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: cat?.name ?? f.value }),
1527
- f.count > 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs flex-shrink-0", children: f.count })
1528
- ]
1529
- },
1530
- f.value
1531
- );
1532
- })
1533
- ] })
1534
- ] }),
1535
- facetTypes.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
1536
- FacetList,
1537
- {
1538
- title: "Available types",
1539
- facets: facetTypes,
1540
- selected: type,
1541
- onSelect: setType
1542
- }
1543
- ),
1544
- hasActiveFilters && /* @__PURE__ */ jsxRuntime.jsx(
1545
- "button",
1546
- {
1547
- type: "button",
1548
- onClick: resetFilters,
1549
- className: "text-sm text-muted-foreground hover:text-foreground underline",
1550
- children: "Clear all filters"
1551
- }
1552
- )
1553
- ] });
1554
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full", children: [
1555
- /* @__PURE__ */ jsxRuntime.jsx(
1556
- PageHead,
1557
- {
1558
- title: seoTitle ?? `Browse Store${productName ? ` \u2013 ${productName}` : ""}`,
1559
- description: seoDescription ?? browseDescription,
1560
- productName
1561
- }
1562
- ),
1563
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8", children: [
1564
- /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "mb-6", children: [
1565
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl font-bold tracking-tight", children: productName ? `Browse the ${productName} Store` : "Browse the Store" }),
1566
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-muted-foreground", children: browseDescription })
1567
- ] }),
1568
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col sm:flex-row gap-3 mb-6", children: [
1569
- /* @__PURE__ */ jsxRuntime.jsxs("form", { className: "flex-1 flex gap-2", onSubmit: handleSearchSubmit, children: [
1570
- /* @__PURE__ */ jsxRuntime.jsx(
1571
- "input",
1572
- {
1573
- ref: searchRef,
1574
- type: "search",
1575
- "aria-label": "Search store items",
1576
- value: inputValue,
1577
- onChange: (e) => setInputValue(e.target.value),
1578
- placeholder: "Search\u2026",
1579
- className: "flex-1 h-10 rounded-lg border border-border bg-background px-4 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50"
1580
- }
1581
- ),
1582
- /* @__PURE__ */ jsxRuntime.jsx(
1583
- "button",
1584
- {
1585
- type: "submit",
1586
- className: "h-10 px-5 rounded-lg bg-primary text-primary-foreground text-sm font-medium hover:opacity-90 transition-opacity",
1587
- children: "Search"
1588
- }
1589
- )
1590
- ] }),
1591
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
1592
- /* @__PURE__ */ jsxRuntime.jsx(
1593
- "select",
1594
- {
1595
- "aria-label": "Sort store items",
1596
- value: sortBy,
1597
- onChange: (e) => setSortBy(e.target.value),
1598
- className: "h-10 rounded-lg border border-border bg-background px-3 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-primary/50",
1599
- children: SORT_OPTIONS.map((o) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: o.value, children: o.label }, o.value))
1600
- }
1601
- ),
1602
- /* @__PURE__ */ jsxRuntime.jsxs(
1603
- "button",
1604
- {
1605
- type: "button",
1606
- onClick: () => setFiltersOpen(!filtersOpen),
1607
- className: "lg:hidden h-10 px-4 rounded-lg border border-border bg-background text-sm text-foreground hover:bg-muted/50 transition-colors",
1608
- children: [
1609
- "Filters",
1610
- " ",
1611
- hasActiveFilters && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-primary", children: "\u2022" })
1612
- ]
1613
- }
1614
- )
1615
- ] })
1616
- ] }),
1617
- filtersOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lg:hidden mb-6 p-4 border border-border rounded-xl bg-card", children: sidebarContent }),
1618
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-8", children: [
1619
- /* @__PURE__ */ jsxRuntime.jsx("aside", { className: "hidden lg:block w-52 flex-shrink-0", children: sidebarContent }),
1620
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
1621
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-4 flex-wrap", children: [
1622
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-muted-foreground", children: loading ? "Loading\u2026" : `${result?.totalCount ?? 0} results` }),
1623
- hasActiveFilters && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-1.5", children: [
1624
- search && /* @__PURE__ */ jsxRuntime.jsx(
1625
- Chip,
1626
- {
1627
- label: `"${search}"`,
1628
- onRemove: () => {
1629
- setSearch("");
1630
- setInputValue("");
1631
- }
1632
- }
1633
- ),
1634
- type && /* @__PURE__ */ jsxRuntime.jsx(Chip, { label: type, onRemove: () => setType("") }),
1635
- pricingModel && /* @__PURE__ */ jsxRuntime.jsx(
1636
- Chip,
1637
- {
1638
- label: pricingModel,
1639
- onRemove: () => setPricingModel("")
1640
- }
1641
- ),
1642
- category && /* @__PURE__ */ jsxRuntime.jsx(Chip, { label: category, onRemove: () => setCategory("") }),
1643
- featured && /* @__PURE__ */ jsxRuntime.jsx(
1644
- Chip,
1645
- {
1646
- label: "Featured",
1647
- onRemove: () => setFeatured(false)
1648
- }
1649
- )
1650
- ] })
1651
- ] }),
1652
- loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-4", children: [...Array(12)].map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
1653
- "div",
1654
- {
1655
- className: "bg-card border border-border rounded-xl p-4 h-36 animate-pulse"
1656
- },
1657
- i
1658
- )) }) : allProducts.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "py-20 text-center", children: [
1659
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-muted-foreground mb-4", children: [
1660
- "No results found",
1661
- search ? ` for "${search}"` : "",
1662
- "."
1663
- ] }),
1664
- /* @__PURE__ */ jsxRuntime.jsx(
1665
- "button",
1666
- {
1667
- type: "button",
1668
- onClick: resetFilters,
1669
- className: "text-sm text-primary hover:underline",
1670
- children: "Clear filters"
1671
- }
1672
- )
1673
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1674
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sr-only", children: "Store items" }),
1675
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-4", children: allProducts.map((product) => /* @__PURE__ */ jsxRuntime.jsx(
1676
- ProductCard,
1677
- {
1678
- product,
1679
- onClick: () => navigate(`/store/${product.slug}`)
1680
- },
1681
- product.id
1682
- )) }),
1683
- result?.hasMore && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-8 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(
1684
- "button",
1685
- {
1686
- type: "button",
1687
- onClick: loadMore,
1688
- disabled: loadingMore,
1689
- className: "h-10 px-8 rounded-lg border border-border text-sm font-medium text-foreground hover:bg-muted/50 transition-colors disabled:opacity-50",
1690
- children: loadingMore ? "Loading\u2026" : "Load more"
1691
- }
1692
- ) })
1693
- ] })
1694
- ] })
1695
- ] })
1696
- ] })
1697
- ] });
1698
- }
1699
- function Chip({ label, onRemove }) {
1700
- return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 text-xs bg-primary/10 text-primary rounded-full px-2.5 py-0.5 font-medium", children: [
1701
- label,
1702
- /* @__PURE__ */ jsxRuntime.jsx(
1703
- "button",
1704
- {
1705
- type: "button",
1706
- onClick: onRemove,
1707
- "aria-label": `Remove ${label}`,
1708
- className: "hover:opacity-70",
1709
- children: "\xD7"
1710
- }
1711
- )
1712
- ] });
1713
- }
1714
- function detectTheme() {
1715
- if (typeof document === "undefined") return "light";
1716
- const root = document.documentElement;
1717
- return root.classList.contains("dark") || root.getAttribute("data-theme") === "dark" ? "dark" : "light";
1718
- }
1719
- function WorkflowEmbed({
1720
- appUrl,
1721
- productId,
1722
- theme,
1723
- clickToInteract = true,
1724
- hideCta = false,
1725
- className,
1726
- onError
1727
- }) {
1728
- const iframeRef = React.useRef(null);
1729
- const [status, setStatus] = React.useState("loading");
1730
- const [interactive, setInteractive] = React.useState(!clickToInteract);
1731
- const resolvedTheme = React.useMemo(() => theme ?? detectTheme(), [theme]);
1732
- const src = React.useMemo(() => {
1733
- const base = appUrl.replace(/\/+$/, "");
1734
- const params = new URLSearchParams({
1735
- productId,
1736
- theme: resolvedTheme
1737
- });
1738
- if (hideCta) params.set("cta", "false");
1739
- return `${base}/embed/workflow?${params.toString()}`;
1740
- }, [appUrl, productId, resolvedTheme, hideCta]);
1741
- React.useEffect(() => {
1742
- const handler = (event) => {
1743
- if (event.source !== iframeRef.current?.contentWindow) return;
1744
- const data = event.data;
1745
- if (data?.type === "fluidgrids:embed:loaded") setStatus("loaded");
1746
- if (data?.type === "fluidgrids:embed:error") {
1747
- setStatus("error");
1748
- onError?.(data.message ?? "Failed to load workflow preview");
1749
- }
1750
- };
1751
- window.addEventListener("message", handler);
1752
- return () => window.removeEventListener("message", handler);
1753
- }, [onError]);
1754
- if (status === "error") return null;
1755
- return /* @__PURE__ */ jsxRuntime.jsxs(
1756
- "div",
1757
- {
1758
- className: `relative w-full overflow-hidden rounded-xl border border-border bg-muted ${className ?? ""}`,
1759
- style: { aspectRatio: "16 / 9" },
1760
- children: [
1761
- status === "loading" && /* @__PURE__ */ jsxRuntime.jsx(
1762
- "div",
1763
- {
1764
- className: "absolute inset-0 animate-pulse bg-muted",
1765
- "aria-hidden": "true"
1766
- }
1767
- ),
1768
- /* @__PURE__ */ jsxRuntime.jsx(
1769
- "iframe",
1770
- {
1771
- ref: iframeRef,
1772
- src,
1773
- title: "Workflow preview",
1774
- loading: "lazy",
1775
- className: "h-full w-full border-0",
1776
- style: interactive ? void 0 : { pointerEvents: "none" }
1777
- }
1778
- ),
1779
- clickToInteract && !interactive && status === "loaded" && /* @__PURE__ */ jsxRuntime.jsx(
1780
- "button",
1781
- {
1782
- type: "button",
1783
- onClick: () => setInteractive(true),
1784
- className: "absolute inset-0 flex items-end justify-center bg-transparent pb-4",
1785
- "aria-label": "Click to explore the workflow",
1786
- children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-full border border-border bg-background/90 px-4 py-1.5 text-xs font-medium text-foreground shadow-sm backdrop-blur transition-colors hover:bg-background", children: "Click to explore" })
1787
- }
1788
- )
1789
- ]
1790
- }
1791
- );
1792
- }
1793
- function MarkdownLink({
1794
- href,
1795
- children,
1796
- // react-markdown passes the hast node to every override; it is not a DOM attribute.
1797
- node: _node,
1798
- ...props
1799
- }) {
1800
- const isExternal = typeof href === "string" && (href.startsWith("http://") || href.startsWith("https://"));
1801
- return /* @__PURE__ */ jsxRuntime.jsx(
1802
- "a",
1803
- {
1804
- href,
1805
- ...props,
1806
- ...isExternal ? { target: "_blank", rel: "noopener noreferrer" } : { rel: "noopener noreferrer" },
1807
- children
1808
- }
1809
- );
1810
- }
1811
- var defaultComponents = {
1812
- a: MarkdownLink
1813
- };
1814
- function Markdown({
1815
- children,
1816
- className,
1817
- inline,
1818
- components,
1819
- rehypePlugins
1820
- }) {
1821
- const content = /* @__PURE__ */ jsxRuntime.jsx(
1822
- ReactMarkdown__default.default,
1823
- {
1824
- remarkPlugins: [remarkGfm__default.default],
1825
- rehypePlugins: [rehypeSanitize__default.default, ...rehypePlugins ?? []],
1826
- components: { ...defaultComponents, ...components },
1827
- ...inline ? { disallowedElements: ["p"], unwrapDisallowed: true } : {},
1828
- children
1829
- }
1830
- );
1831
- if (inline) {
1832
- return /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: content });
1833
- }
1834
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children: content });
1835
- }
1836
-
1837
- // src/utils/product-slug.ts
1838
- var PRODUCT_SLUG_RENAMES = Object.freeze(
1839
- [Object.freeze({ legacy: "botlit", current: "botmanor" })]
1840
- );
1841
- Object.freeze(
1842
- Object.fromEntries(PRODUCT_SLUG_RENAMES.map((r) => [r.legacy, r.current]))
1843
- );
1844
- React.createContext({
1845
- product: null,
1846
- loading: true,
1847
- error: null,
1848
- hasProvider: false,
1849
- refetch: () => {
1850
- }
3
+ var chunkKSMBOWQQ_js = require('../chunk-KSMBOWQQ.js');
4
+ require('../chunk-3X26GE4S.js');
5
+ require('../chunk-4U6YAII5.js');
6
+ require('../chunk-3LFFY7AZ.js');
7
+ require('../chunk-GEXC4LRP.js');
8
+ require('../chunk-7A6R7CGG.js');
9
+ require('../chunk-UXWT7G77.js');
10
+ require('../chunk-UQ2HALUD.js');
11
+ require('../chunk-C6OJIHNO.js');
12
+ require('../chunk-BYSZT35R.js');
13
+ require('../chunk-E3GUGJMY.js');
14
+ require('../chunk-JXLSTVBK.js');
15
+ require('../chunk-ZSTFX7FV.js');
16
+
17
+
18
+
19
+ Object.defineProperty(exports, "ProductCard", {
20
+ enumerable: true,
21
+ get: function () { return chunkKSMBOWQQ_js.ProductCard; }
22
+ });
23
+ Object.defineProperty(exports, "StarRating", {
24
+ enumerable: true,
25
+ get: function () { return chunkKSMBOWQQ_js.StarRating; }
26
+ });
27
+ Object.defineProperty(exports, "StoreBrowsePage", {
28
+ enumerable: true,
29
+ get: function () { return chunkKSMBOWQQ_js.StoreBrowsePage; }
30
+ });
31
+ Object.defineProperty(exports, "StoreHomePage", {
32
+ enumerable: true,
33
+ get: function () { return chunkKSMBOWQQ_js.StoreHomePage; }
34
+ });
35
+ Object.defineProperty(exports, "StoreProductDetailPage", {
36
+ enumerable: true,
37
+ get: function () { return chunkKSMBOWQQ_js.StoreProductDetailPage; }
38
+ });
39
+ Object.defineProperty(exports, "TypeBadge", {
40
+ enumerable: true,
41
+ get: function () { return chunkKSMBOWQQ_js.TypeBadge; }
42
+ });
43
+ Object.defineProperty(exports, "WorkflowEmbed", {
44
+ enumerable: true,
45
+ get: function () { return chunkKSMBOWQQ_js.WorkflowEmbed; }
46
+ });
47
+ Object.defineProperty(exports, "formatDownloads", {
48
+ enumerable: true,
49
+ get: function () { return chunkKSMBOWQQ_js.formatDownloads; }
50
+ });
51
+ Object.defineProperty(exports, "formatPrice", {
52
+ enumerable: true,
53
+ get: function () { return chunkKSMBOWQQ_js.formatPrice; }
1851
54
  });
1852
- var PROSE_CSS = `
1853
- .boff-prose {
1854
- max-width: none;
1855
- }
1856
-
1857
- .boff-prose h1 {
1858
- font-size: 2.25rem;
1859
- font-weight: 800;
1860
- line-height: 1.2;
1861
- margin-top: 0;
1862
- margin-bottom: 1rem;
1863
- color: hsl(var(--foreground));
1864
- }
1865
-
1866
- .boff-prose h2 {
1867
- font-size: 1.75rem;
1868
- font-weight: 700;
1869
- line-height: 1.3;
1870
- margin-top: 2rem;
1871
- margin-bottom: 1rem;
1872
- color: hsl(var(--foreground));
1873
- }
1874
-
1875
- .boff-prose h3 {
1876
- font-size: 1.5rem;
1877
- font-weight: 600;
1878
- line-height: 1.4;
1879
- margin-top: 1.5rem;
1880
- margin-bottom: 0.75rem;
1881
- color: hsl(var(--foreground));
1882
- }
1883
-
1884
- .boff-prose h4 {
1885
- font-size: 1.25rem;
1886
- font-weight: 600;
1887
- line-height: 1.5;
1888
- margin-top: 1.25rem;
1889
- margin-bottom: 0.5rem;
1890
- color: hsl(var(--foreground));
1891
- }
1892
-
1893
- .boff-prose p {
1894
- margin-bottom: 1rem;
1895
- line-height: 1.75;
1896
- color: hsl(var(--foreground));
1897
- }
1898
-
1899
- .boff-prose ul,
1900
- .boff-prose ol {
1901
- margin-bottom: 1rem;
1902
- padding-left: 1.5rem;
1903
- }
1904
-
1905
- .boff-prose ul {
1906
- list-style-type: disc;
1907
- }
1908
-
1909
- .boff-prose ol {
1910
- list-style-type: decimal;
1911
- }
1912
-
1913
- .boff-prose li {
1914
- margin-bottom: 0.25rem;
1915
- line-height: 1.75;
1916
- color: hsl(var(--foreground));
1917
- }
1918
-
1919
- .boff-prose li > ul,
1920
- .boff-prose li > ol {
1921
- margin-top: 0.25rem;
1922
- margin-bottom: 0.25rem;
1923
- }
1924
-
1925
- .boff-prose a {
1926
- color: hsl(var(--primary));
1927
- text-decoration: underline;
1928
- text-underline-offset: 2px;
1929
- }
1930
-
1931
- .boff-prose a:hover {
1932
- opacity: 0.8;
1933
- }
1934
-
1935
- .boff-prose hr {
1936
- border: none;
1937
- height: 1px;
1938
- background-color: hsl(var(--border));
1939
- margin: 2rem 0;
1940
- }
1941
-
1942
- .boff-prose blockquote {
1943
- border-left: 4px solid hsl(var(--border));
1944
- padding-left: 1rem;
1945
- margin: 1.5rem 0;
1946
- font-style: italic;
1947
- color: hsl(var(--muted-foreground));
1948
- }
1949
-
1950
- .boff-prose strong {
1951
- font-weight: 700;
1952
- color: hsl(var(--foreground));
1953
- }
1954
-
1955
- .boff-prose code {
1956
- background-color: hsl(var(--muted));
1957
- padding: 0.125rem 0.25rem;
1958
- border-radius: 0.25rem;
1959
- font-size: 0.875em;
1960
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
1961
- }
1962
-
1963
- .boff-prose pre {
1964
- background-color: hsl(var(--muted));
1965
- padding: 1rem;
1966
- border-radius: 0.5rem;
1967
- overflow-x: auto;
1968
- margin-bottom: 1rem;
1969
- }
1970
-
1971
- .boff-prose pre code {
1972
- background-color: transparent;
1973
- padding: 0;
1974
- }
1975
-
1976
- .boff-prose img {
1977
- max-width: 100%;
1978
- height: auto;
1979
- }
1980
-
1981
- .boff-prose table {
1982
- width: 100%;
1983
- border-collapse: collapse;
1984
- margin-bottom: 1rem;
1985
- }
1986
-
1987
- .boff-prose th,
1988
- .boff-prose td {
1989
- border: 1px solid hsl(var(--border));
1990
- padding: 0.5rem;
1991
- text-align: left;
1992
- }
1993
-
1994
- .boff-prose th {
1995
- font-weight: 600;
1996
- background-color: hsl(var(--muted));
1997
- }
1998
-
1999
- /* Smaller variant used inside cards and compact panels */
2000
- .boff-prose-sm {
2001
- font-size: 0.875rem;
2002
- }
2003
-
2004
- .boff-prose-sm h1 {
2005
- font-size: 1.75rem;
2006
- }
2007
-
2008
- .boff-prose-sm h2 {
2009
- font-size: 1.5rem;
2010
- }
2011
-
2012
- .boff-prose-sm h3 {
2013
- font-size: 1.25rem;
2014
- }
2015
-
2016
- .boff-prose-sm h4 {
2017
- font-size: 1.125rem;
2018
- }
2019
-
2020
- .boff-prose-sm p,
2021
- .boff-prose-sm li {
2022
- line-height: 1.625;
2023
- }
2024
- `;
2025
- function ProseStyles() {
2026
- return /* @__PURE__ */ jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: PROSE_CSS } });
2027
- }
2028
- function cn(...inputs) {
2029
- return tailwindMerge.twMerge(clsx.clsx(inputs));
2030
- }
2031
- var buttonVariants = classVarianceAuthority.cva(
2032
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
2033
- {
2034
- variants: {
2035
- variant: {
2036
- default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
2037
- destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
2038
- outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
2039
- secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
2040
- ghost: "hover:bg-accent hover:text-accent-foreground",
2041
- link: "text-primary underline-offset-4 hover:underline"
2042
- },
2043
- size: {
2044
- default: "h-9 px-4 py-2",
2045
- sm: "h-8 rounded-md px-3 text-xs",
2046
- lg: "h-10 rounded-md px-8",
2047
- icon: "h-9 w-9"
2048
- }
2049
- },
2050
- defaultVariants: {
2051
- variant: "default",
2052
- size: "default"
2053
- }
2054
- }
2055
- );
2056
- var Button = React__namespace.forwardRef(
2057
- ({ className, variant, size, asChild = false, ...props }, ref) => {
2058
- const Comp = asChild ? reactSlot.Slot : "button";
2059
- return /* @__PURE__ */ jsxRuntime.jsx(
2060
- Comp,
2061
- {
2062
- className: cn(buttonVariants({ variant, size, className })),
2063
- ref,
2064
- ...props
2065
- }
2066
- );
2067
- }
2068
- );
2069
- Button.displayName = "Button";
2070
- var Input = React__namespace.forwardRef(
2071
- ({ className, type, ...props }, ref) => {
2072
- return /* @__PURE__ */ jsxRuntime.jsx(
2073
- "input",
2074
- {
2075
- type,
2076
- className: cn(
2077
- "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
2078
- className
2079
- ),
2080
- ref,
2081
- ...props
2082
- }
2083
- );
2084
- }
2085
- );
2086
- Input.displayName = "Input";
2087
- var ANIM_STYLES = `
2088
- @keyframes sdFadeUp {
2089
- from { opacity: 0; transform: translateY(16px); }
2090
- to { opacity: 1; transform: translateY(0); }
2091
- }
2092
- @keyframes sdFadeIn {
2093
- from { opacity: 0; }
2094
- to { opacity: 1; }
2095
- }
2096
- @keyframes sdScaleIn {
2097
- from { opacity: 0; transform: scale(0.94); }
2098
- to { opacity: 1; transform: scale(1); }
2099
- }
2100
- @keyframes sdGlowPulse {
2101
- 0%, 100% { opacity: 0.35; }
2102
- 50% { opacity: 0.65; }
2103
- }
2104
- @media (prefers-reduced-motion: reduce) {
2105
- .sd-fu, .sd-fi, .sd-si { animation: none !important; opacity: 1 !important; transform: none !important; }
2106
- .sd-gp { animation: none !important; }
2107
- }
2108
- .sd-fu { animation: sdFadeUp 0.55s cubic-bezier(0.16,1,0.3,1) both; }
2109
- .sd-fi { animation: sdFadeIn 0.45s ease-out both; }
2110
- .sd-si { animation: sdScaleIn 0.45s cubic-bezier(0.16,1,0.3,1) both; }
2111
- .sd-gp { animation: sdGlowPulse 3s ease-in-out infinite; }
2112
- .sd-d1 { animation-delay: 60ms; }
2113
- .sd-d2 { animation-delay: 120ms; }
2114
- .sd-d3 { animation-delay: 180ms; }
2115
- .sd-d4 { animation-delay: 240ms; }
2116
- .sd-d5 { animation-delay: 320ms; }
2117
- .sd-d6 { animation-delay: 400ms; }
2118
- `;
2119
- function RatingBar({
2120
- label,
2121
- count,
2122
- total
2123
- }) {
2124
- const pct = total > 0 ? Math.round(count / total * 100) : 0;
2125
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
2126
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-12 text-right shrink-0", children: label }),
2127
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 h-1.5 bg-muted rounded-full overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
2128
- "div",
2129
- {
2130
- className: "h-full bg-amber-400 rounded-full transition-all duration-700",
2131
- style: { width: `${pct}%` }
2132
- }
2133
- ) }),
2134
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-6 shrink-0", children: count })
2135
- ] });
2136
- }
2137
- function IconBlock({ src }) {
2138
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex-shrink-0", children: [
2139
- /* @__PURE__ */ jsxRuntime.jsx(
2140
- "div",
2141
- {
2142
- className: "sd-gp absolute -inset-1.5 rounded-2xl bg-primary/30 blur-md pointer-events-none",
2143
- "aria-hidden": "true"
2144
- }
2145
- ),
2146
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sd-si relative w-20 h-20 rounded-2xl overflow-hidden shadow-lg ring-1 ring-border", children: src ? /* @__PURE__ */ jsxRuntime.jsx(
2147
- "img",
2148
- {
2149
- src,
2150
- alt: "",
2151
- "aria-hidden": true,
2152
- className: "w-full h-full object-cover"
2153
- }
2154
- ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full bg-gradient-to-br from-primary/25 to-primary/8 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs(
2155
- "svg",
2156
- {
2157
- viewBox: "0 0 40 40",
2158
- className: "w-10 h-10",
2159
- fill: "none",
2160
- xmlns: "http://www.w3.org/2000/svg",
2161
- "aria-hidden": "true",
2162
- children: [
2163
- /* @__PURE__ */ jsxRuntime.jsx(
2164
- "polygon",
2165
- {
2166
- points: "20,4 34,11 20,18 6,11",
2167
- className: "fill-primary/40",
2168
- stroke: "currentColor",
2169
- strokeWidth: "0.6",
2170
- strokeOpacity: "0.4"
2171
- }
2172
- ),
2173
- /* @__PURE__ */ jsxRuntime.jsx(
2174
- "polygon",
2175
- {
2176
- points: "6,11 20,18 20,34 6,27",
2177
- className: "fill-primary/20",
2178
- stroke: "currentColor",
2179
- strokeWidth: "0.6",
2180
- strokeOpacity: "0.4"
2181
- }
2182
- ),
2183
- /* @__PURE__ */ jsxRuntime.jsx(
2184
- "polygon",
2185
- {
2186
- points: "34,11 20,18 20,34 34,27",
2187
- className: "fill-primary/30",
2188
- stroke: "currentColor",
2189
- strokeWidth: "0.6",
2190
- strokeOpacity: "0.4"
2191
- }
2192
- )
2193
- ]
2194
- }
2195
- ) }) })
2196
- ] });
2197
- }
2198
- function StoreProductDetailPage({
2199
- slug,
2200
- id,
2201
- appLoginUrl,
2202
- onNavigate,
2203
- productName,
2204
- waitlist: waitlistProp
2205
- }) {
2206
- const client = useWebSDK();
2207
- const waitlist = waitlistProp ?? false;
2208
- const [details, setDetails] = React.useState(null);
2209
- const [loading, setLoading] = React.useState(true);
2210
- const [error, setError] = React.useState(null);
2211
- const [activeScreenshot, setActiveScreenshot] = React.useState(0);
2212
- const [screenshotKey, setScreenshotKey] = React.useState(0);
2213
- const [embedFailed, setEmbedFailed] = React.useState(false);
2214
- React.useEffect(() => {
2215
- if (!slug && !id) {
2216
- setError("Product not found.");
2217
- setLoading(false);
2218
- return;
2219
- }
2220
- let cancelled = false;
2221
- setLoading(true);
2222
- client.query(STORE_PRODUCT_DETAILS_QUERY, {
2223
- input: { slug: slug ?? void 0, id: id ?? void 0 }
2224
- }).then((r) => {
2225
- if (cancelled) return;
2226
- if (r.errors?.length) throw new Error(r.errors[0].message);
2227
- if (!r.data?.storeProductDetails) throw new Error("Not found");
2228
- setDetails(r.data.storeProductDetails);
2229
- }).catch(() => {
2230
- if (!cancelled) setError("Product not found or unavailable.");
2231
- }).finally(() => {
2232
- if (!cancelled) setLoading(false);
2233
- });
2234
- return () => {
2235
- cancelled = true;
2236
- };
2237
- }, [client, slug, id]);
2238
- function navigate(path) {
2239
- if (onNavigate) {
2240
- onNavigate(path);
2241
- return;
2242
- }
2243
- window.location.href = path;
2244
- }
2245
- function switchScreenshot(i) {
2246
- setActiveScreenshot(i);
2247
- setScreenshotKey((k) => k + 1);
2248
- }
2249
- if (loading) {
2250
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12 animate-pulse", children: [
2251
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-40 mb-8" }),
2252
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-6 mb-8", children: [
2253
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-20 h-20 rounded-2xl bg-muted flex-shrink-0" }),
2254
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 space-y-3 pt-1", children: [
2255
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 bg-muted rounded w-1/3" }),
2256
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/2" }),
2257
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/4" })
2258
- ] })
2259
- ] }),
2260
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-56 bg-muted rounded-xl mb-8" }),
2261
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: [...Array(5)].map((_, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded" }, i)) })
2262
- ] });
2263
- }
2264
- if (error || !details) {
2265
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-20 text-center", children: [
2266
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground mb-4", children: error ?? "Product not found." }),
2267
- /* @__PURE__ */ jsxRuntime.jsx(
2268
- "button",
2269
- {
2270
- type: "button",
2271
- onClick: () => navigate("/store"),
2272
- className: "text-sm text-primary hover:underline",
2273
- children: "Back to Store"
2274
- }
2275
- )
2276
- ] });
2277
- }
2278
- const {
2279
- product,
2280
- publisher,
2281
- reviews,
2282
- reviewsCount,
2283
- relatedProducts,
2284
- ratingDistribution
2285
- } = details;
2286
- const priceLabel = formatPrice(
2287
- product.price,
2288
- product.currency,
2289
- product.pricingModel
2290
- );
2291
- const ctaLabel = waitlist ? "Coming Soon" : priceLabel === "Free" ? "Install Free" : `Get for ${priceLabel}`;
2292
- const ctaHref = waitlist ? "#" : appLoginUrl;
2293
- const ctaAriaDisabled = waitlist || void 0;
2294
- const screenshots = product.screenshots ?? [];
2295
- const totalRatings = ratingDistribution ? ratingDistribution.fiveStars + ratingDistribution.fourStars + ratingDistribution.threeStars + ratingDistribution.twoStars + ratingDistribution.oneStar : 0;
2296
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full", children: [
2297
- /* @__PURE__ */ jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: ANIM_STYLES } }),
2298
- /* @__PURE__ */ jsxRuntime.jsx(ProseStyles, {}),
2299
- /* @__PURE__ */ jsxRuntime.jsx(
2300
- PageHead,
2301
- {
2302
- title: `${product.name}${productName ? ` \u2013 ${productName} Store` : " \u2013 Store"}`,
2303
- description: product.description ?? void 0,
2304
- productName
2305
- }
2306
- ),
2307
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative overflow-hidden", children: [
2308
- /* @__PURE__ */ jsxRuntime.jsx(
2309
- "div",
2310
- {
2311
- className: "absolute inset-0 bg-gradient-to-b from-primary/[0.07] via-background to-background pointer-events-none",
2312
- "aria-hidden": "true"
2313
- }
2314
- ),
2315
- /* @__PURE__ */ jsxRuntime.jsxs(
2316
- "svg",
2317
- {
2318
- className: "absolute inset-0 w-full h-full opacity-[0.04] pointer-events-none",
2319
- xmlns: "http://www.w3.org/2000/svg",
2320
- "aria-hidden": "true",
2321
- children: [
2322
- /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsx(
2323
- "pattern",
2324
- {
2325
- id: "sd-grid",
2326
- width: "40",
2327
- height: "40",
2328
- patternUnits: "userSpaceOnUse",
2329
- children: /* @__PURE__ */ jsxRuntime.jsx(
2330
- "path",
2331
- {
2332
- d: "M 40 0 L 0 0 0 40",
2333
- fill: "none",
2334
- stroke: "currentColor",
2335
- strokeWidth: "1"
2336
- }
2337
- )
2338
- }
2339
- ) }),
2340
- /* @__PURE__ */ jsxRuntime.jsx("rect", { width: "100%", height: "100%", fill: "url(#sd-grid)" })
2341
- ]
2342
- }
2343
- ),
2344
- /* @__PURE__ */ jsxRuntime.jsx(
2345
- "div",
2346
- {
2347
- className: "absolute -top-12 -left-12 w-72 h-72 rounded-full bg-primary/10 blur-3xl pointer-events-none",
2348
- "aria-hidden": "true"
2349
- }
2350
- ),
2351
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pt-6 pb-8", children: [
2352
- /* @__PURE__ */ jsxRuntime.jsxs("nav", { className: "sd-fi text-sm text-muted-foreground mb-6 flex items-center gap-1.5 flex-wrap", children: [
2353
- /* @__PURE__ */ jsxRuntime.jsx(
2354
- "button",
2355
- {
2356
- type: "button",
2357
- onClick: () => navigate("/store"),
2358
- className: "hover:text-primary transition-colors",
2359
- children: "Store"
2360
- }
2361
- ),
2362
- /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "\u203A" }),
2363
- product.category && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2364
- /* @__PURE__ */ jsxRuntime.jsx(
2365
- "button",
2366
- {
2367
- type: "button",
2368
- onClick: () => navigate(`/store/browse?category=${product.category}`),
2369
- className: "hover:text-primary transition-colors",
2370
- children: product.category
2371
- }
2372
- ),
2373
- /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "\u203A" })
2374
- ] }),
2375
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-foreground font-medium", children: product.name })
2376
- ] }),
2377
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-5 items-start", children: [
2378
- /* @__PURE__ */ jsxRuntime.jsx(IconBlock, { src: product.icon, name: product.name }),
2379
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
2380
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sd-fu flex items-start gap-2 flex-wrap mb-1", children: [
2381
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl sm:text-3xl font-bold text-foreground leading-tight", children: product.name }),
2382
- /* @__PURE__ */ jsxRuntime.jsx(TypeBadge, { type: product.type })
2383
- ] }),
2384
- product.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "sd-fu sd-d1 text-muted-foreground text-sm mb-2 leading-relaxed", children: product.description }),
2385
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sd-fu sd-d2 flex items-center gap-3 text-sm text-muted-foreground flex-wrap", children: [
2386
- product.rating != null && product.rating > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-1", children: [
2387
- /* @__PURE__ */ jsxRuntime.jsx(StarRating, { rating: product.rating }),
2388
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-foreground", children: product.rating.toFixed(1) }),
2389
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
2390
- "(",
2391
- reviewsCount ?? 0,
2392
- ")"
2393
- ] })
2394
- ] }),
2395
- product.downloads != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
2396
- formatDownloads(product.downloads),
2397
- " downloads"
2398
- ] }),
2399
- product.authorName && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
2400
- "by ",
2401
- product.authorName
2402
- ] }),
2403
- product.license && /* @__PURE__ */ jsxRuntime.jsx("span", { children: product.license })
2404
- ] })
2405
- ] })
2406
- ] })
2407
- ] })
2408
- ] }),
2409
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid lg:grid-cols-[1fr_300px] gap-8", children: [
2410
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2411
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sd-fu sd-d3 lg:hidden mb-6 p-4 rounded-xl border border-border bg-card flex items-center gap-4", children: [
2412
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2413
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-2xl font-bold text-foreground", children: priceLabel }),
2414
- product.pricingModel === "SUBSCRIPTION" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-muted-foreground", children: "per month" })
2415
- ] }),
2416
- /* @__PURE__ */ jsxRuntime.jsx(
2417
- "a",
2418
- {
2419
- href: ctaHref,
2420
- target: waitlist ? void 0 : "_blank",
2421
- rel: waitlist ? void 0 : "noopener noreferrer",
2422
- "aria-disabled": ctaAriaDisabled,
2423
- className: "flex-1 h-11 rounded-lg bg-primary text-primary-foreground text-sm font-semibold flex items-center justify-center hover:opacity-90 transition-opacity aria-disabled:opacity-60 aria-disabled:cursor-not-allowed",
2424
- children: ctaLabel
2425
- }
2426
- )
2427
- ] }),
2428
- product.type === "WORKFLOW" && !embedFailed && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sd-fu sd-d3 mb-8", children: /* @__PURE__ */ jsxRuntime.jsx(
2429
- WorkflowEmbed,
2430
- {
2431
- appUrl: appLoginUrl,
2432
- productId: product.id,
2433
- onError: () => setEmbedFailed(true)
2434
- }
2435
- ) }),
2436
- screenshots.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sd-fu sd-d3 mb-8", children: [
2437
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl overflow-hidden bg-muted aspect-video mb-3 shadow-md", children: /* @__PURE__ */ jsxRuntime.jsx(
2438
- "img",
2439
- {
2440
- src: screenshots[activeScreenshot],
2441
- alt: `Screenshot ${activeScreenshot + 1}`,
2442
- className: "sd-fi w-full h-full object-cover"
2443
- },
2444
- screenshotKey
2445
- ) }),
2446
- screenshots.length > 1 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-2 overflow-x-auto pb-1", children: screenshots.map((src, i) => /* @__PURE__ */ jsxRuntime.jsx(
2447
- "button",
2448
- {
2449
- type: "button",
2450
- onClick: () => switchScreenshot(i),
2451
- className: `w-20 h-14 rounded-lg overflow-hidden flex-shrink-0 border-2 transition-all duration-200 ${i === activeScreenshot ? "border-primary shadow-sm shadow-primary/20" : "border-transparent opacity-50 hover:opacity-80"}`,
2452
- children: /* @__PURE__ */ jsxRuntime.jsx(
2453
- "img",
2454
- {
2455
- src,
2456
- alt: `Thumbnail ${i + 1}`,
2457
- className: "w-full h-full object-cover"
2458
- }
2459
- )
2460
- },
2461
- i
2462
- )) })
2463
- ] }),
2464
- product.longDescription && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sd-fu sd-d4 mb-8", children: [
2465
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-bold text-foreground mb-3", children: "About" }),
2466
- /* @__PURE__ */ jsxRuntime.jsx(Markdown, { className: "boff-prose boff-prose-sm max-w-none text-muted-foreground leading-relaxed", children: product.longDescription })
2467
- ] }),
2468
- publisher && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sd-fu sd-d5 mb-8 p-4 rounded-xl border border-border bg-card hover:border-primary/30 transition-colors duration-300", children: [
2469
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm font-semibold text-foreground mb-3", children: "Publisher" }),
2470
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
2471
- publisher.logoUrl ? /* @__PURE__ */ jsxRuntime.jsx(
2472
- "img",
2473
- {
2474
- src: publisher.logoUrl,
2475
- alt: publisher.name,
2476
- className: "w-10 h-10 rounded-full object-cover"
2477
- }
2478
- ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center text-primary text-sm font-bold", children: publisher.name.charAt(0).toUpperCase() }),
2479
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2480
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
2481
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-foreground text-sm", children: publisher.name }),
2482
- publisher.isVerified && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-primary font-medium", children: "\u2713 Verified" })
2483
- ] }),
2484
- publisher.bio && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground mt-0.5 line-clamp-2", children: publisher.bio })
2485
- ] })
2486
- ] })
2487
- ] }),
2488
- (reviews?.length ?? 0) > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sd-fu sd-d5 mb-8", children: [
2489
- /* @__PURE__ */ jsxRuntime.jsxs("h2", { className: "text-lg font-bold text-foreground mb-4", children: [
2490
- "Reviews (",
2491
- reviewsCount ?? reviews.length,
2492
- ")"
2493
- ] }),
2494
- ratingDistribution && totalRatings > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sd-si flex gap-6 mb-5 p-4 rounded-xl border border-border bg-card", children: [
2495
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
2496
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-4xl font-bold text-foreground tabular-nums", children: product.rating?.toFixed(1) ?? "\u2014" }),
2497
- product.rating != null && /* @__PURE__ */ jsxRuntime.jsx(StarRating, { rating: product.rating }),
2498
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-muted-foreground mt-1", children: [
2499
- totalRatings,
2500
- " ratings"
2501
- ] })
2502
- ] }),
2503
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 space-y-1.5 pt-1", children: [
2504
- /* @__PURE__ */ jsxRuntime.jsx(
2505
- RatingBar,
2506
- {
2507
- label: "5 \u2605",
2508
- count: ratingDistribution.fiveStars,
2509
- total: totalRatings
2510
- }
2511
- ),
2512
- /* @__PURE__ */ jsxRuntime.jsx(
2513
- RatingBar,
2514
- {
2515
- label: "4 \u2605",
2516
- count: ratingDistribution.fourStars,
2517
- total: totalRatings
2518
- }
2519
- ),
2520
- /* @__PURE__ */ jsxRuntime.jsx(
2521
- RatingBar,
2522
- {
2523
- label: "3 \u2605",
2524
- count: ratingDistribution.threeStars,
2525
- total: totalRatings
2526
- }
2527
- ),
2528
- /* @__PURE__ */ jsxRuntime.jsx(
2529
- RatingBar,
2530
- {
2531
- label: "2 \u2605",
2532
- count: ratingDistribution.twoStars,
2533
- total: totalRatings
2534
- }
2535
- ),
2536
- /* @__PURE__ */ jsxRuntime.jsx(
2537
- RatingBar,
2538
- {
2539
- label: "1 \u2605",
2540
- count: ratingDistribution.oneStar,
2541
- total: totalRatings
2542
- }
2543
- )
2544
- ] })
2545
- ] }),
2546
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: reviews.slice(0, 5).map((review, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
2547
- "div",
2548
- {
2549
- className: "sd-fu p-4 rounded-xl border border-border bg-card hover:border-primary/20 transition-colors duration-200",
2550
- style: { animationDelay: `${(idx + 5) * 60}ms` },
2551
- children: [
2552
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 mb-2", children: [
2553
- /* @__PURE__ */ jsxRuntime.jsx(StarRating, { rating: review.rating }),
2554
- review.verifiedPurchase && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-emerald-700 dark:text-emerald-400 font-medium", children: "Verified" })
2555
- ] }),
2556
- review.title && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-sm text-foreground mb-1", children: review.title }),
2557
- review.comment && /* @__PURE__ */ jsxRuntime.jsx(Markdown, { className: "text-sm text-muted-foreground leading-relaxed", children: review.comment }),
2558
- review.createdAt && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground mt-2", children: new Date(review.createdAt).toLocaleDateString(
2559
- "en-US",
2560
- {
2561
- year: "numeric",
2562
- month: "short",
2563
- day: "numeric"
2564
- }
2565
- ) })
2566
- ]
2567
- },
2568
- review.id
2569
- )) })
2570
- ] }),
2571
- (relatedProducts?.length ?? 0) > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sd-fu sd-d6", children: [
2572
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-bold text-foreground mb-4", children: "Related" }),
2573
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4", children: relatedProducts.slice(0, 6).map((p) => /* @__PURE__ */ jsxRuntime.jsx(
2574
- ProductCard,
2575
- {
2576
- product: p,
2577
- onClick: () => navigate(`/store/${p.slug}`)
2578
- },
2579
- p.id
2580
- )) })
2581
- ] })
2582
- ] }),
2583
- /* @__PURE__ */ jsxRuntime.jsx("aside", { className: "lg:sticky lg:top-24 self-start", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sd-fu sd-d4 bg-card border border-border rounded-xl p-5 space-y-4 hover:border-primary/30 transition-colors duration-300 hover:shadow-lg hover:shadow-primary/5", children: [
2584
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2585
- /* @__PURE__ */ jsxRuntime.jsx(
2586
- "div",
2587
- {
2588
- className: `text-3xl font-extrabold ${priceLabel === "Free" ? "text-emerald-700 dark:text-emerald-400" : "text-foreground"}`,
2589
- children: priceLabel
2590
- }
2591
- ),
2592
- product.pricingModel === "SUBSCRIPTION" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-muted-foreground", children: "per month" })
2593
- ] }),
2594
- /* @__PURE__ */ jsxRuntime.jsx(
2595
- "a",
2596
- {
2597
- href: ctaHref,
2598
- target: waitlist ? void 0 : "_blank",
2599
- rel: waitlist ? void 0 : "noopener noreferrer",
2600
- "aria-disabled": ctaAriaDisabled,
2601
- className: "flex items-center justify-center w-full h-11 rounded-lg bg-primary text-primary-foreground text-sm font-semibold hover:opacity-90 active:scale-[0.98] transition-all duration-150 aria-disabled:opacity-60 aria-disabled:cursor-not-allowed",
2602
- children: ctaLabel
2603
- }
2604
- ),
2605
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground text-center", children: waitlist ? `Available when ${product?.name ?? "this product"} launches` : "Login required to install" }),
2606
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-border pt-4 space-y-2 text-xs text-muted-foreground", children: [
2607
- product.type && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between", children: [
2608
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Type" }),
2609
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-foreground font-medium", children: product.type })
2610
- ] }),
2611
- product.category && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between", children: [
2612
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Category" }),
2613
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-foreground font-medium", children: product.category })
2614
- ] }),
2615
- product.license && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between", children: [
2616
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "License" }),
2617
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-foreground font-medium", children: product.license })
2618
- ] }),
2619
- product.minPlatformVersion && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between", children: [
2620
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Requires" }),
2621
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-foreground font-medium", children: [
2622
- "v",
2623
- product.minPlatformVersion,
2624
- "+"
2625
- ] })
2626
- ] }),
2627
- product.downloads != null && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between", children: [
2628
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Downloads" }),
2629
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-foreground font-medium", children: formatDownloads(product.downloads) })
2630
- ] })
2631
- ] }),
2632
- (product.tags?.length ?? 0) > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1.5 pt-1", children: product.tags.map((tag) => /* @__PURE__ */ jsxRuntime.jsx(
2633
- "span",
2634
- {
2635
- className: "text-xs px-2 py-0.5 rounded-full bg-primary/10 text-primary font-medium",
2636
- children: tag
2637
- },
2638
- tag
2639
- )) })
2640
- ] }) })
2641
- ] }) })
2642
- ] });
2643
- }
2644
-
2645
- exports.ProductCard = ProductCard;
2646
- exports.StarRating = StarRating;
2647
- exports.StoreBrowsePage = StoreBrowsePage;
2648
- exports.StoreHomePage = StoreHomePage;
2649
- exports.StoreProductDetailPage = StoreProductDetailPage;
2650
- exports.TypeBadge = TypeBadge;
2651
- exports.WorkflowEmbed = WorkflowEmbed;
2652
- exports.formatDownloads = formatDownloads;
2653
- exports.formatPrice = formatPrice;
2654
55
  //# sourceMappingURL=index.js.map
2655
56
  //# sourceMappingURL=index.js.map