@whop/elements-react-native 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (339) hide show
  1. package/README.md +179 -0
  2. package/android/build.gradle +64 -0
  3. package/android/consumer-rules.pro +3 -0
  4. package/android/gradle.properties +5 -0
  5. package/android/src/main/java/com/whop/elements/WhopAddressSuggestionsModule.kt +102 -0
  6. package/android/src/main/java/com/whop/elements/WhopAuthSessionModule.kt +126 -0
  7. package/android/src/main/java/com/whop/elements/WhopElementsPackage.kt +39 -0
  8. package/android/src/main/java/com/whop/elements/WhopPayButtonManager.kt +74 -0
  9. package/android/src/main/java/com/whop/elements/WhopPayButtonPressEvent.kt +17 -0
  10. package/android/src/main/java/com/whop/elements/WhopPayButtonView.kt +178 -0
  11. package/android/src/main/java/com/whop/elements/WhopPlatformPayModule.kt +212 -0
  12. package/assets/whop-wordmark-dark.png +0 -0
  13. package/assets/whop-wordmark-dark@2x.png +0 -0
  14. package/assets/whop-wordmark-dark@3x.png +0 -0
  15. package/assets/whop-wordmark-light.png +0 -0
  16. package/assets/whop-wordmark-light@2x.png +0 -0
  17. package/assets/whop-wordmark-light@3x.png +0 -0
  18. package/ios/Package.swift +59 -0
  19. package/ios/Sources/ElementsReactNative/WhopAddressSuggestionsImpl.swift +189 -0
  20. package/ios/Sources/ElementsReactNative/WhopAuthSessionImpl.swift +106 -0
  21. package/ios/Sources/ElementsReactNative/WhopPayButtonImpl.swift +89 -0
  22. package/ios/Sources/ElementsReactNative/WhopPlatformPayImpl.swift +181 -0
  23. package/ios/generated/WhopAddressSuggestionsModule.h +11 -0
  24. package/ios/generated/WhopAddressSuggestionsModule.mm +46 -0
  25. package/ios/generated/WhopAuthSessionModule.h +11 -0
  26. package/ios/generated/WhopAuthSessionModule.mm +45 -0
  27. package/ios/generated/WhopElementsShims.h +35 -0
  28. package/ios/generated/WhopPayButtonComponentView.h +12 -0
  29. package/ios/generated/WhopPayButtonComponentView.mm +82 -0
  30. package/ios/generated/WhopPlatformPayModule.h +11 -0
  31. package/ios/generated/WhopPlatformPayModule.mm +51 -0
  32. package/lib/module/api/client.js +139 -0
  33. package/lib/module/api/client.js.map +1 -0
  34. package/lib/module/api/client.test.js +137 -0
  35. package/lib/module/api/client.test.js.map +1 -0
  36. package/lib/module/api/types.js +2 -0
  37. package/lib/module/api/types.js.map +1 -0
  38. package/lib/module/controller/actions.js +148 -0
  39. package/lib/module/controller/actions.js.map +1 -0
  40. package/lib/module/controller/actions.test.js +102 -0
  41. package/lib/module/controller/actions.test.js.map +1 -0
  42. package/lib/module/controller/store.js +158 -0
  43. package/lib/module/controller/store.js.map +1 -0
  44. package/lib/module/core/address/country-data.js +1370 -0
  45. package/lib/module/core/address/country-data.js.map +1 -0
  46. package/lib/module/core/address/country-name.js +38 -0
  47. package/lib/module/core/address/country-name.js.map +1 -0
  48. package/lib/module/core/address/country-names.generated.js +249 -0
  49. package/lib/module/core/address/country-names.generated.js.map +1 -0
  50. package/lib/module/core/address/engine.js +245 -0
  51. package/lib/module/core/address/engine.js.map +1 -0
  52. package/lib/module/core/address/engine.test.js +216 -0
  53. package/lib/module/core/address/engine.test.js.map +1 -0
  54. package/lib/module/core/address/form-state.js +409 -0
  55. package/lib/module/core/address/form-state.js.map +1 -0
  56. package/lib/module/core/address/form-state.test.js +516 -0
  57. package/lib/module/core/address/form-state.test.js.map +1 -0
  58. package/lib/module/core/address/places.js +36 -0
  59. package/lib/module/core/address/places.js.map +1 -0
  60. package/lib/module/core/address/required-coverage.test.js +57 -0
  61. package/lib/module/core/address/required-coverage.test.js.map +1 -0
  62. package/lib/module/core/address/suggest.js +102 -0
  63. package/lib/module/core/address/suggest.js.map +1 -0
  64. package/lib/module/core/address/suggest.test.js +156 -0
  65. package/lib/module/core/address/suggest.test.js.map +1 -0
  66. package/lib/module/core/address/use-suggestions.js +33 -0
  67. package/lib/module/core/address/use-suggestions.js.map +1 -0
  68. package/lib/module/core/payments/address-change.js +91 -0
  69. package/lib/module/core/payments/address-change.js.map +1 -0
  70. package/lib/module/core/payments/billing-merge.js +62 -0
  71. package/lib/module/core/payments/billing-merge.js.map +1 -0
  72. package/lib/module/core/payments/browser-info.js +42 -0
  73. package/lib/module/core/payments/browser-info.js.map +1 -0
  74. package/lib/module/core/payments/bt.js +151 -0
  75. package/lib/module/core/payments/bt.js.map +1 -0
  76. package/lib/module/core/payments/bt.test.js +23 -0
  77. package/lib/module/core/payments/bt.test.js.map +1 -0
  78. package/lib/module/core/payments/currency.js +37 -0
  79. package/lib/module/core/payments/currency.js.map +1 -0
  80. package/lib/module/core/payments/detail.js +159 -0
  81. package/lib/module/core/payments/detail.js.map +1 -0
  82. package/lib/module/core/payments/detail.test.js +225 -0
  83. package/lib/module/core/payments/detail.test.js.map +1 -0
  84. package/lib/module/core/payments/errors.js +115 -0
  85. package/lib/module/core/payments/errors.js.map +1 -0
  86. package/lib/module/core/payments/mint.js +89 -0
  87. package/lib/module/core/payments/mint.js.map +1 -0
  88. package/lib/module/core/payments/plan.js +131 -0
  89. package/lib/module/core/payments/plan.js.map +1 -0
  90. package/lib/module/core/payments/refusals.js +17 -0
  91. package/lib/module/core/payments/refusals.js.map +1 -0
  92. package/lib/module/core/payments/secure-fields.js +87 -0
  93. package/lib/module/core/payments/secure-fields.js.map +1 -0
  94. package/lib/module/core/payments/tax-id-catalog.js +616 -0
  95. package/lib/module/core/payments/tax-id-catalog.js.map +1 -0
  96. package/lib/module/core/payments/wallet-contact.js +42 -0
  97. package/lib/module/core/payments/wallet-contact.js.map +1 -0
  98. package/lib/module/core/payments/wallet-contact.test.js +77 -0
  99. package/lib/module/core/payments/wallet-contact.test.js.map +1 -0
  100. package/lib/module/elements/address.js +467 -0
  101. package/lib/module/elements/address.js.map +1 -0
  102. package/lib/module/elements/branding.js +114 -0
  103. package/lib/module/elements/branding.js.map +1 -0
  104. package/lib/module/elements/card-fields/context.js +254 -0
  105. package/lib/module/elements/card-fields/context.js.map +1 -0
  106. package/lib/module/elements/card-fields/fields.js +309 -0
  107. package/lib/module/elements/card-fields/fields.js.map +1 -0
  108. package/lib/module/elements/card-fields/index.js +30 -0
  109. package/lib/module/elements/card-fields/index.js.map +1 -0
  110. package/lib/module/elements/card.js +107 -0
  111. package/lib/module/elements/card.js.map +1 -0
  112. package/lib/module/elements/email.js +84 -0
  113. package/lib/module/elements/email.js.map +1 -0
  114. package/lib/module/elements/payment/declared-fields.js +186 -0
  115. package/lib/module/elements/payment/declared-fields.js.map +1 -0
  116. package/lib/module/elements/payment/index.js +448 -0
  117. package/lib/module/elements/payment/index.js.map +1 -0
  118. package/lib/module/elements/payment/wallet.js +132 -0
  119. package/lib/module/elements/payment/wallet.js.map +1 -0
  120. package/lib/module/elements/tax-id.js +100 -0
  121. package/lib/module/elements/tax-id.js.map +1 -0
  122. package/lib/module/i18n/catalogs.js +103 -0
  123. package/lib/module/i18n/catalogs.js.map +1 -0
  124. package/lib/module/i18n/index.js +45 -0
  125. package/lib/module/i18n/index.js.map +1 -0
  126. package/lib/module/index.js +27 -0
  127. package/lib/module/index.js.map +1 -0
  128. package/lib/module/namespace.js +174 -0
  129. package/lib/module/namespace.js.map +1 -0
  130. package/lib/module/native/index.js +128 -0
  131. package/lib/module/native/index.js.map +1 -0
  132. package/lib/module/package.json +1 -0
  133. package/lib/module/provider.js +56 -0
  134. package/lib/module/provider.js.map +1 -0
  135. package/lib/module/specs/NativeWhopAddressSuggestions.js +6 -0
  136. package/lib/module/specs/NativeWhopAddressSuggestions.js.map +1 -0
  137. package/lib/module/specs/NativeWhopAuthSession.js +10 -0
  138. package/lib/module/specs/NativeWhopAuthSession.js.map +1 -0
  139. package/lib/module/specs/NativeWhopPlatformPay.js +10 -0
  140. package/lib/module/specs/NativeWhopPlatformPay.js.map +1 -0
  141. package/lib/module/specs/WhopPayButtonNativeComponent.ts +22 -0
  142. package/lib/module/theme/appearance.js +85 -0
  143. package/lib/module/theme/appearance.js.map +1 -0
  144. package/lib/module/theme/appearance.test.js +124 -0
  145. package/lib/module/theme/appearance.test.js.map +1 -0
  146. package/lib/module/theme/provider.js +25 -0
  147. package/lib/module/theme/provider.js.map +1 -0
  148. package/lib/module/theme/resolve.js +69 -0
  149. package/lib/module/theme/resolve.js.map +1 -0
  150. package/lib/module/theme/scales.generated.js +419 -0
  151. package/lib/module/theme/scales.generated.js.map +1 -0
  152. package/lib/module/ui/element-frame.js +83 -0
  153. package/lib/module/ui/element-frame.js.map +1 -0
  154. package/lib/module/ui/payment-method-icon.js +69 -0
  155. package/lib/module/ui/payment-method-icon.js.map +1 -0
  156. package/lib/module/ui/primitives.js +266 -0
  157. package/lib/module/ui/primitives.js.map +1 -0
  158. package/lib/module/ui/select.js +281 -0
  159. package/lib/module/ui/select.js.map +1 -0
  160. package/lib/module/ui/suggestions.js +71 -0
  161. package/lib/module/ui/suggestions.js.map +1 -0
  162. package/lib/module/ui/whop-logo.js +42 -0
  163. package/lib/module/ui/whop-logo.js.map +1 -0
  164. package/lib/typescript/package.json +1 -0
  165. package/lib/typescript/src/api/client.d.ts +87 -0
  166. package/lib/typescript/src/api/client.d.ts.map +1 -0
  167. package/lib/typescript/src/api/types.d.ts +175 -0
  168. package/lib/typescript/src/api/types.d.ts.map +1 -0
  169. package/lib/typescript/src/controller/actions.d.ts +42 -0
  170. package/lib/typescript/src/controller/actions.d.ts.map +1 -0
  171. package/lib/typescript/src/controller/store.d.ts +90 -0
  172. package/lib/typescript/src/controller/store.d.ts.map +1 -0
  173. package/lib/typescript/src/core/address/country-data.d.ts +4 -0
  174. package/lib/typescript/src/core/address/country-data.d.ts.map +1 -0
  175. package/lib/typescript/src/core/address/country-name.d.ts +2 -0
  176. package/lib/typescript/src/core/address/country-name.d.ts.map +1 -0
  177. package/lib/typescript/src/core/address/country-names.generated.d.ts +2 -0
  178. package/lib/typescript/src/core/address/country-names.generated.d.ts.map +1 -0
  179. package/lib/typescript/src/core/address/engine.d.ts +97 -0
  180. package/lib/typescript/src/core/address/engine.d.ts.map +1 -0
  181. package/lib/typescript/src/core/address/form-state.d.ts +144 -0
  182. package/lib/typescript/src/core/address/form-state.d.ts.map +1 -0
  183. package/lib/typescript/src/core/address/places.d.ts +23 -0
  184. package/lib/typescript/src/core/address/places.d.ts.map +1 -0
  185. package/lib/typescript/src/core/address/suggest.d.ts +47 -0
  186. package/lib/typescript/src/core/address/suggest.d.ts.map +1 -0
  187. package/lib/typescript/src/core/address/use-suggestions.d.ts +3 -0
  188. package/lib/typescript/src/core/address/use-suggestions.d.ts.map +1 -0
  189. package/lib/typescript/src/core/payments/address-change.d.ts +52 -0
  190. package/lib/typescript/src/core/payments/address-change.d.ts.map +1 -0
  191. package/lib/typescript/src/core/payments/billing-merge.d.ts +39 -0
  192. package/lib/typescript/src/core/payments/billing-merge.d.ts.map +1 -0
  193. package/lib/typescript/src/core/payments/browser-info.d.ts +12 -0
  194. package/lib/typescript/src/core/payments/browser-info.d.ts.map +1 -0
  195. package/lib/typescript/src/core/payments/bt.d.ts +55 -0
  196. package/lib/typescript/src/core/payments/bt.d.ts.map +1 -0
  197. package/lib/typescript/src/core/payments/currency.d.ts +7 -0
  198. package/lib/typescript/src/core/payments/currency.d.ts.map +1 -0
  199. package/lib/typescript/src/core/payments/detail.d.ts +53 -0
  200. package/lib/typescript/src/core/payments/detail.d.ts.map +1 -0
  201. package/lib/typescript/src/core/payments/errors.d.ts +62 -0
  202. package/lib/typescript/src/core/payments/errors.d.ts.map +1 -0
  203. package/lib/typescript/src/core/payments/mint.d.ts +74 -0
  204. package/lib/typescript/src/core/payments/mint.d.ts.map +1 -0
  205. package/lib/typescript/src/core/payments/plan.d.ts +69 -0
  206. package/lib/typescript/src/core/payments/plan.d.ts.map +1 -0
  207. package/lib/typescript/src/core/payments/refusals.d.ts +12 -0
  208. package/lib/typescript/src/core/payments/refusals.d.ts.map +1 -0
  209. package/lib/typescript/src/core/payments/secure-fields.d.ts +33 -0
  210. package/lib/typescript/src/core/payments/secure-fields.d.ts.map +1 -0
  211. package/lib/typescript/src/core/payments/tax-id-catalog.d.ts +481 -0
  212. package/lib/typescript/src/core/payments/tax-id-catalog.d.ts.map +1 -0
  213. package/lib/typescript/src/core/payments/wallet-contact.d.ts +40 -0
  214. package/lib/typescript/src/core/payments/wallet-contact.d.ts.map +1 -0
  215. package/lib/typescript/src/elements/address.d.ts +55 -0
  216. package/lib/typescript/src/elements/address.d.ts.map +1 -0
  217. package/lib/typescript/src/elements/branding.d.ts +18 -0
  218. package/lib/typescript/src/elements/branding.d.ts.map +1 -0
  219. package/lib/typescript/src/elements/card-fields/context.d.ts +67 -0
  220. package/lib/typescript/src/elements/card-fields/context.d.ts.map +1 -0
  221. package/lib/typescript/src/elements/card-fields/fields.d.ts +15 -0
  222. package/lib/typescript/src/elements/card-fields/fields.d.ts.map +1 -0
  223. package/lib/typescript/src/elements/card-fields/index.d.ts +21 -0
  224. package/lib/typescript/src/elements/card-fields/index.d.ts.map +1 -0
  225. package/lib/typescript/src/elements/card.d.ts +21 -0
  226. package/lib/typescript/src/elements/card.d.ts.map +1 -0
  227. package/lib/typescript/src/elements/email.d.ts +31 -0
  228. package/lib/typescript/src/elements/email.d.ts.map +1 -0
  229. package/lib/typescript/src/elements/payment/declared-fields.d.ts +43 -0
  230. package/lib/typescript/src/elements/payment/declared-fields.d.ts.map +1 -0
  231. package/lib/typescript/src/elements/payment/index.d.ts +37 -0
  232. package/lib/typescript/src/elements/payment/index.d.ts.map +1 -0
  233. package/lib/typescript/src/elements/payment/wallet.d.ts +39 -0
  234. package/lib/typescript/src/elements/payment/wallet.d.ts.map +1 -0
  235. package/lib/typescript/src/elements/tax-id.d.ts +41 -0
  236. package/lib/typescript/src/elements/tax-id.d.ts.map +1 -0
  237. package/lib/typescript/src/i18n/catalogs.d.ts +7 -0
  238. package/lib/typescript/src/i18n/catalogs.d.ts.map +1 -0
  239. package/lib/typescript/src/i18n/index.d.ts +17 -0
  240. package/lib/typescript/src/i18n/index.d.ts.map +1 -0
  241. package/lib/typescript/src/index.d.ts +42 -0
  242. package/lib/typescript/src/index.d.ts.map +1 -0
  243. package/lib/typescript/src/namespace.d.ts +36 -0
  244. package/lib/typescript/src/namespace.d.ts.map +1 -0
  245. package/lib/typescript/src/native/index.d.ts +62 -0
  246. package/lib/typescript/src/native/index.d.ts.map +1 -0
  247. package/lib/typescript/src/provider.d.ts +37 -0
  248. package/lib/typescript/src/provider.d.ts.map +1 -0
  249. package/lib/typescript/src/specs/NativeWhopAddressSuggestions.d.ts +13 -0
  250. package/lib/typescript/src/specs/NativeWhopAddressSuggestions.d.ts.map +1 -0
  251. package/lib/typescript/src/specs/NativeWhopAuthSession.d.ts +20 -0
  252. package/lib/typescript/src/specs/NativeWhopAuthSession.d.ts.map +1 -0
  253. package/lib/typescript/src/specs/NativeWhopPlatformPay.d.ts +24 -0
  254. package/lib/typescript/src/specs/NativeWhopPlatformPay.d.ts.map +1 -0
  255. package/lib/typescript/src/specs/WhopPayButtonNativeComponent.d.ts +21 -0
  256. package/lib/typescript/src/specs/WhopPayButtonNativeComponent.d.ts.map +1 -0
  257. package/lib/typescript/src/theme/appearance.d.ts +71 -0
  258. package/lib/typescript/src/theme/appearance.d.ts.map +1 -0
  259. package/lib/typescript/src/theme/provider.d.ts +11 -0
  260. package/lib/typescript/src/theme/provider.d.ts.map +1 -0
  261. package/lib/typescript/src/theme/resolve.d.ts +19 -0
  262. package/lib/typescript/src/theme/resolve.d.ts.map +1 -0
  263. package/lib/typescript/src/theme/scales.generated.d.ts +18 -0
  264. package/lib/typescript/src/theme/scales.generated.d.ts.map +1 -0
  265. package/lib/typescript/src/ui/element-frame.d.ts +32 -0
  266. package/lib/typescript/src/ui/element-frame.d.ts.map +1 -0
  267. package/lib/typescript/src/ui/payment-method-icon.d.ts +21 -0
  268. package/lib/typescript/src/ui/payment-method-icon.d.ts.map +1 -0
  269. package/lib/typescript/src/ui/primitives.d.ts +94 -0
  270. package/lib/typescript/src/ui/primitives.d.ts.map +1 -0
  271. package/lib/typescript/src/ui/select.d.ts +25 -0
  272. package/lib/typescript/src/ui/select.d.ts.map +1 -0
  273. package/lib/typescript/src/ui/suggestions.d.ts +15 -0
  274. package/lib/typescript/src/ui/suggestions.d.ts.map +1 -0
  275. package/lib/typescript/src/ui/whop-logo.d.ts +4 -0
  276. package/lib/typescript/src/ui/whop-logo.d.ts.map +1 -0
  277. package/package.json +121 -0
  278. package/scripts/check-swift.sh +27 -0
  279. package/scripts/codegen-shims.mjs +409 -0
  280. package/scripts/gen-country-names.mjs +58 -0
  281. package/scripts/gen-theme.mjs +124 -0
  282. package/scripts/gen-wordmark.mjs +68 -0
  283. package/scripts/mirror.mjs +112 -0
  284. package/src/api/client.ts +202 -0
  285. package/src/api/types.ts +166 -0
  286. package/src/controller/actions.ts +197 -0
  287. package/src/controller/store.ts +201 -0
  288. package/src/core/address/country-data.ts +927 -0
  289. package/src/core/address/country-name.ts +36 -0
  290. package/src/core/address/country-names.generated.ts +246 -0
  291. package/src/core/address/engine.ts +349 -0
  292. package/src/core/address/form-state.ts +479 -0
  293. package/src/core/address/places.ts +39 -0
  294. package/src/core/address/suggest.ts +153 -0
  295. package/src/core/address/use-suggestions.ts +37 -0
  296. package/src/core/payments/address-change.ts +121 -0
  297. package/src/core/payments/billing-merge.ts +91 -0
  298. package/src/core/payments/browser-info.ts +43 -0
  299. package/src/core/payments/bt.ts +176 -0
  300. package/src/core/payments/currency.ts +54 -0
  301. package/src/core/payments/detail.ts +204 -0
  302. package/src/core/payments/errors.ts +171 -0
  303. package/src/core/payments/mint.ts +130 -0
  304. package/src/core/payments/plan.ts +198 -0
  305. package/src/core/payments/refusals.ts +24 -0
  306. package/src/core/payments/secure-fields.ts +85 -0
  307. package/src/core/payments/tax-id-catalog.ts +320 -0
  308. package/src/core/payments/wallet-contact.ts +56 -0
  309. package/src/elements/address.tsx +570 -0
  310. package/src/elements/branding.tsx +87 -0
  311. package/src/elements/card-fields/context.tsx +310 -0
  312. package/src/elements/card-fields/fields.tsx +281 -0
  313. package/src/elements/card-fields/index.tsx +37 -0
  314. package/src/elements/card.tsx +101 -0
  315. package/src/elements/email.tsx +100 -0
  316. package/src/elements/payment/declared-fields.tsx +238 -0
  317. package/src/elements/payment/index.tsx +497 -0
  318. package/src/elements/payment/wallet.tsx +155 -0
  319. package/src/elements/tax-id.tsx +143 -0
  320. package/src/i18n/catalogs.ts +100 -0
  321. package/src/i18n/index.tsx +46 -0
  322. package/src/index.ts +68 -0
  323. package/src/namespace.tsx +231 -0
  324. package/src/native/index.ts +173 -0
  325. package/src/provider.tsx +76 -0
  326. package/src/specs/NativeWhopAddressSuggestions.ts +14 -0
  327. package/src/specs/NativeWhopAuthSession.ts +21 -0
  328. package/src/specs/NativeWhopPlatformPay.ts +25 -0
  329. package/src/specs/WhopPayButtonNativeComponent.ts +22 -0
  330. package/src/theme/appearance.ts +150 -0
  331. package/src/theme/provider.tsx +28 -0
  332. package/src/theme/resolve.ts +68 -0
  333. package/src/theme/scales.generated.ts +427 -0
  334. package/src/ui/element-frame.tsx +94 -0
  335. package/src/ui/payment-method-icon.tsx +55 -0
  336. package/src/ui/primitives.tsx +269 -0
  337. package/src/ui/select.tsx +255 -0
  338. package/src/ui/suggestions.tsx +69 -0
  339. package/src/ui/whop-logo.tsx +37 -0
