vibefast-cli 1.1.5 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (301) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +63 -169
  3. package/dist/__tests__/recipes.test.js +89 -85
  4. package/dist/__tests__/recipes.test.js.map +1 -1
  5. package/dist/commands/add.d.ts +1 -1
  6. package/dist/commands/add.d.ts.map +1 -1
  7. package/dist/commands/add.js +576 -588
  8. package/dist/commands/add.js.map +1 -1
  9. package/dist/commands/checklist.d.ts +1 -1
  10. package/dist/commands/checklist.d.ts.map +1 -1
  11. package/dist/commands/checklist.js +40 -39
  12. package/dist/commands/checklist.js.map +1 -1
  13. package/dist/commands/doctor.d.ts +1 -1
  14. package/dist/commands/doctor.js +22 -22
  15. package/dist/commands/doctor.js.map +1 -1
  16. package/dist/commands/env.d.ts +1 -1
  17. package/dist/commands/env.d.ts.map +1 -1
  18. package/dist/commands/env.js +58 -53
  19. package/dist/commands/env.js.map +1 -1
  20. package/dist/commands/health.d.ts +1 -1
  21. package/dist/commands/health.d.ts.map +1 -1
  22. package/dist/commands/health.js +101 -93
  23. package/dist/commands/health.js.map +1 -1
  24. package/dist/commands/init.d.ts +1 -1
  25. package/dist/commands/init.d.ts.map +1 -1
  26. package/dist/commands/init.js +416 -296
  27. package/dist/commands/init.js.map +1 -1
  28. package/dist/commands/remove.d.ts +1 -1
  29. package/dist/commands/remove.d.ts.map +1 -1
  30. package/dist/commands/remove.js +77 -64
  31. package/dist/commands/remove.js.map +1 -1
  32. package/dist/commands/status.d.ts +1 -1
  33. package/dist/commands/status.d.ts.map +1 -1
  34. package/dist/commands/status.js +15 -14
  35. package/dist/commands/status.js.map +1 -1
  36. package/dist/core/__tests__/detect.test.js +68 -34
  37. package/dist/core/__tests__/detect.test.js.map +1 -1
  38. package/dist/core/ast.d.ts +14 -0
  39. package/dist/core/ast.d.ts.map +1 -0
  40. package/dist/core/ast.js +239 -0
  41. package/dist/core/ast.js.map +1 -0
  42. package/dist/core/codemod.d.ts.map +1 -1
  43. package/dist/core/codemod.js +62 -44
  44. package/dist/core/codemod.js.map +1 -1
  45. package/dist/core/config.d.ts +10 -0
  46. package/dist/core/config.d.ts.map +1 -0
  47. package/dist/core/config.js +51 -0
  48. package/dist/core/config.js.map +1 -0
  49. package/dist/core/detect.d.ts +8 -2
  50. package/dist/core/detect.d.ts.map +1 -1
  51. package/dist/core/detect.js +52 -21
  52. package/dist/core/detect.js.map +1 -1
  53. package/dist/core/errors.d.ts.map +1 -1
  54. package/dist/core/errors.js +9 -8
  55. package/dist/core/errors.js.map +1 -1
  56. package/dist/core/exec.d.ts +16 -0
  57. package/dist/core/exec.d.ts.map +1 -0
  58. package/dist/core/exec.js +48 -0
  59. package/dist/core/exec.js.map +1 -0
  60. package/dist/core/manualSteps.d.ts +7 -0
  61. package/dist/core/manualSteps.d.ts.map +1 -0
  62. package/dist/core/manualSteps.js +59 -0
  63. package/dist/core/manualSteps.js.map +1 -0
  64. package/dist/core/paths.d.ts +3 -1
  65. package/dist/core/paths.d.ts.map +1 -1
  66. package/dist/core/paths.js +14 -10
  67. package/dist/core/paths.js.map +1 -1
  68. package/dist/core/spinner.d.ts +1 -1
  69. package/dist/core/spinner.d.ts.map +1 -1
  70. package/dist/core/spinner.js +38 -8
  71. package/dist/core/spinner.js.map +1 -1
  72. package/dist/core/vosk.d.ts.map +1 -1
  73. package/dist/core/vosk.js +50 -39
  74. package/dist/core/vosk.js.map +1 -1
  75. package/docs/manual-testing.md +91 -0
  76. package/package.json +6 -3
  77. package/recipes/audio-recorder/apps/native/src/app/audio-recorder/index.tsx +5 -0
  78. package/recipes/audio-recorder/recipe.json +3 -3
  79. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/audio-player.tsx +301 -0
  80. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/audio-recorder.tsx +373 -0
  81. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/audio-waveform.tsx +270 -0
  82. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/index.ts +4 -0
  83. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/recording-list.tsx +89 -0
  84. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/audio-player-demo.tsx +66 -0
  85. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/audio-recorder-cloud.tsx +68 -0
  86. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/audio-recorder-interview.tsx +102 -0
  87. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/basic.tsx +27 -0
  88. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/index.ts +5 -0
  89. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/with-recording-list-demo.tsx +82 -0
  90. package/recipes/audio-recorder-supabase/packages/backend/src/services/recordings.ts +369 -0
  91. package/recipes/audio-recorder-supabase/packages/backend/supabase/migrations/recordings.sql +70 -0
  92. package/recipes/audio-recorder-supabase/recipe.json +52 -0
  93. package/recipes/audio-recorder-supabase@latest.zip +0 -0
  94. package/recipes/audio-recorder@latest.zip +0 -0
  95. package/recipes/charts/apps/native/src/features/charts/components/bar-chart.tsx +3 -3
  96. package/recipes/charts/apps/native/src/features/charts/components/candlestick-chart.tsx +2 -2
  97. package/recipes/charts/apps/native/src/features/charts/components/chart-card.tsx +5 -5
  98. package/recipes/charts/apps/native/src/features/charts/components/column-chart.tsx +3 -3
  99. package/recipes/charts/apps/native/src/features/charts/components/doughnut-chart.tsx +20 -4
  100. package/recipes/charts/apps/native/src/features/charts/components/line-chart.tsx +7 -6
  101. package/recipes/charts/apps/native/src/features/charts/components/radar-chart.tsx +6 -4
  102. package/recipes/charts/apps/native/src/features/charts/components/radial-bar-chart.tsx +1 -1
  103. package/recipes/charts/apps/native/src/features/charts/components/stacked-bar-chart.tsx +5 -4
  104. package/recipes/charts/recipe.json +4 -13
  105. package/recipes/charts@latest.zip +0 -0
  106. package/recipes/chatbot/apps/native/src/app/chatbot/index.tsx +1 -0
  107. package/recipes/chatbot/apps/native/src/features/chatbot/components/chat-markdown.tsx +86 -86
  108. package/recipes/chatbot/apps/native/src/features/chatbot/components/markdown/code-block.tsx +86 -53
  109. package/recipes/chatbot/recipe.json +26 -92
  110. package/recipes/chatbot-supabase/apps/native/src/api-client/supabase/chatbot.ts +515 -0
  111. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/app/index.tsx +257 -0
  112. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-header-buttons.tsx +59 -0
  113. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-input-bar.tsx +485 -0
  114. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-markdown.tsx +575 -0
  115. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-message-bubble.tsx +223 -0
  116. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-settings-modal.tsx +161 -0
  117. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/image-preview-list.tsx +116 -0
  118. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/markdown/code-block.tsx +165 -0
  119. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/markdown/index.ts +10 -0
  120. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/markdown/table-renderer.tsx +129 -0
  121. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/message-error-boundary.tsx +78 -0
  122. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/message-list.tsx +170 -0
  123. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/model-selector.tsx +283 -0
  124. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/report-content-modal.tsx +188 -0
  125. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/suggested-messages.tsx +67 -0
  126. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/constants/models.ts +20 -0
  127. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/constants/report-reasons.ts +9 -0
  128. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-attachment-cache.ts +142 -0
  129. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-chat-config.ts +458 -0
  130. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-chat-handlers.ts +429 -0
  131. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-chatbot-settings.ts +89 -0
  132. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-conversation.ts +90 -0
  133. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-image-picker.ts +122 -0
  134. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-keyboard-coordinator.ts +161 -0
  135. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-smart-scroll-manager.ts +213 -0
  136. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/models/index.ts +86 -0
  137. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/models/models.ts +162 -0
  138. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/models/providers.ts +62 -0
  139. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/models/types.ts +40 -0
  140. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/services/file-uploader.ts +287 -0
  141. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/services/message-handler-service.ts +189 -0
  142. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/types/index.ts +70 -0
  143. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/utils/chat-telemetry.ts +91 -0
  144. package/recipes/chatbot-supabase/packages/backend/src/services/conversations.ts +243 -0
  145. package/recipes/chatbot-supabase/packages/backend/src/services/messages.ts +327 -0
  146. package/recipes/chatbot-supabase/packages/backend/supabase/functions/chat-stream/index.ts +347 -0
  147. package/recipes/chatbot-supabase/packages/backend/supabase/migrations/chatbot.sql +104 -0
  148. package/recipes/chatbot-supabase/recipe.json +106 -0
  149. package/recipes/chatbot-supabase@latest.zip +0 -0
  150. package/recipes/chatbot.zip +0 -0
  151. package/recipes/chatbot@latest.zip +0 -0
  152. package/recipes/image-analysis/packages/backend/convex/imageAnalysis/index.ts +2 -2
  153. package/recipes/image-analysis/packages/backend/convex/imageAnalysis.ts +0 -1
  154. package/recipes/image-analysis/recipe.json +15 -55
  155. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/analysis-options-screen.tsx +304 -0
  156. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/camera.tsx +221 -0
  157. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/image-capture-screen.tsx +333 -0
  158. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/loading-screen.tsx +214 -0
  159. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/loading.tsx +191 -0
  160. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/results.tsx +137 -0
  161. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/trait-details.tsx +172 -0
  162. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/use-analysis-data.ts +160 -0
  163. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/use-results-screen.ts +151 -0
  164. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/achievement-badge.tsx +77 -0
  165. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/achievement-card.tsx +75 -0
  166. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/achievement-unlocked-modal.tsx +162 -0
  167. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/achievements-section.tsx +44 -0
  168. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/advice-list.tsx +42 -0
  169. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/circular-progress.tsx +233 -0
  170. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/content-card.tsx +38 -0
  171. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/error-state.tsx +42 -0
  172. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/index.ts +9 -0
  173. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/loading-state.tsx +26 -0
  174. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/profile-image.tsx +60 -0
  175. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/results-header.tsx +62 -0
  176. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/score-display.tsx +54 -0
  177. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/share-options-modal.tsx +110 -0
  178. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/traits-grid.tsx +74 -0
  179. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/config/analysis-config.ts +80 -0
  180. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/config/master-analysis-config.ts +157 -0
  181. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/hooks/index.ts +1 -0
  182. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/hooks/use-analysis.ts +38 -0
  183. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/hooks/use-image-analysis.ts +208 -0
  184. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/services/analysis-service.ts +262 -0
  185. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/services/share-service.ts +176 -0
  186. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/services/trait-details-service.ts +289 -0
  187. package/recipes/image-analysis-supabase/packages/backend/src/services/image-analyses.ts +132 -0
  188. package/recipes/image-analysis-supabase/packages/backend/supabase/functions/analyze-image/index.ts +312 -0
  189. package/recipes/image-analysis-supabase/packages/backend/supabase/migrations/image_analysis.sql +42 -0
  190. package/recipes/image-analysis-supabase/recipe.json +90 -0
  191. package/recipes/image-analysis-supabase@latest.zip +0 -0
  192. package/recipes/image-analysis@latest.zip +0 -0
  193. package/recipes/image-generator/apps/native/src/features/image-generator/app/index.tsx +16 -2
  194. package/recipes/image-generator/apps/native/src/features/image-generator/components/image-model-selector.tsx +11 -5
  195. package/recipes/image-generator/apps/native/src/features/image-generator/hooks/use-image-generator.ts +11 -5
  196. package/recipes/image-generator/packages/backend/convex/imageGeneration/index.ts +2 -2
  197. package/recipes/image-generator/recipe.json +16 -39
  198. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/app/_layout.tsx +26 -0
  199. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/app/gallery.tsx +217 -0
  200. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/app/index.tsx +251 -0
  201. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/components/gallery-image.tsx +25 -0
  202. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/components/image-detail-modal.tsx +215 -0
  203. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/components/image-model-selector.tsx +216 -0
  204. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/components/image-placeholder.tsx +26 -0
  205. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/hooks/use-image-gallery.ts +71 -0
  206. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/hooks/use-image-generator-settings.ts +152 -0
  207. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/hooks/use-image-generator.ts +103 -0
  208. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/models/models.ts +66 -0
  209. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/services/image-gallery-service.ts +96 -0
  210. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/services/image-save-service.ts +120 -0
  211. package/recipes/image-generator-supabase/packages/backend/supabase/functions/generate-image/index.ts +291 -0
  212. package/recipes/image-generator-supabase/packages/backend/supabase/migrations/image_generator.sql +71 -0
  213. package/recipes/image-generator-supabase/recipe.json +86 -0
  214. package/recipes/image-generator-supabase@latest.zip +0 -0
  215. package/recipes/image-generator@latest.zip +0 -0
  216. package/recipes/ios-widget/recipe.json +15 -24
  217. package/recipes/ios-widget@latest.zip +0 -0
  218. package/recipes/onboarding/apps/native/src/features/onboarding/analytics/index.ts +9 -0
  219. package/recipes/onboarding/apps/native/src/features/onboarding/components/onboarding-with-analytics.tsx +141 -0
  220. package/recipes/onboarding/apps/native/src/features/onboarding/components/onboarding.tsx +173 -0
  221. package/recipes/onboarding/apps/native/src/features/onboarding/config/onboarding-flow-config.ts +189 -0
  222. package/recipes/onboarding/apps/native/src/features/onboarding/demo-one/app/index.tsx +42 -0
  223. package/recipes/onboarding/apps/native/src/features/onboarding/demo-one/data.ts +32 -0
  224. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/app/index.tsx +43 -0
  225. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/interactive-onboarding.tsx +222 -0
  226. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/ai-tone-step.tsx +133 -0
  227. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/currency-step.tsx +165 -0
  228. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-ai-step.tsx +199 -0
  229. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-chatbot-step.tsx +154 -0
  230. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-manual-step.tsx +156 -0
  231. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-scan-step.tsx +158 -0
  232. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/main-reason-step.tsx +139 -0
  233. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/notification-step.tsx +129 -0
  234. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/overspend-step.tsx +138 -0
  235. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/personalizing-step.tsx +190 -0
  236. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/rating-step.tsx +98 -0
  237. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/reminder-step.tsx +181 -0
  238. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/safety-step.tsx +110 -0
  239. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/struggle-step.tsx +139 -0
  240. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/welcome-step.tsx +217 -0
  241. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/ui/onboarding-header.tsx +58 -0
  242. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/constants.ts +179 -0
  243. package/recipes/onboarding/apps/native/src/features/onboarding/hooks/use-onboarding-analytics.ts +323 -0
  244. package/recipes/onboarding/apps/native/src/features/onboarding/services/onboarding-analytics.ts +432 -0
  245. package/recipes/onboarding/recipe.json +15 -0
  246. package/recipes/onboarding@latest.zip +0 -0
  247. package/recipes/payments/recipe.json +28 -61
  248. package/recipes/payments-supabase/apps/native/src/features/payments/README.md +200 -0
  249. package/recipes/payments-supabase/apps/native/src/features/payments/app/local-paywall.tsx +194 -0
  250. package/recipes/payments-supabase/apps/native/src/features/payments/app/remote-paywall.tsx +79 -0
  251. package/recipes/payments-supabase/apps/native/src/features/payments/components/payment-initializer.tsx +95 -0
  252. package/recipes/payments-supabase/apps/native/src/features/payments/components/paywall-error-state.tsx +60 -0
  253. package/recipes/payments-supabase/apps/native/src/features/payments/components/paywall-local-mode.tsx +116 -0
  254. package/recipes/payments-supabase/apps/native/src/features/payments/components/paywall-product-card.tsx +133 -0
  255. package/recipes/payments-supabase/apps/native/src/features/payments/components/paywall-remote-mode.tsx +146 -0
  256. package/recipes/payments-supabase/apps/native/src/features/payments/hooks/use-entitlement.ts +63 -0
  257. package/recipes/payments-supabase/apps/native/src/features/payments/index.ts +8 -0
  258. package/recipes/payments-supabase/apps/native/src/features/payments/services/revenuecat-adapter.ts +407 -0
  259. package/recipes/payments-supabase/packages/backend/src/services/payments.ts +201 -0
  260. package/recipes/payments-supabase/packages/backend/supabase/migrations/payments.sql +35 -0
  261. package/recipes/payments-supabase/recipe.json +72 -0
  262. package/recipes/payments-supabase@latest.zip +0 -0
  263. package/recipes/payments@latest.zip +0 -0
  264. package/recipes/quiz/apps/native/src/features/quiz/index.tsx +1 -2
  265. package/recipes/quiz/recipe.json +6 -9
  266. package/recipes/quiz@latest.zip +0 -0
  267. package/recipes/tracker-app/apps/native/src/features/tracker-app/app/index.tsx +1 -2
  268. package/recipes/tracker-app/recipe.json +7 -10
  269. package/recipes/tracker-app@latest.zip +0 -0
  270. package/recipes/voice-bot/recipe.json +8 -68
  271. package/recipes/voice-bot.zip +0 -0
  272. package/recipes/voice-bot@latest.zip +0 -0
  273. package/recipes/wake-word/recipe.json +10 -9
  274. package/recipes/wake-word.zip +0 -0
  275. package/recipes/wake-word@latest.zip +0 -0
  276. package/recipes/charts/apps/native/src/app/(root)/(protected)/charts/index.tsx +0 -3
  277. package/recipes/chatbot/packages/backend/convex/lib/rateLimit.ts +0 -100
  278. package/recipes/chatbot/packages/backend/convex/lib/telemetry.ts +0 -29
  279. package/recipes/chatbot/packages/backend/convex/ragKnowledge.ts +0 -0
  280. package/recipes/image-analysis/apps/native/assets/features/image-analyzer/front.jpg +0 -0
  281. package/recipes/image-analysis/apps/native/assets/features/image-analyzer/side.jpg +0 -0
  282. package/recipes/image-analysis/apps/native/assets/features/image-analyzer/threeQuarter.jpg +0 -0
  283. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/_layout.tsx +0 -5
  284. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/analysis-options.tsx +0 -50
  285. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/camera.tsx +0 -2
  286. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/index.tsx +0 -50
  287. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/loading.tsx +0 -50
  288. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/results.tsx +0 -2
  289. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/trait-details.tsx +0 -3
  290. package/recipes/image-analysis/packages/backend/convex/imageAnalysisFunctions.ts +0 -325
  291. package/recipes/image-analysis/packages/backend/convex/lib/ai/imageAnalysisAdapter.ts +0 -200
  292. package/recipes/payments/apps/native/src/app/(root)/(protected)/paywall/index.tsx +0 -74
  293. package/recipes/payments/apps/native/src/app/(root)/(protected)/paywall/local.tsx +0 -25
  294. package/recipes/payments/apps/native/src/app/(root)/(protected)/paywall/remote.tsx +0 -23
  295. package/recipes/quiz/apps/native/src/app/(root)/(protected)/quiz/index.tsx +0 -47
  296. package/recipes/tracker-app/apps/native/src/app/(root)/(protected)/tracker-app/index.tsx +0 -1
  297. package/recipes/voice-bot/apps/native/src/app/(root)/(protected)/voice-bot/index.tsx +0 -27
  298. package/recipes/voice-bot/packages/backend/convex/router.ts +0 -81
  299. /package/recipes/{chatbot/apps/native/src/app/(root)/(protected) → chatbot-supabase/apps/native/src/app}/chatbot/index.tsx +0 -0
  300. /package/recipes/{image-generator/apps/native/src/app/(root)/(protected) → image-generator-supabase/apps/native/src/app}/image-generator/gallery.tsx +0 -0
  301. /package/recipes/{image-generator/apps/native/src/app/(root)/(protected) → image-generator-supabase/apps/native/src/app}/image-generator/index.tsx +0 -0
