vibefast-cli 1.1.5 → 1.2.1

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 (299) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +63 -169
  3. package/dist/commands/add.d.ts +1 -1
  4. package/dist/commands/add.d.ts.map +1 -1
  5. package/dist/commands/add.js +547 -589
  6. package/dist/commands/add.js.map +1 -1
  7. package/dist/commands/checklist.d.ts +1 -1
  8. package/dist/commands/checklist.d.ts.map +1 -1
  9. package/dist/commands/checklist.js +40 -39
  10. package/dist/commands/checklist.js.map +1 -1
  11. package/dist/commands/doctor.d.ts +1 -1
  12. package/dist/commands/doctor.js +22 -22
  13. package/dist/commands/doctor.js.map +1 -1
  14. package/dist/commands/env.d.ts +1 -1
  15. package/dist/commands/env.d.ts.map +1 -1
  16. package/dist/commands/env.js +58 -53
  17. package/dist/commands/env.js.map +1 -1
  18. package/dist/commands/health.d.ts +1 -1
  19. package/dist/commands/health.d.ts.map +1 -1
  20. package/dist/commands/health.js +101 -93
  21. package/dist/commands/health.js.map +1 -1
  22. package/dist/commands/init.d.ts +1 -1
  23. package/dist/commands/init.d.ts.map +1 -1
  24. package/dist/commands/init.js +416 -296
  25. package/dist/commands/init.js.map +1 -1
  26. package/dist/commands/remove.d.ts +1 -1
  27. package/dist/commands/remove.d.ts.map +1 -1
  28. package/dist/commands/remove.js +77 -64
  29. package/dist/commands/remove.js.map +1 -1
  30. package/dist/commands/status.d.ts +1 -1
  31. package/dist/commands/status.d.ts.map +1 -1
  32. package/dist/commands/status.js +15 -14
  33. package/dist/commands/status.js.map +1 -1
  34. package/dist/core/__tests__/detect.test.js +68 -34
  35. package/dist/core/__tests__/detect.test.js.map +1 -1
  36. package/dist/core/ast.d.ts +14 -0
  37. package/dist/core/ast.d.ts.map +1 -0
  38. package/dist/core/ast.js +239 -0
  39. package/dist/core/ast.js.map +1 -0
  40. package/dist/core/codemod.d.ts.map +1 -1
  41. package/dist/core/codemod.js +62 -44
  42. package/dist/core/codemod.js.map +1 -1
  43. package/dist/core/config.d.ts +10 -0
  44. package/dist/core/config.d.ts.map +1 -0
  45. package/dist/core/config.js +51 -0
  46. package/dist/core/config.js.map +1 -0
  47. package/dist/core/detect.d.ts +8 -2
  48. package/dist/core/detect.d.ts.map +1 -1
  49. package/dist/core/detect.js +52 -21
  50. package/dist/core/detect.js.map +1 -1
  51. package/dist/core/errors.d.ts.map +1 -1
  52. package/dist/core/errors.js +9 -8
  53. package/dist/core/errors.js.map +1 -1
  54. package/dist/core/exec.d.ts +16 -0
  55. package/dist/core/exec.d.ts.map +1 -0
  56. package/dist/core/exec.js +48 -0
  57. package/dist/core/exec.js.map +1 -0
  58. package/dist/core/manualSteps.d.ts +7 -0
  59. package/dist/core/manualSteps.d.ts.map +1 -0
  60. package/dist/core/manualSteps.js +59 -0
  61. package/dist/core/manualSteps.js.map +1 -0
  62. package/dist/core/paths.d.ts +3 -1
  63. package/dist/core/paths.d.ts.map +1 -1
  64. package/dist/core/paths.js +14 -10
  65. package/dist/core/paths.js.map +1 -1
  66. package/dist/core/spinner.d.ts +1 -1
  67. package/dist/core/spinner.d.ts.map +1 -1
  68. package/dist/core/spinner.js +38 -8
  69. package/dist/core/spinner.js.map +1 -1
  70. package/dist/core/vosk.d.ts.map +1 -1
  71. package/dist/core/vosk.js +50 -39
  72. package/dist/core/vosk.js.map +1 -1
  73. package/docs/manual-testing.md +91 -0
  74. package/package.json +6 -3
  75. package/recipes/audio-recorder/apps/native/src/app/audio-recorder/index.tsx +5 -0
  76. package/recipes/audio-recorder/recipe.json +3 -3
  77. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/audio-player.tsx +301 -0
  78. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/audio-recorder.tsx +373 -0
  79. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/audio-waveform.tsx +270 -0
  80. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/index.ts +4 -0
  81. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/recording-list.tsx +89 -0
  82. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/audio-player-demo.tsx +66 -0
  83. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/audio-recorder-cloud.tsx +68 -0
  84. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/audio-recorder-interview.tsx +102 -0
  85. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/basic.tsx +27 -0
  86. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/index.ts +5 -0
  87. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/with-recording-list-demo.tsx +82 -0
  88. package/recipes/audio-recorder-supabase/packages/backend/src/services/recordings.ts +369 -0
  89. package/recipes/audio-recorder-supabase/packages/backend/supabase/migrations/recordings.sql +70 -0
  90. package/recipes/audio-recorder-supabase/recipe.json +35 -0
  91. package/recipes/audio-recorder-supabase@latest.zip +0 -0
  92. package/recipes/audio-recorder@latest.zip +0 -0
  93. package/recipes/charts/apps/native/src/features/charts/components/bar-chart.tsx +3 -3
  94. package/recipes/charts/apps/native/src/features/charts/components/candlestick-chart.tsx +2 -2
  95. package/recipes/charts/apps/native/src/features/charts/components/chart-card.tsx +5 -5
  96. package/recipes/charts/apps/native/src/features/charts/components/column-chart.tsx +3 -3
  97. package/recipes/charts/apps/native/src/features/charts/components/doughnut-chart.tsx +20 -4
  98. package/recipes/charts/apps/native/src/features/charts/components/line-chart.tsx +7 -6
  99. package/recipes/charts/apps/native/src/features/charts/components/radar-chart.tsx +6 -4
  100. package/recipes/charts/apps/native/src/features/charts/components/radial-bar-chart.tsx +1 -1
  101. package/recipes/charts/apps/native/src/features/charts/components/stacked-bar-chart.tsx +5 -4
  102. package/recipes/charts/recipe.json +4 -13
  103. package/recipes/charts@latest.zip +0 -0
  104. package/recipes/chatbot/apps/native/src/app/chatbot/index.tsx +1 -0
  105. package/recipes/chatbot/apps/native/src/features/chatbot/components/chat-markdown.tsx +86 -86
  106. package/recipes/chatbot/apps/native/src/features/chatbot/components/markdown/code-block.tsx +86 -53
  107. package/recipes/chatbot/recipe.json +26 -92
  108. package/recipes/chatbot-supabase/apps/native/src/api-client/supabase/chatbot.ts +515 -0
  109. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/app/index.tsx +257 -0
  110. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-header-buttons.tsx +59 -0
  111. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-input-bar.tsx +485 -0
  112. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-markdown.tsx +575 -0
  113. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-message-bubble.tsx +223 -0
  114. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-settings-modal.tsx +161 -0
  115. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/image-preview-list.tsx +116 -0
  116. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/markdown/code-block.tsx +165 -0
  117. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/markdown/index.ts +10 -0
  118. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/markdown/table-renderer.tsx +129 -0
  119. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/message-error-boundary.tsx +78 -0
  120. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/message-list.tsx +170 -0
  121. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/model-selector.tsx +283 -0
  122. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/report-content-modal.tsx +188 -0
  123. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/suggested-messages.tsx +67 -0
  124. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/constants/models.ts +20 -0
  125. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/constants/report-reasons.ts +9 -0
  126. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-attachment-cache.ts +142 -0
  127. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-chat-config.ts +458 -0
  128. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-chat-handlers.ts +429 -0
  129. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-chatbot-settings.ts +89 -0
  130. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-conversation.ts +90 -0
  131. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-image-picker.ts +122 -0
  132. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-keyboard-coordinator.ts +161 -0
  133. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-smart-scroll-manager.ts +213 -0
  134. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/models/index.ts +86 -0
  135. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/models/models.ts +162 -0
  136. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/models/providers.ts +62 -0
  137. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/models/types.ts +40 -0
  138. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/services/file-uploader.ts +287 -0
  139. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/services/message-handler-service.ts +189 -0
  140. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/types/index.ts +70 -0
  141. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/utils/chat-telemetry.ts +91 -0
  142. package/recipes/chatbot-supabase/packages/backend/src/services/conversations.ts +243 -0
  143. package/recipes/chatbot-supabase/packages/backend/src/services/messages.ts +327 -0
  144. package/recipes/chatbot-supabase/packages/backend/supabase/functions/chat-stream/index.ts +347 -0
  145. package/recipes/chatbot-supabase/packages/backend/supabase/migrations/chatbot.sql +104 -0
  146. package/recipes/chatbot-supabase/recipe.json +79 -0
  147. package/recipes/chatbot-supabase@latest.zip +0 -0
  148. package/recipes/chatbot.zip +0 -0
  149. package/recipes/chatbot@latest.zip +0 -0
  150. package/recipes/image-analysis/packages/backend/convex/imageAnalysis/index.ts +2 -2
  151. package/recipes/image-analysis/packages/backend/convex/imageAnalysis.ts +0 -1
  152. package/recipes/image-analysis/recipe.json +15 -55
  153. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/analysis-options-screen.tsx +304 -0
  154. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/camera.tsx +221 -0
  155. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/image-capture-screen.tsx +333 -0
  156. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/loading-screen.tsx +214 -0
  157. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/loading.tsx +191 -0
  158. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/results.tsx +137 -0
  159. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/trait-details.tsx +172 -0
  160. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/use-analysis-data.ts +160 -0
  161. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/use-results-screen.ts +151 -0
  162. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/achievement-badge.tsx +77 -0
  163. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/achievement-card.tsx +75 -0
  164. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/achievement-unlocked-modal.tsx +162 -0
  165. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/achievements-section.tsx +44 -0
  166. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/advice-list.tsx +42 -0
  167. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/circular-progress.tsx +233 -0
  168. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/content-card.tsx +38 -0
  169. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/error-state.tsx +42 -0
  170. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/index.ts +9 -0
  171. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/loading-state.tsx +26 -0
  172. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/profile-image.tsx +60 -0
  173. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/results-header.tsx +62 -0
  174. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/score-display.tsx +54 -0
  175. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/share-options-modal.tsx +110 -0
  176. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/traits-grid.tsx +74 -0
  177. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/config/analysis-config.ts +80 -0
  178. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/config/master-analysis-config.ts +157 -0
  179. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/hooks/index.ts +1 -0
  180. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/hooks/use-analysis.ts +38 -0
  181. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/hooks/use-image-analysis.ts +208 -0
  182. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/services/analysis-service.ts +262 -0
  183. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/services/share-service.ts +176 -0
  184. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/services/trait-details-service.ts +289 -0
  185. package/recipes/image-analysis-supabase/packages/backend/src/services/image-analyses.ts +132 -0
  186. package/recipes/image-analysis-supabase/packages/backend/supabase/functions/analyze-image/index.ts +312 -0
  187. package/recipes/image-analysis-supabase/packages/backend/supabase/migrations/image_analysis.sql +42 -0
  188. package/recipes/image-analysis-supabase/recipe.json +57 -0
  189. package/recipes/image-analysis-supabase@latest.zip +0 -0
  190. package/recipes/image-analysis@latest.zip +0 -0
  191. package/recipes/image-generator/apps/native/src/features/image-generator/app/index.tsx +16 -2
  192. package/recipes/image-generator/apps/native/src/features/image-generator/components/image-model-selector.tsx +11 -5
  193. package/recipes/image-generator/apps/native/src/features/image-generator/hooks/use-image-generator.ts +11 -5
  194. package/recipes/image-generator/packages/backend/convex/imageGeneration/index.ts +2 -2
  195. package/recipes/image-generator/recipe.json +16 -39
  196. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/app/_layout.tsx +26 -0
  197. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/app/gallery.tsx +217 -0
  198. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/app/index.tsx +251 -0
  199. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/components/gallery-image.tsx +25 -0
  200. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/components/image-detail-modal.tsx +215 -0
  201. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/components/image-model-selector.tsx +216 -0
  202. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/components/image-placeholder.tsx +26 -0
  203. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/hooks/use-image-gallery.ts +71 -0
  204. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/hooks/use-image-generator-settings.ts +152 -0
  205. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/hooks/use-image-generator.ts +103 -0
  206. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/models/models.ts +66 -0
  207. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/services/image-gallery-service.ts +96 -0
  208. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/services/image-save-service.ts +120 -0
  209. package/recipes/image-generator-supabase/packages/backend/supabase/functions/generate-image/index.ts +291 -0
  210. package/recipes/image-generator-supabase/packages/backend/supabase/migrations/image_generator.sql +71 -0
  211. package/recipes/image-generator-supabase/recipe.json +59 -0
  212. package/recipes/image-generator-supabase@latest.zip +0 -0
  213. package/recipes/image-generator@latest.zip +0 -0
  214. package/recipes/ios-widget/recipe.json +15 -24
  215. package/recipes/ios-widget@latest.zip +0 -0
  216. package/recipes/onboarding/apps/native/src/features/onboarding/analytics/index.ts +9 -0
  217. package/recipes/onboarding/apps/native/src/features/onboarding/components/onboarding-with-analytics.tsx +141 -0
  218. package/recipes/onboarding/apps/native/src/features/onboarding/components/onboarding.tsx +173 -0
  219. package/recipes/onboarding/apps/native/src/features/onboarding/config/onboarding-flow-config.ts +189 -0
  220. package/recipes/onboarding/apps/native/src/features/onboarding/demo-one/app/index.tsx +42 -0
  221. package/recipes/onboarding/apps/native/src/features/onboarding/demo-one/data.ts +32 -0
  222. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/app/index.tsx +43 -0
  223. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/interactive-onboarding.tsx +222 -0
  224. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/ai-tone-step.tsx +133 -0
  225. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/currency-step.tsx +165 -0
  226. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-ai-step.tsx +199 -0
  227. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-chatbot-step.tsx +154 -0
  228. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-manual-step.tsx +156 -0
  229. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-scan-step.tsx +158 -0
  230. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/main-reason-step.tsx +139 -0
  231. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/notification-step.tsx +129 -0
  232. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/overspend-step.tsx +138 -0
  233. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/personalizing-step.tsx +190 -0
  234. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/rating-step.tsx +98 -0
  235. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/reminder-step.tsx +181 -0
  236. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/safety-step.tsx +110 -0
  237. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/struggle-step.tsx +139 -0
  238. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/welcome-step.tsx +217 -0
  239. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/ui/onboarding-header.tsx +58 -0
  240. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/constants.ts +179 -0
  241. package/recipes/onboarding/apps/native/src/features/onboarding/hooks/use-onboarding-analytics.ts +323 -0
  242. package/recipes/onboarding/apps/native/src/features/onboarding/services/onboarding-analytics.ts +432 -0
  243. package/recipes/onboarding/recipe.json +15 -0
  244. package/recipes/onboarding@latest.zip +0 -0
  245. package/recipes/payments/recipe.json +28 -61
  246. package/recipes/payments-supabase/apps/native/src/features/payments/README.md +200 -0
  247. package/recipes/payments-supabase/apps/native/src/features/payments/app/local-paywall.tsx +194 -0
  248. package/recipes/payments-supabase/apps/native/src/features/payments/app/remote-paywall.tsx +79 -0
  249. package/recipes/payments-supabase/apps/native/src/features/payments/components/payment-initializer.tsx +95 -0
  250. package/recipes/payments-supabase/apps/native/src/features/payments/components/paywall-error-state.tsx +60 -0
  251. package/recipes/payments-supabase/apps/native/src/features/payments/components/paywall-local-mode.tsx +116 -0
  252. package/recipes/payments-supabase/apps/native/src/features/payments/components/paywall-product-card.tsx +133 -0
  253. package/recipes/payments-supabase/apps/native/src/features/payments/components/paywall-remote-mode.tsx +146 -0
  254. package/recipes/payments-supabase/apps/native/src/features/payments/hooks/use-entitlement.ts +63 -0
  255. package/recipes/payments-supabase/apps/native/src/features/payments/index.ts +8 -0
  256. package/recipes/payments-supabase/apps/native/src/features/payments/services/revenuecat-adapter.ts +407 -0
  257. package/recipes/payments-supabase/packages/backend/src/services/payments.ts +201 -0
  258. package/recipes/payments-supabase/packages/backend/supabase/migrations/payments.sql +35 -0
  259. package/recipes/payments-supabase/recipe.json +51 -0
  260. package/recipes/payments-supabase@latest.zip +0 -0
  261. package/recipes/payments@latest.zip +0 -0
  262. package/recipes/quiz/apps/native/src/features/quiz/index.tsx +1 -2
  263. package/recipes/quiz/recipe.json +6 -9
  264. package/recipes/quiz@latest.zip +0 -0
  265. package/recipes/tracker-app/apps/native/src/features/tracker-app/app/index.tsx +1 -2
  266. package/recipes/tracker-app/recipe.json +7 -10
  267. package/recipes/tracker-app@latest.zip +0 -0
  268. package/recipes/voice-bot/recipe.json +8 -68
  269. package/recipes/voice-bot.zip +0 -0
  270. package/recipes/voice-bot@latest.zip +0 -0
  271. package/recipes/wake-word/recipe.json +10 -9
  272. package/recipes/wake-word.zip +0 -0
  273. package/recipes/wake-word@latest.zip +0 -0
  274. package/recipes/charts/apps/native/src/app/(root)/(protected)/charts/index.tsx +0 -3
  275. package/recipes/chatbot/packages/backend/convex/lib/rateLimit.ts +0 -100
  276. package/recipes/chatbot/packages/backend/convex/lib/telemetry.ts +0 -29
  277. package/recipes/chatbot/packages/backend/convex/ragKnowledge.ts +0 -0
  278. package/recipes/image-analysis/apps/native/assets/features/image-analyzer/front.jpg +0 -0
  279. package/recipes/image-analysis/apps/native/assets/features/image-analyzer/side.jpg +0 -0
  280. package/recipes/image-analysis/apps/native/assets/features/image-analyzer/threeQuarter.jpg +0 -0
  281. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/_layout.tsx +0 -5
  282. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/analysis-options.tsx +0 -50
  283. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/camera.tsx +0 -2
  284. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/index.tsx +0 -50
  285. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/loading.tsx +0 -50
  286. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/results.tsx +0 -2
  287. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/trait-details.tsx +0 -3
  288. package/recipes/image-analysis/packages/backend/convex/imageAnalysisFunctions.ts +0 -325
  289. package/recipes/image-analysis/packages/backend/convex/lib/ai/imageAnalysisAdapter.ts +0 -200
  290. package/recipes/payments/apps/native/src/app/(root)/(protected)/paywall/index.tsx +0 -74
  291. package/recipes/payments/apps/native/src/app/(root)/(protected)/paywall/local.tsx +0 -25
  292. package/recipes/payments/apps/native/src/app/(root)/(protected)/paywall/remote.tsx +0 -23
  293. package/recipes/quiz/apps/native/src/app/(root)/(protected)/quiz/index.tsx +0 -47
  294. package/recipes/tracker-app/apps/native/src/app/(root)/(protected)/tracker-app/index.tsx +0 -1
  295. package/recipes/voice-bot/apps/native/src/app/(root)/(protected)/voice-bot/index.tsx +0 -27
  296. package/recipes/voice-bot/packages/backend/convex/router.ts +0 -81
  297. /package/recipes/{chatbot/apps/native/src/app/(root)/(protected) → chatbot-supabase/apps/native/src/app}/chatbot/index.tsx +0 -0
  298. /package/recipes/{image-generator/apps/native/src/app/(root)/(protected) → image-generator-supabase/apps/native/src/app}/image-generator/gallery.tsx +0 -0
  299. /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,221 @@