@@ -0,0 +1,409 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Emits the Objective-C++ that React Native Codegen requires on iOS and that nobody should ever
4
+ * hand-maintain.
5
+ *
6
+ * A Fabric component view must be an ObjC++ class conforming to RCTComponentViewProtocol, because
7
+ * its props are C++ templates that Swift cannot see; a Turbo module must subclass the Codegen-emitted
8
+ * NativeXSpecBase for the same reason. Expo solves this with one generic ObjC++ base written once in
9
+ * expo-modules-core; this file solves it by generating the layer from the same specs Codegen reads,
10
+ * so a rename can never land on one side only.
11
+ *
12
+ * Output goes to ios/generated/, which is gitignored and rebuilt on `prepare`. Everything a human
13
+ * writes on iOS lives in ios/Sources/WhopElements and is Swift.
14
+ */
15
+ import { mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
16
+ import { dirname, join } from 'node:path';
17
+ import { fileURLToPath } from 'node:url';
18
+
19
+ const root = join(dirname(fileURLToPath(import.meta.url)), '..');
20
+ const specsDir = join(root, 'src', 'specs');
21
+ const outDir = join(root, 'ios', 'generated');
22
+
23
+ const SPEC_LIBRARY = 'WhopElementsSpec';
24
+
25
+ /** codegen prop type -> { objc, cxx accessor, swift } */
26
+ const PROP_TYPES = {
27
+ string: { objc: 'NSString *', convert: (expr) => `RCTNSStringFromString(${expr})`, compare: (a, b) => `${a} != ${b}` },
28
+ boolean: { objc: 'BOOL', convert: (expr) => expr, compare: (a, b) => `${a} != ${b}` },
29
+ double: { objc: 'double', convert: (expr) => expr, compare: (a, b) => `${a} != ${b}` },
30
+ enum: {
31
+ objc: 'NSString *',
32
+ convert: (expr, name) => `RCTNSStringFrom${name}(${expr})`,
33
+ compare: (a, b) => `${a} != ${b}`,
34
+ },
35
+ };
36
+
37
+ function read(file) {
38
+ return readFileSync(join(specsDir, file), 'utf8');
39
+ }
40
+
41
+ /** a commented-out example member would otherwise register as a real one */
42
+ function stripComments(src) {
43
+ return src.replace(/\/\*[\s\S]*?\*\//g, '').replace(/^[ \t]*\/\/.*$/gm, '');
44
+ }
45
+
46
+ function parseComponentSpec(file) {
47
+ const src = stripComments(read(file));
48
+ const name = /codegenNativeComponent<NativeProps>\(\s*'([^']+)'/.exec(src)?.[1];
49
+ if (!name) throw new Error(`${file}: no codegenNativeComponent<NativeProps>('Name') call found`);
50
+
51
+ const body = /export interface NativeProps extends ViewProps \{([\s\S]*?)\n\}/.exec(src)?.[1] ?? '';
52
+ const props = [];
53
+ const events = [];
54
+ for (const line of body.split('\n')) {
55
+ const m = /^\s*(\w+)\??:\s*(.+?);\s*$/.exec(line);
56
+ if (!m) continue;
57
+ const [, key, type] = m;
58
+ if (/DirectEventHandler|BubblingEventHandler/.test(type)) {
59
+ events.push({ key });
60
+ continue;
61
+ }
62
+ if (/WithDefault<\s*'/.test(type)) props.push({ key, kind: 'enum' });
63
+ else if (/WithDefault<\s*boolean|(^|\W)boolean(\W|$)/.test(type)) props.push({ key, kind: 'boolean' });
64
+ else if (/Double|Float|Int32/.test(type)) props.push({ key, kind: 'double' });
65
+ else props.push({ key, kind: 'string' });
66
+ }
67
+ return { name, props, events };
68
+ }
69
+
70
+ function parseModuleSpec(file) {
71
+ const src = stripComments(read(file));
72
+ // Either verb: `get` returns null for a module the binary lacks, `getEnforcing` throws.
73
+ const name = /TurboModuleRegistry\.get(?:Enforcing)?<Spec>\(\s*'([^']+)'/.exec(src)?.[1];
74
+ if (!name) throw new Error(`${file}: no TurboModuleRegistry.get<Spec>('Name') call found`);
75
+
76
+ const body = /export interface Spec extends TurboModule \{([\s\S]*?)\n\}/.exec(src)?.[1] ?? '';
77
+ const methods = [];
78
+ for (const line of body.split('\n')) {
79
+ const m = /^\s*(\w+)\(([^)]*)\)\s*:\s*(.+?);\s*$/.exec(line);
80
+ if (!m) continue;
81
+ const [, key, rawArgs, ret] = m;
82
+ const args = rawArgs
83
+ .split(',')
84
+ .map((a) => a.trim())
85
+ .filter(Boolean)
86
+ .map((a) => {
87
+ const [argName, argType] = a.split(':').map((p) => p.trim());
88
+ const optional = argName.endsWith('?') || /\|\s*null/.test(argType ?? '');
89
+ const base = argName.replace(/\?$/, '');
90
+ // `id` is a type in Objective-C, and a few others are keywords: emitting one as a
91
+ // parameter name produces a .mm that fails to compile, with the error pointing at
92
+ // generated code rather than at the spec that caused it.
93
+ if (RESERVED_OBJC_NAMES.has(base)) {
94
+ throw new Error(
95
+ `src/specs: '${base}' cannot be a parameter name — it is reserved in Objective-C, and the ` +
96
+ `generated bridge would not compile. Rename it in the spec (e.g. '${base}Value').`,
97
+ );
98
+ }
99
+ let objc = 'NSString *';
100
+ if (/\bboolean\b/.test(argType ?? '')) objc = 'BOOL';
101
+ else if (/\bnumber\b/.test(argType ?? '')) objc = 'double';
102
+ return { name: base, objc, optional };
103
+ });
104
+ methods.push({ key, args, promise: ret.trim().startsWith('Promise<'), returns: ret.trim() });
105
+ }
106
+ return { name, methods };
107
+ }
108
+
109
+ const RESERVED_OBJC_NAMES = new Set(['id', 'self', 'super', 'nil', 'Nil', 'BOOL', 'YES', 'NO', 'Class', 'SEL', 'IMP']);
110
+
111
+ const HEADER = `// GENERATED by scripts/codegen-shims.mjs — do not edit, do not commit.\n// Source of truth: src/specs/. Implementations are Swift, in ios/Sources/WhopElements.\n`;
112
+
113
+ function upperFirst(s) {
114
+ return s.charAt(0).toUpperCase() + s.slice(1);
115
+ }
116
+
117
+ function componentHeader(spec) {
118
+ return `${HEADER}
119
+ #import <React/RCTViewComponentView.h>
120
+ #import <UIKit/UIKit.h>
121
+
122
+ NS_ASSUME_NONNULL_BEGIN
123
+
124
+ @interface ${spec.name}ComponentView : RCTViewComponentView
125
+ @end
126
+
127
+ NS_ASSUME_NONNULL_END
128
+ `;
129
+ }
130
+
131
+ /** The ObjC mirror of the Swift view's @objc surface. Emitted from the same spec as the shim, so
132
+ * the two sides cannot drift; a Swift rename fails the ObjC compile instead of failing at runtime. */
133
+ function componentMirror(spec) {
134
+ const setters = spec.props
135
+ .map((p) => {
136
+ const t = PROP_TYPES[p.kind];
137
+ const objcType = t.objc === 'NSString *' ? 'NSString * _Nonnull' : t.objc;
138
+ return `- (void)set${upperFirst(p.key)}:(${objcType})value;`;
139
+ })
140
+ .join('\n');
141
+ const eventBlocks = spec.events.map((e) => `@property (nonatomic, copy, nullable) void (^${e.key})(void);`).join('\n');
142
+ return `${HEADER}
143
+ #import <UIKit/UIKit.h>
144
+
145
+ NS_ASSUME_NONNULL_BEGIN
146
+
147
+ @interface ${spec.name}Impl : UIView
148
+ ${eventBlocks}
149
+ ${setters}
150
+ @end
151
+
152
+ NS_ASSUME_NONNULL_END
153
+ `;
154
+ }
155
+
156
+ function componentImpl(spec) {
157
+ const propDiffs = spec.props
158
+ .map((p) => {
159
+ const t = PROP_TYPES[p.kind];
160
+ const value = p.kind === 'enum' ? `${spec.name}${upperFirst(p.key)}ToString(newViewProps.${p.key})` : t.convert(`newViewProps.${p.key}`);
161
+ return ` if (oldViewProps.${p.key} != newViewProps.${p.key}) {
162
+ [_impl set${upperFirst(p.key)}:${value}];
163
+ }`;
164
+ })
165
+ .join('\n');
166
+
167
+ const enumHelpers = spec.props
168
+ .filter((p) => p.kind === 'enum')
169
+ .map(
170
+ (p) => `static NSString *${spec.name}${upperFirst(p.key)}ToString(${spec.name}${upperFirst(p.key)} value)
171
+ {
172
+ return RCTNSStringFromString(toString(value));
173
+ }`,
174
+ )
175
+ .join('\n\n');
176
+
177
+ const eventWiring = spec.events
178
+ .map(
179
+ (e) => ` __weak __typeof(self) weakSelf = self;
180
+ _impl.${e.key} = ^{
181
+ __strong __typeof(weakSelf) strongSelf = weakSelf;
182
+ if (!strongSelf || !strongSelf->_eventEmitter) { return; }
183
+ std::static_pointer_cast<const ${spec.name}EventEmitter>(strongSelf->_eventEmitter)->${e.key}({});
184
+ };`,
185
+ )
186
+ .join('\n');
187
+
188
+ return `${HEADER}
189
+ #import "${spec.name}ComponentView.h"
190
+ #import "WhopElementsShims.h"
191
+
192
+ #import <React/RCTConversions.h>
193
+ #import <React/RCTFabricComponentsPlugins.h>
194
+
195
+ #import <react/renderer/components/${SPEC_LIBRARY}/ComponentDescriptors.h>
196
+ #import <react/renderer/components/${SPEC_LIBRARY}/EventEmitters.h>
197
+ #import <react/renderer/components/${SPEC_LIBRARY}/Props.h>
198
+ #import <react/renderer/components/${SPEC_LIBRARY}/RCTComponentViewHelpers.h>
199
+
200
+ using namespace facebook::react;
201
+
202
+ ${enumHelpers}
203
+
204
+ @interface ${spec.name}ComponentView () <RCT${spec.name}ViewProtocol>
205
+ @end
206
+
207
+ @implementation ${spec.name}ComponentView {
208
+ ${spec.name}Impl *_impl;
209
+ }
210
+
211
+ + (ComponentDescriptorProvider)componentDescriptorProvider
212
+ {
213
+ return concreteComponentDescriptorProvider<${spec.name}ComponentDescriptor>();
214
+ }
215
+
216
+ - (instancetype)initWithFrame:(CGRect)frame
217
+ {
218
+ if (self = [super initWithFrame:frame]) {
219
+ static const auto defaultProps = std::make_shared<const ${spec.name}Props>();
220
+ _props = defaultProps;
221
+ _impl = [[${spec.name}Impl alloc] initWithFrame:frame];
222
+ ${eventWiring}
223
+ self.contentView = _impl;
224
+ }
225
+ return self;
226
+ }
227
+
228
+ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
229
+ {
230
+ const auto &oldViewProps = *std::static_pointer_cast<${spec.name}Props const>(_props);
231
+ const auto &newViewProps = *std::static_pointer_cast<${spec.name}Props const>(props);
232
+
233
+ ${propDiffs}
234
+
235
+ [super updateProps:props oldProps:oldProps];
236
+ }
237
+
238
+ @end
239
+
240
+ Class<RCTComponentViewProtocol> ${spec.name}Cls(void)
241
+ {
242
+ return ${spec.name}ComponentView.class;
243
+ }
244
+ `;
245
+ }
246
+
247
+ /**
248
+ * Codegen's own Objective-C conventions, verified against its emitted protocol:
249
+ * - a boolean return is NSNumber *, a string return is NSString *, void stays void
250
+ * - a promise becomes trailing resolve/reject blocks
251
+ * - the first argument rides the selector, the rest are named keywords
252
+ */
253
+ function objcReturn(ret) {
254
+ if (/\bboolean\b/.test(ret)) return 'NSNumber *';
255
+ if (/\bstring\b/.test(ret)) return 'NSString *';
256
+ return 'void';
257
+ }
258
+
259
+ /** What Swift's @objc bridging actually produces: a Bool becomes BOOL, not a boxed NSNumber. */
260
+ function swiftReturn(ret) {
261
+ if (/\bboolean\b/.test(ret)) return 'BOOL';
262
+ if (/\bstring\b/.test(ret)) return 'NSString * _Nonnull';
263
+ return 'void';
264
+ }
265
+
266
+ function objcArgType(arg) {
267
+ if (arg.objc !== 'NSString *') return arg.objc;
268
+ return arg.optional ? 'NSString * _Nullable' : 'NSString *';
269
+ }
270
+
271
+ function selector(m, tail = []) {
272
+ const parts = m.args.map((a, i) =>
273
+ i === 0 ? `${m.key}:(${objcArgType(a)})${a.name}` : `${a.name}:(${objcArgType(a)})${a.name}`,
274
+ );
275
+ if (parts.length === 0) parts.push(m.key);
276
+ return [...parts, ...tail].join(' ');
277
+ }
278
+
279
+ /** The Swift class's @objc surface, mirrored so a rename fails the compile instead of the app. */
280
+ function moduleMirror(spec) {
281
+ const decls = spec.methods
282
+ .map((m) => {
283
+ if (m.promise) {
284
+ return `- (void)${selector(m, [
285
+ 'resolve:(void (^ _Nonnull)(id _Nullable))resolve',
286
+ 'reject:(void (^ _Nonnull)(NSString * _Nonnull, NSString * _Nonnull))reject',
287
+ ])};`;
288
+ }
289
+ return `- (${swiftReturn(m.returns)})${selector(m)};`;
290
+ })
291
+ .join('\n');
292
+
293
+ return `@interface ${spec.name}Impl : NSObject
294
+ ${decls}
295
+ @end
296
+ `;
297
+ }
298
+
299
+ function moduleImpl(spec) {
300
+ const body = spec.methods
301
+ .map((m) => {
302
+ const call = m.args.map((a, i) => (i === 0 ? `:${a.name}` : ` ${a.name}:${a.name}`)).join('');
303
+ const head = m.args.length === 0 ? m.key : '';
304
+
305
+ if (m.promise) {
306
+ return `- (void)${selector(m, ['resolve:(RCTPromiseResolveBlock)resolve', 'reject:(RCTPromiseRejectBlock)reject'])}
307
+ {
308
+ [_impl ${m.key}${call} resolve:^(id _Nullable value) { resolve(value); }
309
+ ${' '.repeat(m.key.length + 8)}reject:^(NSString *code, NSString *message) { reject(code, message, nil); }];
310
+ }`;
311
+ }
312
+
313
+ const ret = objcReturn(m.returns);
314
+ if (ret === 'void') {
315
+ return `- (void)${selector(m)}
316
+ {
317
+ [_impl ${head || m.key}${call}];
318
+ }`;
319
+ }
320
+ // Codegen declares a boolean return as NSNumber *, so box the Swift BOOL.
321
+ const expr = ret === 'NSNumber *' ? `@([_impl ${head || m.key}${call}])` : `[_impl ${head || m.key}${call}]`;
322
+ return `- (${ret})${selector(m)}
323
+ {
324
+ return ${expr};
325
+ }`;
326
+ })
327
+ .join('\n\n');
328
+
329
+ return `${HEADER}
330
+ #import "${spec.name}Module.h"
331
+ #import "WhopElementsShims.h"
332
+
333
+ @implementation ${spec.name}Module {
334
+ ${spec.name}Impl *_impl;
335
+ }
336
+
337
+ // The JS name, explicitly: bare RCT_EXPORT_MODULE() registers under the CLASS name
338
+ // ("${spec.name}Module"), and TurboModuleRegistry then cannot find "${spec.name}".
339
+ RCT_EXPORT_MODULE(${spec.name})
340
+
341
+ - (instancetype)init
342
+ {
343
+ if (self = [super init]) {
344
+ _impl = [${spec.name}Impl new];
345
+ }
346
+ return self;
347
+ }
348
+
349
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
350
+ (const facebook::react::ObjCTurboModule::InitParams &)params
351
+ {
352
+ return std::make_shared<facebook::react::Native${spec.name}SpecJSI>(params);
353
+ }
354
+
355
+ ${body}
356
+
357
+ @end
358
+ `;
359
+ }
360
+
361
+ function moduleHeader(spec) {
362
+ return `${HEADER}
363
+ #import <${SPEC_LIBRARY}/${SPEC_LIBRARY}.h>
364
+
365
+ NS_ASSUME_NONNULL_BEGIN
366
+
367
+ @interface ${spec.name}Module : NSObject <Native${spec.name}Spec>
368
+ @end
369
+
370
+ NS_ASSUME_NONNULL_END
371
+ `;
372
+ }
373
+
374
+ function main() {
375
+ rmSync(outDir, { recursive: true, force: true });
376
+ mkdirSync(outDir, { recursive: true });
377
+
378
+ const files = readdirSync(specsDir).filter((f) => f.endsWith('.ts'));
379
+ const components = files.filter((f) => f.endsWith('NativeComponent.ts')).map(parseComponentSpec);
380
+ const modules = files.filter((f) => /^Native\w+\.ts$/.test(f)).map(parseModuleSpec);
381
+
382
+ for (const spec of components) {
383
+ writeFileSync(join(outDir, `${spec.name}ComponentView.h`), componentHeader(spec));
384
+ writeFileSync(join(outDir, `${spec.name}ComponentView.mm`), componentImpl(spec));
385
+ }
386
+ for (const spec of modules) {
387
+ writeFileSync(join(outDir, `${spec.name}Module.h`), moduleHeader(spec));
388
+ writeFileSync(join(outDir, `${spec.name}Module.mm`), moduleImpl(spec));
389
+ }
390
+
391
+ const mirrors = [...components.map(componentMirror), ...modules.map(moduleMirror)]
392
+ .map((m) =>
393
+ m
394
+ .replace(HEADER, '')
395
+ .replace(/#import <UIKit\/UIKit\.h>/, '')
396
+ .replace(/NS_ASSUME_NONNULL_(BEGIN|END)/g, '')
397
+ .trim(),
398
+ )
399
+ .join('\n\n');
400
+ writeFileSync(
401
+ join(outDir, 'WhopElementsShims.h'),
402
+ `${HEADER}\n#import <UIKit/UIKit.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n${mirrors}\n\nNS_ASSUME_NONNULL_END\n`,
403
+ );
404
+
405
+ const names = [...components.map((c) => c.name), ...modules.map((m) => m.name)];
406
+ console.log(`whop elements: emitted ios/generated for ${names.join(', ')}`);
407
+ }
408
+
409
+ main();
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Emits src/core/address/country-names.generated.ts — English display names for exactly the
4
+ * countries in the address catalog.
5
+ *
6
+ * Hermes does not ship `Intl.DisplayNames`, so the address element's country selector cannot
7
+ * resolve names at runtime the way the web element does. Node has full ICU, so the table is
8
+ * resolved here at build time instead. A runtime that DOES have DisplayNames still gets localized
9
+ * names; this is the floor, not a replacement.
10
+ *
11
+ * Run after bumping the country catalog: pnpm country-names
12
+ */
13
+ import { readFileSync, writeFileSync } from 'node:fs';
14
+ import { dirname, join } from 'node:path';
15
+ import { fileURLToPath } from 'node:url';
16
+
17
+ const root = join(dirname(fileURLToPath(import.meta.url)), '..');
18
+ const catalog = join(root, 'src', 'core', 'address', 'country-data.ts');
19
+
20
+ function codes() {
21
+ const source = readFileSync(catalog, 'utf8');
22
+ const found = new Set();
23
+ for (const match of source.matchAll(/^ {2}([A-Z]{2}): \{/gm)) found.add(match[1]);
24
+ return [...found].sort();
25
+ }
26
+
27
+ function main() {
28
+ const display = new Intl.DisplayNames(['en'], { type: 'region' });
29
+ const rows = [];
30
+ for (const code of codes()) {
31
+ let name;
32
+ try {
33
+ name = display.of(code);
34
+ } catch {
35
+ name = undefined;
36
+ }
37
+ // An unknown region resolves to the code itself; no row is better than a fake name.
38
+ if (name && name !== code) rows.push([code, name]);
39
+ }
40
+
41
+ const body = rows.map(([code, name]) => `\t${code}: ${JSON.stringify(name)},`).join('\n');
42
+ writeFileSync(
43
+ join(root, 'src', 'core', 'address', 'country-names.generated.ts'),
44
+ [
45
+ '// GENERATED by scripts/gen-country-names.mjs from Node\'s ICU. Run `pnpm country-names`.',
46
+ '//',
47
+ '// Hermes ships no Intl.DisplayNames, so this is the country selector\'s name source there.',
48
+ '',
49
+ 'export const COUNTRY_NAMES_EN: Record<string, string> = {',
50
+ body,
51
+ '};',
52
+ '',
53
+ ].join('\n'),
54
+ );
55
+ console.log(`whop elements: wrote country-names.generated.ts (${rows.length} countries)`);
56
+ }
57
+
58
+ main();
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Emits src/theme/scales.generated.ts from frosted-ui's own stylesheet.
4
+ *
5
+ * NOT from @radix-ui/colors, which is what this generator used to read while claiming the result
6
+ * was "the same scales the web elements resolve through frosted-ui". It is not: frosted-ui
7
+ * customizes them, so raw Radix put blue-9 at #0090ff where the web renders #1754d8 — a visibly
8
+ * different accent on a payments button, in a package whose docs promise one styling vocabulary
9
+ * across both surfaces.
10
+ *
11
+ * frosted-ui publishes the palette only as CSS custom properties, so the values are parsed out of
12
+ * styles.css. It stays a devDependency: generation-time only, and the emitted file is committed.
13
+ *
14
+ * Run after bumping frosted-ui: pnpm theme
15
+ */
16
+ import { createRequire } from 'node:module';
17
+ import { readFileSync, writeFileSync } from 'node:fs';
18
+ import { dirname, join } from 'node:path';
19
+ import { fileURLToPath } from 'node:url';
20
+
21
+ const require = createRequire(import.meta.url);
22
+ const root = join(dirname(fileURLToPath(import.meta.url)), '..');
23
+
24
+ // The named scales the Appearance type accepts, from
25
+ // frontend/packages/elements-framework/src/builder/appearance/parse-theme.ts.
26
+ const ACCENTS = [
27
+ 'ruby', 'blue', 'red', 'yellow', 'green', 'gray', 'tomato', 'crimson', 'pink', 'plum',
28
+ 'purple', 'violet', 'iris', 'cyan', 'teal', 'jade', 'grass', 'brown', 'orange', 'indigo',
29
+ 'sky', 'mint', 'amber', 'lime', 'lemon', 'magenta', 'gold', 'bronze',
30
+ ];
31
+ const GRAYS = ['gray', 'mauve', 'slate', 'sage', 'olive', 'sand'];
32
+
33
+ // Radix has no `lemon` or `magenta`; frosted maps them onto their nearest published scale.
34
+ const ALIASES = { lemon: 'yellow', magenta: 'pink' };
35
+
36
+ /** Steps 9 and 10 are solid brand colors; every other step is a surface or a border. */
37
+ const CONTRAST_ON_9 = new Set(['sky', 'mint', 'lime', 'yellow', 'amber', 'lemon']);
38
+
39
+ /**
40
+ * The light and dark variable blocks, hex values only.
41
+ *
42
+ * frosted-ui also emits a display-p3 copy of every scale inside an @supports block. Those are not
43
+ * colors React Native can parse, and they appear LATER in the file, so a naive last-wins parse
44
+ * would collect them instead. Anything that is not a hex literal is skipped.
45
+ */
46
+ function readPalette(css) {
47
+ const light = {};
48
+ const dark = {};
49
+ // Blocks are introduced by their selector; the dark one is `.dark, .dark-theme`.
50
+ const blocks = css.split(/(?=^\s*[.:][^{]*\{)/m);
51
+ for (const block of blocks) {
52
+ const header = block.slice(0, block.indexOf('{'));
53
+ if (/display-p3/.test(block.slice(0, 200))) continue;
54
+ const target = /\.dark/.test(header) ? dark : /:root|\.light/.test(header) ? light : null;
55
+ if (!target) continue;
56
+ for (const [, name, step, value] of block.matchAll(/--([a-z]+)-(a?\d{1,2}):\s*(#[0-9a-fA-F]{3,8})/g)) {
57
+ target[`${name}-${step}`] = value;
58
+ }
59
+ }
60
+ if (!light['blue-9'] || !dark['blue-9']) {
61
+ throw new Error('could not read frosted-ui scales from styles.css — did its structure change?');
62
+ }
63
+ return { light, dark };
64
+ }
65
+
66
+ function scale(palette, name, dark, alpha) {
67
+ const source = ALIASES[name] ?? name;
68
+ const bag = dark ? palette.dark : palette.light;
69
+ return Array.from({ length: 12 }, (_, i) => {
70
+ const key = `${source}-${alpha ? 'a' : ''}${i + 1}`;
71
+ const step = bag[key];
72
+ if (!step) throw new Error(`frosted-ui is missing --${key} in the ${dark ? 'dark' : 'light'} block`);
73
+ return step;
74
+ });
75
+ }
76
+
77
+ function main() {
78
+ const css = readFileSync(require.resolve('frosted-ui/styles.css'), 'utf8');
79
+ const colors = readPalette(css);
80
+ const out = [];
81
+
82
+ out.push('// GENERATED by scripts/gen-theme.mjs from frosted-ui/styles.css. Run `pnpm theme` to refresh.');
83
+ out.push('');
84
+ out.push('export type Scale = readonly [string, string, string, string, string, string, string, string, string, string, string, string];');
85
+ out.push('');
86
+ out.push('export interface ColorScales {');
87
+ out.push('\tsolid: Scale;');
88
+ out.push('\talpha: Scale;');
89
+ out.push('\t/** Text that sits on step 9. */');
90
+ out.push('\tcontrast: string;');
91
+ out.push('}');
92
+ out.push('');
93
+
94
+ const emit = (label, names) => {
95
+ out.push(`export const ${label}: Record<string, { light: ColorScales; dark: ColorScales }> = {`);
96
+ for (const name of names) {
97
+ const contrast = CONTRAST_ON_9.has(name) ? '#1c2024' : '#ffffff';
98
+ out.push(`\t${name}: {`);
99
+ for (const mode of ['light', 'dark']) {
100
+ const dark = mode === 'dark';
101
+ out.push(`\t\t${mode}: {`);
102
+ out.push(`\t\t\tsolid: [${scale(colors, name, dark, false).map((c) => `'${c}'`).join(', ')}],`);
103
+ out.push(`\t\t\talpha: [${scale(colors, name, dark, true).map((c) => `'${c}'`).join(', ')}],`);
104
+ out.push(`\t\t\tcontrast: '${contrast}',`);
105
+ out.push('\t\t},');
106
+ }
107
+ out.push('\t},');
108
+ }
109
+ out.push('};');
110
+ out.push('');
111
+ };
112
+
113
+ emit('ACCENT_SCALES', ACCENTS);
114
+ emit('GRAY_SCALES', GRAYS);
115
+
116
+ out.push(`export const ACCENT_NAMES = [${ACCENTS.map((a) => `'${a}'`).join(', ')}] as const;`);
117
+ out.push(`export const GRAY_NAMES = [${GRAYS.map((g) => `'${g}'`).join(', ')}] as const;`);
118
+ out.push('');
119
+
120
+ writeFileSync(join(root, 'src', 'theme', 'scales.generated.ts'), out.join('\n'));
121
+ console.log(`whop elements: wrote src/theme/scales.generated.ts (${ACCENTS.length} accents, ${GRAYS.length} grays)`);
122
+ }
123
+
124
+ main();
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Renders assets/whop-wordmark-{light,dark}{,@2x,@3x}.png from the same paths the web element draws
4
+ * (frontend/apps/web/src/elements/payments/whop-logo.tsx).
5
+ *
6
+ * A bitmap rather than react-native-svg: a required native peer would have to ship its own
7
+ * Package.swift for a Swift Package Manager consumer to build, and an optional one is impossible
8
+ * because Metro resolves `require` statically.
9
+ *
10
+ * Needs rsvg-convert (`brew install librsvg`). Run after changing the paths: pnpm wordmark
11
+ */
12
+ import { execFileSync } from 'node:child_process';
13
+ import { mkdirSync, writeFileSync } from 'node:fs';
14
+ import { dirname, join } from 'node:path';
15
+ import { tmpdir } from 'node:os';
16
+ import { fileURLToPath } from 'node:url';
17
+
18
+ const root = join(dirname(fileURLToPath(import.meta.url)), '..');
19
+ const assets = join(root, 'assets');
20
+
21
+ /** Whop's brand orange, the same default the web wordmark carries. */
22
+ const MARK_FILL = '#FA4616';
23
+ const WIDTH = 119;
24
+ const HEIGHT = 26;
25
+
26
+ const MARK_PATHS = [
27
+ 'M6.29654 0.0205078C3.70387 0.0205078 1.92397 1.15863 0.565903 2.45194C0.565903 2.45194 0.0206171 2.96927 0.0309055 2.98996L5.71011 8.70125L11.3893 2.98996C10.309 1.50006 8.28221 0.0205078 6.29654 0.0205078Z',
28
+ 'M20.3194 0.0205078C17.7267 0.0205078 15.9468 1.16897 14.5888 2.45194C14.5888 2.45194 14.0949 2.95892 14.0641 2.98996L7.04736 10.0463L12.7163 15.7472L25.4019 2.98996C24.3216 1.50006 22.2948 0.0205078 20.3194 0.0205078Z',
29
+ 'M34.3738 0.0205078C31.7811 0.0205078 30.0012 1.16897 28.6431 2.45194C28.6431 2.45194 28.1287 2.95893 28.1081 2.98997L14.0645 17.1026L15.546 18.5925C17.8403 20.8998 21.6059 20.8998 23.9105 18.5925L39.4357 2.98997C38.3657 1.50006 36.3594 0.0205078 34.3738 0.0205078Z',
30
+ ];
31
+
32
+ const WORD_PATHS = [
33
+ 'M49.8827 20.3443L45.5576 2.99365H49.4193L52.3233 15.0096H52.3748L56.5557 2.99365H59.8716L64.0525 15.0096H64.104L67.0286 2.99365H70.8696L66.5446 20.3443H62.6108L58.2342 8.74266H58.1827L53.7856 20.3443H49.8827Z',
34
+ 'M72.0435 2.99365H75.6786V8.74266H75.7301C76.0493 8.15222 76.6878 7.67572 77.4292 7.35461C78.1192 7.05421 79.1181 6.86776 80.0037 6.86776C80.982 6.86776 82.3207 7.11636 83.2784 7.61357C84.2464 8.14186 84.9775 8.84624 85.4615 9.73708C85.9867 10.659 86.2441 11.6948 86.2441 12.855V20.3443H82.609V13.3419C82.609 12.3164 82.3104 11.5291 81.7028 10.9697C81.0952 10.4104 80.2714 10.1307 79.221 10.1307C78.1707 10.1307 77.3365 10.4311 76.6672 11.0215C76.0081 11.612 75.6889 12.3889 75.6889 13.3419V20.3443H72.0538V2.99365H72.0435Z',
35
+ 'M87.521 13.7976C87.521 12.7617 87.7166 11.8087 88.108 10.949C88.4993 10.0892 89.0348 9.36409 89.7144 8.76329C90.3941 8.1625 91.1767 7.69636 92.0623 7.36489C92.9479 7.03341 93.885 6.86768 94.8633 6.86768C95.8416 6.86768 96.8302 7.03341 97.7261 7.37525C98.622 7.71708 99.3944 8.18321 100.064 8.78401C100.723 9.38481 101.248 10.1099 101.639 10.9593C102.02 11.8087 102.216 12.7513 102.216 13.7872C102.216 14.8231 102.02 15.7968 101.629 16.6462C101.238 17.4956 100.702 18.2207 100.033 18.8215C99.3635 19.4223 98.5808 19.8884 97.6849 20.2199C96.789 20.5513 95.8519 20.7171 94.8736 20.7171C93.8953 20.7171 92.9273 20.5513 92.0314 20.2095C91.1355 19.8677 90.3632 19.4015 89.6938 18.7904C89.0348 18.1792 88.4993 17.4541 88.108 16.6047C87.7166 15.745 87.521 14.8127 87.521 13.7976ZM98.5808 13.7976C98.5808 12.6788 98.241 11.7776 97.5614 11.1043C96.8817 10.431 95.9858 10.0892 94.8736 10.0892C94.3278 10.0892 93.8336 10.1824 93.3702 10.3585C92.9067 10.545 92.5154 10.7936 92.1859 11.1147C91.8564 11.4358 91.6092 11.8294 91.4239 12.2852C91.2385 12.7513 91.1561 13.2486 91.1561 13.7976C91.1561 14.3466 91.2488 14.8438 91.4239 15.2996C91.6092 15.7553 91.8667 16.149 92.1962 16.4701C92.536 16.8015 92.9273 17.0501 93.3804 17.2366C93.8336 17.4231 94.3278 17.5059 94.8736 17.5059C95.4194 17.5059 95.9137 17.4127 96.3771 17.2366C96.8405 17.0501 97.2318 16.8015 97.5614 16.4804C97.8909 16.1593 98.1483 15.7657 98.3234 15.3099C98.4985 14.8438 98.5808 14.3362 98.5808 13.7976Z',
36
+ 'M103.544 6.85742H107.035V8.74268H107.087C107.313 8.24547 108.271 7.41679 109.033 7.19926C109.785 6.98173 110.547 6.86778 111.319 6.86778C112.298 6.86778 113.204 7.05423 114.028 7.41679C114.851 7.77934 115.552 8.27655 116.159 8.89806C116.757 9.52993 117.22 10.255 117.56 11.0941C117.9 11.9331 118.064 12.8343 118.064 13.7873C118.064 14.7403 117.9 15.6933 117.56 16.5323C117.22 17.3714 116.757 18.1068 116.159 18.718C115.562 19.3395 114.851 19.8264 114.017 20.1785C113.193 20.5307 112.287 20.7068 111.33 20.7068C111.021 20.7068 110.671 20.6758 110.289 20.624C109.898 20.5618 109.527 20.4789 109.157 20.365C108.786 20.2511 108.436 20.106 108.096 19.9299C107.756 19.7538 107.478 19.557 107.252 19.3188H107.2V25.8965H103.565L103.544 6.85742ZM114.409 13.7977C114.409 12.6789 114.069 11.7777 113.399 11.1148C112.73 10.4518 111.834 10.11 110.722 10.11C110.331 10.11 109.919 10.1722 109.507 10.3068C109.085 10.4415 108.704 10.6279 108.364 10.8765C108.024 11.1252 107.736 11.4256 107.519 11.7674C107.303 12.1092 107.19 12.5132 107.19 12.9586V14.616C107.19 15.1961 107.355 15.6933 107.694 16.1283C108.024 16.553 108.456 16.8845 108.961 17.1124C109.486 17.3714 110.084 17.506 110.753 17.506C111.855 17.506 112.74 17.1642 113.41 16.4805C114.069 15.7969 114.409 14.8957 114.409 13.7977Z',
37
+ ];
38
+
39
+ /** The mark keeps Whop's orange in both themes; only the wordmark's ink changes. */
40
+ const INK = { light: '#1c2024', dark: '#f5f5f7' };
41
+
42
+ function svg(wordFill) {
43
+ const paths = [
44
+ ...MARK_PATHS.map((d) => `<path d="${d}" fill="${MARK_FILL}"/>`),
45
+ ...WORD_PATHS.map((d) => `<path d="${d}" fill="${wordFill}"/>`),
46
+ ].join('');
47
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${WIDTH}" height="${HEIGHT}" viewBox="0 0 ${WIDTH} ${HEIGHT}" fill="none">${paths}</svg>`;
48
+ }
49
+
50
+ function main() {
51
+ mkdirSync(assets, { recursive: true });
52
+ for (const [mode, ink] of Object.entries(INK)) {
53
+ const source = join(tmpdir(), `whop-wordmark-${mode}.svg`);
54
+ writeFileSync(source, svg(ink));
55
+ for (const scale of [1, 2, 3]) {
56
+ const suffix = scale === 1 ? '' : `@${scale}x`;
57
+ execFileSync('rsvg-convert', [
58
+ '-w', String(WIDTH * scale),
59
+ '-h', String(HEIGHT * scale),
60
+ source,
61
+ '-o', join(assets, `whop-wordmark-${mode}${suffix}.png`),
62
+ ]);
63
+ }
64
+ }
65
+ console.log('whop elements: wrote assets/whop-wordmark-{light,dark}{,@2x,@3x}.png');
66
+ }
67
+
68
+ main();