@@ -0,0 +1,139 @@
1
+ import React from 'react';
2
+ import { View, ScrollView } from 'react-native';
3
+ import Animated, { FadeIn, FadeOut, FadeInDown } from 'react-native-reanimated';
4
+ import { Text, Button, Pressable } from '@/components/ui';
5
+ import { useThemeConfig } from '@/lib/use-theme-config';
6
+ import { MaterialCommunityIcons } from '@expo/vector-icons';
7
+ import * as Haptics from 'expo-haptics';
8
+
9
+ interface StruggleStepProps {
10
+ selectedStruggle: string | null;
11
+ onSelectStruggle: (struggle: string) => void;
12
+ onNext: () => void;
13
+ }
14
+
15
+ const STRUGGLES = [
16
+ {
17
+ id: 'food',
18
+ label: 'I spend too much on food / delivery',
19
+ icon: 'food',
20
+ color: '#EF4444',
21
+ },
22
+ {
23
+ id: 'impulse',
24
+ label: 'Impulse buys / random shopping',
25
+ icon: 'shopping',
26
+ color: '#F59E0B',
27
+ },
28
+ {
29
+ id: 'mindless',
30
+ label: "I swipe my card and don't think",
31
+ icon: 'credit-card-remove-outline',
32
+ color: '#8B5CF6',
33
+ },
34
+ {
35
+ id: 'clueless',
36
+ label: 'I have no idea where my money goes',
37
+ icon: 'help-circle-outline',
38
+ color: '#6366F1',
39
+ },
40
+ {
41
+ id: 'inconsistent',
42
+ label: "I'm inconsistent: some months I'm great, some months I'm a mess",
43
+ icon: 'chart-bell-curve-cumulative',
44
+ color: '#10B981',
45
+ },
46
+ ];
47
+
48
+ export function StruggleStep({
49
+ selectedStruggle,
50
+ onSelectStruggle,
51
+ onNext,
52
+ }: StruggleStepProps) {
53
+ const theme = useThemeConfig();
54
+
55
+ return (
56
+ <Animated.View
57
+ entering={FadeIn}
58
+ exiting={FadeOut}
59
+ className="flex-1 px-6 pt-4"
60
+ >
61
+ <Text className="text-3xl font-bold mb-3">
62
+ What do you struggle with the most?
63
+ </Text>
64
+ <Text className="text-lg text-muted-foreground mb-8">
65
+ Be honest, we won't judge!
66
+ </Text>
67
+
68
+ <ScrollView className="flex-1" showsVerticalScrollIndicator={false}>
69
+ <View className="gap-3 pb-8">
70
+ {STRUGGLES.map((item, index) => (
71
+ <Animated.View
72
+ entering={FadeInDown.delay(index * 100).duration(400)}
73
+ key={item.id}
74
+ >
75
+ <Pressable
76
+ onPress={() => onSelectStruggle(item.id)}
77
+ className={`flex-row items-center p-4 rounded-2xl border relative overflow-hidden ${
78
+ selectedStruggle === item.id
79
+ ? 'border-transparent'
80
+ : 'border-border bg-card'
81
+ }`}
82
+ style={
83
+ selectedStruggle === item.id
84
+ ? { borderColor: item.color, borderWidth: 1 }
85
+ : {}
86
+ }
87
+ >
88
+ {selectedStruggle === item.id && (
89
+ <View
90
+ className="absolute inset-0 opacity-10"
91
+ style={{ backgroundColor: item.color }}
92
+ />
93
+ )}
94
+
95
+ <View
96
+ className="p-3 rounded-full mr-4"
97
+ style={{ backgroundColor: item.color + '20' }}
98
+ >
99
+ <MaterialCommunityIcons
100
+ name={item.icon as any}
101
+ size={24}
102
+ color={item.color}
103
+ />
104
+ </View>
105
+
106
+ <Text
107
+ className={`ml-4 flex-1 font-medium text-base text-foreground`}
108
+ >
109
+ {item.label}
110
+ </Text>
111
+
112
+ {selectedStruggle === item.id && (
113
+ <MaterialCommunityIcons
114
+ name="check-circle"
115
+ size={24}
116
+ color={item.color}
117
+ />
118
+ )}
119
+ </Pressable>
120
+ </Animated.View>
121
+ ))}
122
+ </View>
123
+ </ScrollView>
124
+
125
+ <View className="pt-4 border-t border-border bg-background">
126
+ <Button
127
+ label="Continue"
128
+ onPress={() => {
129
+ Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
130
+ onNext();
131
+ }}
132
+ disabled={!selectedStruggle}
133
+ size="lg"
134
+ className="w-full m-0"
135
+ />
136
+ </View>
137
+ </Animated.View>
138
+ );
139
+ }
@@ -0,0 +1,217 @@
1
+ import React, { useState } from 'react';
2
+ import { View, ScrollView, TouchableOpacity } from 'react-native';
3
+ import Animated, { FadeIn, FadeOut, ZoomIn } from 'react-native-reanimated';
4
+ import { LinearGradient } from 'expo-linear-gradient';
5
+ import { MaterialCommunityIcons } from '@expo/vector-icons';
6
+ import { Text, Button, Pressable } from '@/components/ui';
7
+ import { useThemeConfig } from '@/lib/use-theme-config';
8
+ import * as Haptics from 'expo-haptics';
9
+ import * as Clipboard from 'expo-clipboard';
10
+
11
+ /**
12
+ * Recommended theme colors for this onboarding (from CashPilot/Expense Tracker app)
13
+ * Update your `src/lib/use-theme-config.tsx` with these colors for the best experience.
14
+ */
15
+ const RECOMMENDED_COLORS = [
16
+ {
17
+ name: 'primary',
18
+ hex: '#B47B44',
19
+ description: 'Earthy Brown - Main actions & highlights',
20
+ },
21
+ {
22
+ name: 'background',
23
+ hex: '#FFF9F0',
24
+ description: 'Warm Cream - Page background',
25
+ },
26
+ {
27
+ name: 'foreground',
28
+ hex: '#4A3B32',
29
+ description: 'Dark Brown - Primary text',
30
+ },
31
+ { name: 'card', hex: '#FFFFFF', description: 'White - Card backgrounds' },
32
+ {
33
+ name: 'border',
34
+ hex: '#EBE0D0',
35
+ description: 'Light Tan - Borders & dividers',
36
+ },
37
+ {
38
+ name: 'muted',
39
+ hex: '#FAF2E6',
40
+ description: 'Soft Cream - Muted backgrounds',
41
+ },
42
+ {
43
+ name: 'mutedForeground',
44
+ hex: '#8C7A6B',
45
+ description: 'Warm Gray - Secondary text',
46
+ },
47
+ {
48
+ name: 'secondary',
49
+ hex: '#86A778',
50
+ description: 'Sage Green - Secondary actions',
51
+ },
52
+ {
53
+ name: 'warning',
54
+ hex: '#F4C430',
55
+ description: 'Gold - Warnings & highlights',
56
+ },
57
+ ];
58
+
59
+ interface WelcomeStepProps {
60
+ onNext: () => void;
61
+ onSignIn: () => void;
62
+ }
63
+
64
+ export function WelcomeStep({ onNext, onSignIn }: WelcomeStepProps) {
65
+ const theme = useThemeConfig();
66
+ const [showPalette, setShowPalette] = useState(false);
67
+ const [copiedColor, setCopiedColor] = useState<string | null>(null);
68
+
69
+ const copyToClipboard = async (hex: string) => {
70
+ await Clipboard.setStringAsync(hex);
71
+ setCopiedColor(hex);
72
+ Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
73
+ setTimeout(() => setCopiedColor(null), 1500);
74
+ };
75
+
76
+ return (
77
+ <Animated.View
78
+ entering={FadeIn}
79
+ exiting={FadeOut}
80
+ className="flex-1 px-6 items-center"
81
+ >
82
+ <LinearGradient
83
+ colors={[theme.colors.primary + '20', 'transparent']}
84
+ style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }}
85
+ />
86
+
87
+ <ScrollView
88
+ className="flex-1 w-full"
89
+ showsVerticalScrollIndicator={false}
90
+ contentContainerStyle={{ flexGrow: 1 }}
91
+ >
92
+ <View className="flex-1 w-full md:max-w-4xl mx-auto">
93
+ <View className="flex-1 justify-center items-center py-8">
94
+ <Animated.View
95
+ entering={ZoomIn.duration(1000).springify()}
96
+ className="bg-primary/10 p-10 rounded-full mb-10 border-4 border-primary/5"
97
+ >
98
+ <MaterialCommunityIcons
99
+ name="wallet-outline"
100
+ size={80}
101
+ color={theme.colors.primary}
102
+ />
103
+ </Animated.View>
104
+ <Text className="text-5xl font-bold text-center mb-4 tracking-tight">
105
+ CashPilot
106
+ </Text>
107
+ <Text className="text-xl text-muted-foreground text-center mb-8 px-4 leading-8">
108
+ Your co-pilot for smarter financial decisions.
109
+ </Text>
110
+
111
+ {/* Color Palette Toggle */}
112
+ <TouchableOpacity
113
+ onPress={() => {
114
+ setShowPalette(!showPalette);
115
+ Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
116
+ }}
117
+ className="flex-row items-center gap-2 bg-muted/50 px-4 py-2 rounded-full mb-4"
118
+ >
119
+ <MaterialCommunityIcons
120
+ name="palette"
121
+ size={18}
122
+ color={theme.colors.mutedForeground}
123
+ />
124
+ <Text className="text-sm text-muted-foreground font-medium">
125
+ {showPalette ? 'Hide' : 'View'} Recommended Theme Colors
126
+ </Text>
127
+ <MaterialCommunityIcons
128
+ name={showPalette ? 'chevron-up' : 'chevron-down'}
129
+ size={18}
130
+ color={theme.colors.mutedForeground}
131
+ />
132
+ </TouchableOpacity>
133
+
134
+ {/* Color Palette Section */}
135
+ {showPalette && (
136
+ <Animated.View
137
+ entering={FadeIn.duration(300)}
138
+ exiting={FadeOut.duration(200)}
139
+ className="w-full bg-card border border-border rounded-2xl p-4 mb-4"
140
+ >
141
+ <Text className="text-sm font-bold mb-2 text-center">
142
+ 🎨 CashPilot Theme Colors
143
+ </Text>
144
+ <Text className="text-xs text-muted-foreground text-center mb-4">
145
+ Update{' '}
146
+ <Text className="font-mono">
147
+ src/lib/use-theme-config.tsx
148
+ </Text>{' '}
149
+ for best experience
150
+ </Text>
151
+
152
+ <View className="gap-2">
153
+ {RECOMMENDED_COLORS.map((color) => (
154
+ <TouchableOpacity
155
+ key={color.name}
156
+ onPress={() => copyToClipboard(color.hex)}
157
+ className="flex-row items-center gap-3 p-2 rounded-xl bg-muted/30 active:bg-muted/50"
158
+ >
159
+ <View
160
+ className="w-8 h-8 rounded-lg border border-border shadow-sm"
161
+ style={{ backgroundColor: color.hex }}
162
+ />
163
+ <View className="flex-1">
164
+ <View className="flex-row items-center gap-2">
165
+ <Text className="text-sm font-semibold">
166
+ {color.name}
167
+ </Text>
168
+ <Text className="text-xs font-mono text-muted-foreground">
169
+ {color.hex}
170
+ </Text>
171
+ {copiedColor === color.hex && (
172
+ <Text className="text-xs text-primary font-medium">
173
+ Copied!
174
+ </Text>
175
+ )}
176
+ </View>
177
+ <Text
178
+ className="text-xs text-muted-foreground"
179
+ numberOfLines={1}
180
+ >
181
+ {color.description}
182
+ </Text>
183
+ </View>
184
+ <MaterialCommunityIcons
185
+ name="content-copy"
186
+ size={16}
187
+ color={theme.colors.mutedForeground}
188
+ />
189
+ </TouchableOpacity>
190
+ ))}
191
+ </View>
192
+ </Animated.View>
193
+ )}
194
+ </View>
195
+
196
+ <View className="pt-4 border-t border-border bg-background pb-8">
197
+ <Button
198
+ label="Start Your Journey"
199
+ onPress={() => {
200
+ Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
201
+ onNext();
202
+ }}
203
+ size="lg"
204
+ className="w-full m-0 shadow-lg shadow-primary/20"
205
+ />
206
+ <Pressable onPress={onSignIn} className="mt-6 items-center">
207
+ <Text className="text-muted-foreground text-base">
208
+ Already have an account?{' '}
209
+ <Text className="text-primary font-bold">Sign in</Text>
210
+ </Text>
211
+ </Pressable>
212
+ </View>
213
+ </View>
214
+ </ScrollView>
215
+ </Animated.View>
216
+ );
217
+ }
@@ -0,0 +1,58 @@
1
+ import React from 'react';
2
+ import { View } from 'react-native';
3
+ import { MaterialCommunityIcons } from '@expo/vector-icons';
4
+ import Animated, {
5
+ useAnimatedStyle,
6
+ useSharedValue,
7
+ withTiming,
8
+ } from 'react-native-reanimated';
9
+ import { useThemeConfig } from '@/lib/use-theme-config';
10
+ import { Pressable } from '@/components/ui';
11
+
12
+ interface OnboardingHeaderProps {
13
+ onBack: () => void;
14
+ canGoBack: boolean;
15
+ progress?: number;
16
+ }
17
+
18
+ export function OnboardingHeader({
19
+ onBack,
20
+ canGoBack,
21
+ progress = 0,
22
+ }: OnboardingHeaderProps) {
23
+ const theme = useThemeConfig();
24
+ const progressValue = useSharedValue(0);
25
+
26
+ React.useEffect(() => {
27
+ progressValue.value = withTiming(progress, { duration: 500 });
28
+ }, [progress]);
29
+
30
+ const animatedProgressStyle = useAnimatedStyle(() => ({
31
+ width: `${progressValue.value * 100}%`,
32
+ }));
33
+
34
+ if (!canGoBack) return <View className="h-12" />;
35
+
36
+ return (
37
+ <View className="h-12 px-6 flex-row items-center">
38
+ <Pressable
39
+ onPress={onBack}
40
+ className="w-10 h-10 items-center justify-center -ml-2 rounded-full active:bg-muted/20 mr-4"
41
+ >
42
+ <MaterialCommunityIcons
43
+ name="chevron-left"
44
+ size={32}
45
+ color={theme.colors.foreground}
46
+ />
47
+ </Pressable>
48
+
49
+ {/* Progress Bar */}
50
+ <View className="flex-1 h-1 bg-muted rounded-full overflow-hidden">
51
+ <Animated.View
52
+ className="h-full bg-primary rounded-full"
53
+ style={animatedProgressStyle}
54
+ />
55
+ </View>
56
+ </View>
57
+ );
58
+ }
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Demo Expense Tracker Onboarding - Currency Constants
3
+ *
4
+ * Contains currency data for the onboarding demo.
5
+ * This is a self-contained copy to avoid external dependencies.
6
+ */
7
+
8
+ export const SUPPORTED_CURRENCIES = [
9
+ // Major Global Currencies
10
+ { code: 'USD', symbol: '$', name: 'US Dollar' },
11
+ { code: 'EUR', symbol: 'EUR', name: 'Euro' },
12
+ { code: 'GBP', symbol: 'GBP', name: 'British Pound' },
13
+ { code: 'JPY', symbol: 'JPY', name: 'Japanese Yen' },
14
+ { code: 'CHF', symbol: 'CHF', name: 'Swiss Franc' },
15
+ { code: 'CNY', symbol: 'CNY', name: 'Chinese Yuan' },
16
+ { code: 'CAD', symbol: 'CAD', name: 'Canadian Dollar' },
17
+ { code: 'AUD', symbol: 'AUD', name: 'Australian Dollar' },
18
+ { code: 'NZD', symbol: 'NZD', name: 'New Zealand Dollar' },
19
+ { code: 'SGD', symbol: 'SGD', name: 'Singapore Dollar' },
20
+ { code: 'HKD', symbol: 'HKD', name: 'Hong Kong Dollar' },
21
+
22
+ // Asian Currencies
23
+ { code: 'INR', symbol: 'INR', name: 'Indian Rupee' },
24
+ { code: 'PKR', symbol: 'PKR', name: 'Pakistani Rupee' },
25
+ { code: 'BDT', symbol: 'BDT', name: 'Bangladeshi Taka' },
26
+ { code: 'LKR', symbol: 'LKR', name: 'Sri Lankan Rupee' },
27
+ { code: 'THB', symbol: 'THB', name: 'Thai Baht' },
28
+ { code: 'MYR', symbol: 'MYR', name: 'Malaysian Ringgit' },
29
+ { code: 'IDR', symbol: 'IDR', name: 'Indonesian Rupiah' },
30
+ { code: 'PHP', symbol: 'PHP', name: 'Philippine Peso' },
31
+ { code: 'VND', symbol: 'VND', name: 'Vietnamese Dong' },
32
+ { code: 'KRW', symbol: 'KRW', name: 'South Korean Won' },
33
+ { code: 'TWD', symbol: 'TWD', name: 'Taiwan Dollar' },
34
+
35
+ // Middle Eastern Currencies
36
+ { code: 'AED', symbol: 'AED', name: 'UAE Dirham' },
37
+ { code: 'SAR', symbol: 'SAR', name: 'Saudi Riyal' },
38
+ { code: 'QAR', symbol: 'QAR', name: 'Qatari Riyal' },
39
+ { code: 'KWD', symbol: 'KWD', name: 'Kuwaiti Dinar' },
40
+ { code: 'BHD', symbol: 'BHD', name: 'Bahraini Dinar' },
41
+ { code: 'OMR', symbol: 'OMR', name: 'Omani Rial' },
42
+ { code: 'JOD', symbol: 'JOD', name: 'Jordanian Dinar' },
43
+ { code: 'LBP', symbol: 'LBP', name: 'Lebanese Pound' },
44
+ { code: 'EGP', symbol: 'EGP', name: 'Egyptian Pound' },
45
+ { code: 'ILS', symbol: 'ILS', name: 'Israeli Shekel' },
46
+ { code: 'TRY', symbol: 'TRY', name: 'Turkish Lira' },
47
+ { code: 'IRR', symbol: 'IRR', name: 'Iranian Rial' },
48
+
49
+ // European Currencies
50
+ { code: 'SEK', symbol: 'SEK', name: 'Swedish Krona' },
51
+ { code: 'NOK', symbol: 'NOK', name: 'Norwegian Krone' },
52
+ { code: 'DKK', symbol: 'DKK', name: 'Danish Krone' },
53
+ { code: 'PLN', symbol: 'PLN', name: 'Polish Zloty' },
54
+ { code: 'CZK', symbol: 'CZK', name: 'Czech Koruna' },
55
+ { code: 'HUF', symbol: 'HUF', name: 'Hungarian Forint' },
56
+ { code: 'RON', symbol: 'RON', name: 'Romanian Leu' },
57
+ { code: 'BGN', symbol: 'BGN', name: 'Bulgarian Lev' },
58
+ { code: 'HRK', symbol: 'HRK', name: 'Croatian Kuna' },
59
+ { code: 'RUB', symbol: 'RUB', name: 'Russian Ruble' },
60
+ { code: 'UAH', symbol: 'UAH', name: 'Ukrainian Hryvnia' },
61
+ { code: 'BYN', symbol: 'BYN', name: 'Belarusian Ruble' },
62
+
63
+ // Americas Currencies
64
+ { code: 'MXN', symbol: 'MXN', name: 'Mexican Peso' },
65
+ { code: 'BRL', symbol: 'BRL', name: 'Brazilian Real' },
66
+ { code: 'ARS', symbol: 'ARS', name: 'Argentine Peso' },
67
+ { code: 'CLP', symbol: 'CLP', name: 'Chilean Peso' },
68
+ { code: 'COP', symbol: 'COP', name: 'Colombian Peso' },
69
+ { code: 'PEN', symbol: 'PEN', name: 'Peruvian Sol' },
70
+ { code: 'UYU', symbol: 'UYU', name: 'Uruguayan Peso' },
71
+ { code: 'VEF', symbol: 'VEF', name: 'Venezuelan Bolivar' },
72
+
73
+ // African Currencies
74
+ { code: 'ZAR', symbol: 'ZAR', name: 'South African Rand' },
75
+ { code: 'NGN', symbol: 'NGN', name: 'Nigerian Naira' },
76
+ { code: 'GHS', symbol: 'GHS', name: 'Ghanaian Cedi' },
77
+ { code: 'KES', symbol: 'KES', name: 'Kenyan Shilling' },
78
+ { code: 'UGX', symbol: 'UGX', name: 'Ugandan Shilling' },
79
+ { code: 'ETB', symbol: 'ETB', name: 'Ethiopian Birr' },
80
+ { code: 'MAD', symbol: 'MAD', name: 'Moroccan Dirham' },
81
+ { code: 'TND', symbol: 'TND', name: 'Tunisian Dinar' },
82
+ { code: 'ZWL', symbol: 'ZWL', name: 'Zimbabwean Dollar' },
83
+
84
+ // Oceania & Pacific
85
+ { code: 'FJD', symbol: 'FJD', name: 'Fijian Dollar' },
86
+ { code: 'PGK', symbol: 'PGK', name: 'Papua New Guinean Kina' },
87
+ ] as const;
88
+
89
+ /**
90
+ * Currency flag emojis for visual display
91
+ */
92
+ export const CURRENCY_FLAGS: Record<string, string> = {
93
+ USD: '🇺🇸',
94
+ EUR: '🇪🇺',
95
+ GBP: '🇬🇧',
96
+ JPY: '🇯🇵',
97
+ CHF: '🇨🇭',
98
+ CNY: '🇨🇳',
99
+ CAD: '🇨🇦',
100
+ AUD: '🇦🇺',
101
+ NZD: '🇳🇿',
102
+ SGD: '🇸🇬',
103
+ HKD: '🇭🇰',
104
+ INR: '🇮🇳',
105
+ PKR: '🇵🇰',
106
+ BDT: '🇧🇩',
107
+ LKR: '🇱🇰',
108
+ THB: '🇹🇭',
109
+ MYR: '🇲🇾',
110
+ IDR: '🇮🇩',
111
+ PHP: '🇵🇭',
112
+ VND: '🇻🇳',
113
+ KRW: '🇰🇷',
114
+ TWD: '🇹🇼',
115
+ AED: '🇦🇪',
116
+ SAR: '🇸🇦',
117
+ QAR: '🇶🇦',
118
+ KWD: '🇰🇼',
119
+ BHD: '🇧🇭',
120
+ OMR: '🇴🇲',
121
+ JOD: '🇯🇴',
122
+ LBP: '🇱🇧',
123
+ EGP: '🇪🇬',
124
+ ILS: '🇮🇱',
125
+ TRY: '🇹🇷',
126
+ IRR: '🇮🇷',
127
+ SEK: '🇸🇪',
128
+ NOK: '🇳🇴',
129
+ DKK: '🇩🇰',
130
+ PLN: '🇵🇱',
131
+ CZK: '🇨🇿',
132
+ HUF: '🇭🇺',
133
+ RON: '🇷🇴',
134
+ BGN: '🇧🇬',
135
+ HRK: '🇭🇷',
136
+ RUB: '🇷🇺',
137
+ UAH: '🇺🇦',
138
+ BYN: '🇧🇾',
139
+ MXN: '🇲🇽',
140
+ BRL: '🇧🇷',
141
+ ARS: '🇦🇷',
142
+ CLP: '🇨🇱',
143
+ COP: '🇨🇴',
144
+ PEN: '🇵🇪',
145
+ UYU: '🇺🇾',
146
+ VEF: '🇻🇪',
147
+ ZAR: '🇿🇦',
148
+ NGN: '🇳🇬',
149
+ GHS: '🇬🇭',
150
+ KES: '🇰🇪',
151
+ UGX: '🇺🇬',
152
+ ETB: '🇪🇹',
153
+ MAD: '🇲🇦',
154
+ TND: '🇹🇳',
155
+ ZWL: '🇿🇼',
156
+ FJD: '🇫🇯',
157
+ PGK: '🇵🇬',
158
+ };
159
+
160
+ export function getCurrencyFlag(code: string): string {
161
+ return CURRENCY_FLAGS[code] || code;
162
+ }
163
+
164
+ export type SupportedCurrency = (typeof SUPPORTED_CURRENCIES)[number];
165
+ export type CurrencyCode = SupportedCurrency['code'];
166
+
167
+ /**
168
+ * Currency symbols (derived from SUPPORTED_CURRENCIES)
169
+ */
170
+ export const CURRENCY_SYMBOLS: Record<string, string> = Object.fromEntries(
171
+ SUPPORTED_CURRENCIES.map((c) => [c.code, c.symbol]),
172
+ );
173
+
174
+ /**
175
+ * Get currency symbol for a currency code
176
+ */
177
+ export function getCurrencySymbol(currency: string): string {
178
+ return CURRENCY_SYMBOLS[currency] || currency;
179
+ }