1
+ import { MaterialIcons } from '@expo/vector-icons';
2
+ import { type CameraType, CameraView, useCameraPermissions } from 'expo-camera';
3
+ import { Stack, useLocalSearchParams, useRouter } from 'expo-router';
4
+ import React, { useCallback, useRef, useState } from 'react';
5
+ import { ActivityIndicator, Alert, TouchableOpacity, View } from 'react-native';
6
+
7
+ import { Button, Image, Text } from '@/components/ui';
8
+ import { useCameraCaptureStore } from '@/features/image-analyzer/hooks/use-image-analysis';
9
+
10
+ type CameraScreenParams = {
11
+ stepId: string;
12
+ stepLabel: string;
13
+ stepDescription: string;
14
+ guideText: string;
15
+ };
16
+
17
+ /**
18
+ * Dedicated camera screen for capturing images with frame overlay guide.
19
+ * Based on coin-identifier-real camera implementation.
20
+ */
21
+ export default function CameraScreen() {
22
+ const router = useRouter();
23
+ const params = useLocalSearchParams() as CameraScreenParams;
24
+ const setCapturedImage = useCameraCaptureStore(
25
+ (state) => state.setCapturedImage,
26
+ );
27
+ const [permission, requestPermission] = useCameraPermissions();
28
+ const [facing, setFacing] = useState<CameraType>('back');
29
+ const [capturedImageUri, setCapturedImageUri] = useState<string | null>(null);
30
+ const [isCapturing, setIsCapturing] = useState(false);
31
+ const cameraRef = useRef<CameraView>(null);
32
+
33
+ // Handle photo capture
34
+ const handleTakePhoto = useCallback(async () => {
35
+ if (!cameraRef.current || isCapturing) return;
36
+
37
+ try {
38
+ setIsCapturing(true);
39
+ const photo = await cameraRef.current.takePictureAsync({
40
+ quality: 0.8,
41
+ });
42
+
43
+ if (photo?.uri) {
44
+ setCapturedImageUri(photo.uri);
45
+ }
46
+ } catch (error) {
47
+ console.error('Error taking photo:', error);
48
+ Alert.alert('Error', 'Failed to take photo. Please try again.');
49
+ } finally {
50
+ setIsCapturing(false);
51
+ }
52
+ }, [isCapturing]);
53
+
54
+ // Handle retake
55
+ const handleRetake = useCallback(() => {
56
+ setCapturedImageUri(null);
57
+ }, []);
58
+
59
+ // Handle accept photo
60
+ const handleAccept = useCallback(() => {
61
+ if (capturedImageUri) {
62
+ // Store the captured image in Zustand store
63
+ setCapturedImage(capturedImageUri, params.stepId);
64
+ // Simply navigate back
65
+ router.back();
66
+ }
67
+ }, [capturedImageUri, setCapturedImage, params.stepId, router]);
68
+
69
+ // Toggle camera facing
70
+ const toggleCameraFacing = useCallback(() => {
71
+ setFacing((current) => (current === 'back' ? 'front' : 'back'));
72
+ }, []);
73
+
74
+ // Check camera permissions
75
+ if (!permission) {
76
+ return (
77
+ <View className="flex-1 items-center justify-center bg-black">
78
+ <ActivityIndicator size="large" color="white" />
79
+ </View>
80
+ );
81
+ }
82
+
83
+ if (!permission.granted) {
84
+ return (
85
+ <View className="flex-1 items-center justify-center bg-black px-6">
86
+ <Text className="mb-4 text-center text-white">
87
+ Camera permission is required to take photos
88
+ </Text>
89
+ <Button
90
+ label="Grant Permission"
91
+ onPress={requestPermission}
92
+ variant="outline"
93
+ />
94
+ </View>
95
+ );
96
+ }
97
+
98
+ // Render photo preview
99
+ if (capturedImageUri) {
100
+ return (
101
+ <>
102
+ <Stack.Screen options={{ headerShown: false }} />
103
+ <View className="flex-1 bg-black">
104
+ <Image
105
+ source={{ uri: capturedImageUri }}
106
+ className="flex-1"
107
+ contentFit="contain"
108
+ />
109
+
110
+ {/* Overlay with step info */}
111
+ <View className="absolute inset-x-0 top-12 px-6">
112
+ <View className="rounded-xl bg-black/70 p-4">
113
+ <Text className="text-center text-lg font-bold text-white">
114
+ {params.stepLabel}
115
+ </Text>
116
+ <Text className="text-center text-sm text-white/80">
117
+ {params.stepDescription}
118
+ </Text>
119
+ </View>
120
+ </View>
121
+
122
+ {/* Action buttons */}
123
+ <View className="absolute inset-x-0 bottom-12 flex-row justify-center gap-x-4 px-6">
124
+ <Button
125
+ label="Retake"
126
+ variant="outline"
127
+ onPress={handleRetake}
128
+ className="flex-1 border-slate-500 bg-slate-800"
129
+ textClassName="text-black"
130
+ />
131
+ <Button
132
+ label="Accept"
133
+ onPress={handleAccept}
134
+ className="flex-1"
135
+ style={{ backgroundColor: '#059669' }}
136
+ />
137
+ </View>
138
+ </View>
139
+ </>
140
+ );
141
+ }
142
+
143
+ // Render camera view
144
+ return (
145
+ <>
146
+ <Stack.Screen options={{ headerShown: false }} />
147
+ <View className="flex-1 bg-black">
148
+ <CameraView
149
+ ref={cameraRef}
150
+ style={{ flex: 1 }}
151
+ facing={facing}
152
+ mode="picture"
153
+ >
154
+ {/* Header with step info */}
155
+ <View className="absolute inset-x-0 top-12 px-6">
156
+ <View className="rounded-xl bg-black/70 p-4">
157
+ <Text className="text-center text-lg font-bold text-white">
158
+ {params.stepLabel}
159
+ </Text>
160
+ <Text className="text-center text-sm text-white/80">
161
+ 📸 {params.guideText}
162
+ </Text>
163
+ </View>
164
+ </View>
165
+
166
+ {/* Frame guide overlay */}
167
+ <View className="absolute inset-0 items-center justify-center">
168
+ <View className="relative size-80">
169
+ {/* Frame */}
170
+ <View className="absolute inset-0 rounded-2xl border-2 border-white/60" />
171
+
172
+ {/* Corner guides */}
173
+ <View className="absolute left-2 top-2 size-6 border-l-4 border-t-4 border-white" />
174
+ <View className="absolute right-2 top-2 size-6 border-r-4 border-t-4 border-white" />
175
+ <View className="absolute bottom-2 left-2 size-6 border-b-4 border-l-4 border-white" />
176
+ <View className="absolute bottom-2 right-2 size-6 border-b-4 border-r-4 border-white" />
177
+
178
+ {/* Center guide */}
179
+ <View className="absolute inset-0 items-center justify-center">
180
+ <View className="h-1 w-8 bg-white/60" />
181
+ <View className="absolute h-8 w-1 bg-white/60" />
182
+ </View>
183
+ </View>
184
+ </View>
185
+
186
+ {/* Controls */}
187
+ <View className="absolute inset-x-0 bottom-12 flex-row items-center justify-center px-6">
188
+ {/* Close button */}
189
+ <TouchableOpacity
190
+ onPress={() => router.back()}
191
+ className="absolute left-6 size-12 items-center justify-center rounded-full bg-black/50"
192
+ >
193
+ <Text className="text-xl text-white">✕</Text>
194
+ </TouchableOpacity>
195
+
196
+ {/* Shutter button */}
197
+ <TouchableOpacity
198
+ onPress={handleTakePhoto}
199
+ disabled={isCapturing}
200
+ className="size-20 items-center justify-center rounded-full border-4 border-white bg-white/20"
201
+ >
202
+ {isCapturing ? (
203
+ <ActivityIndicator size="large" color="white" />
204
+ ) : (
205
+ <View className="size-16 rounded-full bg-white" />
206
+ )}
207
+ </TouchableOpacity>
208
+
209
+ {/* Flip camera button */}
210
+ <TouchableOpacity
211
+ onPress={toggleCameraFacing}
212
+ className="absolute right-6 size-12 items-center justify-center rounded-full bg-black/50"
213
+ >
214
+ <MaterialIcons name="cameraswitch" size={24} color="white" />
215
+ </TouchableOpacity>
216
+ </View>
217
+ </CameraView>
218
+ </View>
219
+ </>
220
+ );
221
+ }
@@ -0,0 +1,333 @@
1
+ import * as ImagePicker from 'expo-image-picker';
2
+ import { Stack, useRouter } from 'expo-router';
3
+ import React, { useCallback, useEffect, useState } from 'react';
4
+ import { Alert, ScrollView, TouchableOpacity, View } from 'react-native';
5
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
6
+
7
+ import { Button, Image, Text } from '@/components/ui';
8
+ import type { AnalysisFlowConfig } from '@/features/image-analyzer/config/master-analysis-config';
9
+ import { useCameraCaptureStore } from '@/features/image-analyzer/hooks/use-image-analysis';
10
+ import { translate } from '@/lib';
11
+
12
+ export type CapturedImage = {
13
+ uri: string;
14
+ stepId: string;
15
+ };
16
+
17
+ type ImageCaptureScreenProps = {
18
+ config: AnalysisFlowConfig;
19
+ };
20
+
21
+ /**
22
+ * Multi-step image capture screen that works with any analysis configuration.
23
+ * Guides users through capturing photos based on the provided config.
24
+ */
25
+ export function ImageCaptureScreen({ config }: ImageCaptureScreenProps) {
26
+ const router = useRouter();
27
+ const { capturedImageUri, stepId, clearCapturedImage } =
28
+ useCameraCaptureStore();
29
+ const [currentStepIndex, setCurrentStepIndex] = useState(0);
30
+ const [capturedImages, setCapturedImages] = useState<
31
+ (CapturedImage | null)[]
32
+ >(new Array(config.photoSteps.length).fill(null));
33
+
34
+ const currentStep = config.photoSteps[currentStepIndex];
35
+ const allImagesCaptured = capturedImages.every((img) => img !== null);
36
+
37
+ // Handle incoming captured image from camera screen via Zustand store
38
+ useEffect(() => {
39
+ if (capturedImageUri && stepId) {
40
+ // Find which step this image belongs to
41
+ const capturedStepIndex = config.photoSteps.findIndex(
42
+ (step) => step.id === stepId,
43
+ );
44
+
45
+ if (capturedStepIndex === -1) {
46
+ console.error('❌ Invalid stepId:', stepId);
47
+ clearCapturedImage();
48
+ return;
49
+ }
50
+
51
+ // Update the captured images array
52
+ setCapturedImages((prevImages) => {
53
+ const newCapturedImages = [...prevImages];
54
+ newCapturedImages[capturedStepIndex] = {
55
+ uri: capturedImageUri,
56
+ stepId: stepId,
57
+ };
58
+ return newCapturedImages;
59
+ });
60
+
61
+ // Only auto-advance if we're currently viewing the step we just captured
62
+ // This prevents jumping when user manually navigates to previous steps
63
+ setCurrentStepIndex((currentIndex) => {
64
+ if (currentIndex === capturedStepIndex) {
65
+ // We're on the step we just captured, advance to next if not last
66
+ const isLastStep = capturedStepIndex === config.photoSteps.length - 1;
67
+ return isLastStep ? capturedStepIndex : capturedStepIndex + 1;
68
+ }
69
+ // User is viewing a different step, don't auto-advance
70
+ return currentIndex;
71
+ });
72
+
73
+ clearCapturedImage();
74
+ }
75
+ }, [capturedImageUri, stepId, clearCapturedImage, config.photoSteps]);
76
+
77
+ // Handle camera launch
78
+ const handleOpenCamera = useCallback(async () => {
79
+ try {
80
+ const { status } = await ImagePicker.requestCameraPermissionsAsync();
81
+ if (status !== 'granted') {
82
+ Alert.alert(
83
+ 'Permission Required',
84
+ 'Camera permission is required to take photos.',
85
+ );
86
+ return;
87
+ }
88
+
89
+ router.push({
90
+ pathname: '/analysis/[type]/camera' as any,
91
+ params: {
92
+ type: config.id,
93
+ stepId: currentStep.id,
94
+ stepLabel: currentStep.label,
95
+ stepDescription: currentStep.description,
96
+ },
97
+ });
98
+ } catch (error) {
99
+ console.error('Error requesting camera permission:', error);
100
+ Alert.alert('Error', 'Failed to open camera. Please try again.');
101
+ }
102
+ }, [currentStep, router, config.id]);
103
+
104
+ // Handle gallery selection
105
+ const handleSelectFromGallery = useCallback(async () => {
106
+ try {
107
+ const { status } =
108
+ await ImagePicker.requestMediaLibraryPermissionsAsync();
109
+ if (status !== 'granted') {
110
+ Alert.alert(
111
+ 'Permission Required',
112
+ 'Photo library permission is required to select images.',
113
+ );
114
+ return;
115
+ }
116
+
117
+ const result = await ImagePicker.launchImageLibraryAsync({
118
+ mediaTypes: ImagePicker.MediaTypeOptions.Images,
119
+ allowsEditing: true,
120
+ aspect: [4, 3],
121
+ quality: 0.8,
122
+ });
123
+
124
+ if (!result.canceled && result.assets[0]) {
125
+ const imageUri = result.assets[0].uri;
126
+
127
+ setCapturedImages((prevImages) => {
128
+ const newCapturedImages = [...prevImages];
129
+ newCapturedImages[currentStepIndex] = {
130
+ uri: imageUri,
131
+ stepId: currentStep.id,
132
+ };
133
+ return newCapturedImages;
134
+ });
135
+
136
+ // Auto-advance to next step if not last
137
+ const isLastStep = currentStepIndex === config.photoSteps.length - 1;
138
+ if (!isLastStep) {
139
+ setCurrentStepIndex(currentStepIndex + 1);
140
+ }
141
+ }
142
+ } catch (error) {
143
+ console.error('Error selecting from gallery:', error);
144
+ Alert.alert('Error', 'Failed to select image. Please try again.');
145
+ }
146
+ }, [currentStepIndex, currentStep.id, config.photoSteps.length]);
147
+
148
+ // Handle step switching - allow clicking any step freely
149
+ const handleStepSwitch = useCallback((targetIndex: number) => {
150
+ setCurrentStepIndex(targetIndex);
151
+ }, []);
152
+
153
+ // Handle continue - either to options or directly to loading based on config
154
+ const handleContinue = useCallback(() => {
155
+ const imageUris = capturedImages.map((img) => img!.uri);
156
+
157
+ if (config.skipOptionsScreen) {
158
+ // Go directly to loading with default preferences
159
+ router.push({
160
+ pathname: '/analysis/[type]/loading' as any,
161
+ params: {
162
+ type: config.id,
163
+ imageUris: JSON.stringify(imageUris),
164
+ analysisGoal: config.defaultPreferences?.goal || 'comprehensive',
165
+ feedbackStyle:
166
+ config.defaultPreferences?.feedbackStyle || 'professional',
167
+ },
168
+ });
169
+ } else {
170
+ // Go to options screen
171
+ router.push({
172
+ pathname: '/analysis/[type]/analysis-options' as any,
173
+ params: {
174
+ type: config.id,
175
+ imageUris: JSON.stringify(imageUris),
176
+ },
177
+ });
178
+ }
179
+ }, [capturedImages, router, config]);
180
+
181
+ const insets = useSafeAreaInsets();
182
+
183
+ return (
184
+ <>
185
+ <Stack.Screen
186
+ options={{
187
+ title: `${config.name} ${translate('analysis.setup_title')}`,
188
+ headerBackButtonDisplayMode: 'generic',
189
+ }}
190
+ />
191
+ <ScrollView
192
+ className="flex-1"
193
+ contentContainerStyle={{ paddingBottom: 140 + insets.bottom }}
194
+ showsVerticalScrollIndicator={false}
195
+ >
196
+ <View className="px-6 pt-4">
197
+ {/* Connected Step Progress Indicator */}
198
+ <View className="mb-8">
199
+ <View className="relative flex-row items-center justify-between">
200
+ {config.photoSteps.map((_, index) => (
201
+ <React.Fragment key={index}>
202
+ {/* Step Circle */}
203
+ <TouchableOpacity
204
+ onPress={() => handleStepSwitch(index)}
205
+ className={`relative z-10 size-16 items-center justify-center rounded-full border-2 ${
206
+ capturedImages[index] !== null
207
+ ? 'border-green-500 bg-green-500'
208
+ : index === currentStepIndex
209
+ ? 'border-primary-600 bg-primary-600'
210
+ : 'border-neutral-300 bg-white dark:border-neutral-600 dark:bg-neutral-800'
211
+ }`}
212
+ >
213
+ {capturedImages[index] !== null ? (
214
+ <Text className="text-2xl font-bold text-white">✓</Text>
215
+ ) : index === currentStepIndex ? (
216
+ <Text className="text-xl font-semibold text-white">
217
+ {index + 1}
218
+ </Text>
219
+ ) : (
220
+ <Text className="text-xl font-semibold text-neutral-500 dark:text-neutral-400">
221
+ {index + 1}
222
+ </Text>
223
+ )}
224
+ </TouchableOpacity>
225
+
226
+ {/* Connecting Line */}
227
+ {index < config.photoSteps.length - 1 && (
228
+ <View
229
+ className={`mx-3 h-0.5 flex-1 ${
230
+ capturedImages[index] !== null
231
+ ? 'bg-green-500'
232
+ : index < currentStepIndex
233
+ ? 'bg-primary-600'
234
+ : 'bg-neutral-300 dark:bg-neutral-600'
235
+ }`}
236
+ />
237
+ )}
238
+ </React.Fragment>
239
+ ))}
240
+ </View>
241
+
242
+ {/* Step Labels */}
243
+ {/* <View className="mt-4 flex-row justify-between px-2">
244
+ {config.photoSteps.map((step, index) => (
245
+ <View key={step.id} className="w-28 items-center">
246
+ <Text
247
+ numberOfLines={1}
248
+ className={`text-center text-xs font-medium ${
249
+ capturedImages[index] !== null
250
+ ? 'text-green-600'
251
+ : index === currentStepIndex
252
+ ? 'text-primary-600'
253
+ : 'text-neutral-500 dark:text-neutral-400'
254
+ }`}
255
+ >
256
+ {step.label}
257
+ </Text>
258
+ </View>
259
+ ))}
260
+ </View> */}
261
+ </View>
262
+
263
+ {/* Current Step Info */}
264
+ <View className="mb-8 rounded-3xl bg-card p-5 dark:bg-card">
265
+ <View className="mb-1 ml-2 flex-row items-center">
266
+ <Text className="flex-1 text-lg font-semibold text-neutral-900 dark:text-neutral-100">
267
+ {currentStep.label}
268
+ </Text>
269
+ </View>
270
+ <Text className="mb-4 ml-2 text-neutral-600 dark:text-neutral-300">
271
+ {currentStep.description}
272
+ </Text>
273
+
274
+ {/* Guide Image or Captured Image */}
275
+ <Image
276
+ source={
277
+ capturedImages[currentStepIndex]
278
+ ? { uri: capturedImages[currentStepIndex]!.uri }
279
+ : currentStep.guideImage
280
+ }
281
+ className="aspect-square w-full rounded-3xl"
282
+ contentFit="cover"
283
+ />
284
+ </View>
285
+ </View>
286
+ </ScrollView>
287
+
288
+ {/* Bottom Action Bar */}
289
+ <View
290
+ className="absolute inset-x-0 bottom-0 bg-white px-6 py-4 shadow-lg dark:bg-neutral-900"
291
+ style={{ paddingBottom: insets.bottom }}
292
+ >
293
+ {/* Action Buttons */}
294
+ <View className="mb-1 flex-row gap-x-3">
295
+ <Button
296
+ onPress={handleOpenCamera}
297
+ label={
298
+ capturedImages[currentStepIndex] ? 'Retake Photo' : 'Take Photo'
299
+ }
300
+ className="flex-1 bg-primary-600"
301
+ textClassName="text-white font-semibold"
302
+ />
303
+
304
+ <Button
305
+ onPress={handleSelectFromGallery}
306
+ label="Gallery"
307
+ variant="outline"
308
+ className="flex-1 border-neutral-300 dark:border-neutral-600"
309
+ textClassName="text-neutral-700 dark:text-neutral-300 text-center"
310
+ />
311
+ </View>
312
+
313
+ <Button
314
+ onPress={handleContinue}
315
+ disabled={!allImagesCaptured}
316
+ label={
317
+ config.skipOptionsScreen ? 'Start Analysis' : 'Continue to Options'
318
+ }
319
+ className={`${
320
+ allImagesCaptured
321
+ ? 'bg-neutral-800'
322
+ : 'bg-neutral-300 dark:bg-neutral-700'
323
+ }`}
324
+ textClassName={`font-semibold ${
325
+ allImagesCaptured
326
+ ? 'text-white'
327
+ : 'text-neutral-500 dark:text-neutral-400'
328
+ }`}
329
+ />
330
+ </View>
331
+ </>
332
+ );
333
+ }