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
@@ -1,325 +0,0 @@
1
- import { getAuthUserId } from '@convex-dev/auth/server';
2
- import { v } from 'convex/values';
3
-
4
- import { internal } from './_generated/api';
5
- import type { Id } from './_generated/dataModel';
6
- import {
7
- action,
8
- internalAction,
9
- internalMutation,
10
- internalQuery,
11
- query,
12
- } from './_generated/server';
13
- import { generateImageAnalysis } from './lib/ai/imageAnalysisAdapter';
14
-
15
- /**
16
- * Public action to start image analysis
17
- */
18
- export const analyzeImages = action({
19
- args: {
20
- imagesAsBase64: v.array(v.string()),
21
- preferences: v.object({
22
- goal: v.string(),
23
- feedbackStyle: v.string(),
24
- }),
25
- analysisConfigId: v.optional(v.string()), // Optional for backward compatibility
26
- },
27
- handler: async (ctx, args): Promise<Id<'imageAnalyses'>> => {
28
- const userId = await getAuthUserId(ctx);
29
- if (!userId) {
30
- throw new Error('Not authenticated');
31
- }
32
-
33
- // Use default config if not provided (for backward compatibility)
34
- const configId = args.analysisConfigId || 'face_analysis';
35
-
36
- // Store images in Convex storage
37
- const imageStorageIds: Id<'_storage'>[] = [];
38
- for (const base64Image of args.imagesAsBase64) {
39
- const uint8Array = new Uint8Array(Buffer.from(base64Image, 'base64'));
40
- const blob = new Blob([uint8Array], { type: 'image/jpeg' });
41
- const storageId = await ctx.storage.store(blob);
42
- imageStorageIds.push(storageId);
43
- }
44
-
45
- // Create analysis record
46
- const analysisId = await ctx.runMutation(
47
- internal.imageAnalysisFunctions.createAnalysisRecord,
48
- {
49
- userId,
50
- imageStorageIds,
51
- analysisConfigId: configId,
52
- preferences: args.preferences,
53
- },
54
- );
55
-
56
- // Schedule background AI analysis
57
- await ctx.scheduler.runAfter(
58
- 0,
59
- internal.imageAnalysisFunctions.performAiAnalysis,
60
- {
61
- analysisId,
62
- },
63
- );
64
-
65
- return analysisId;
66
- },
67
- });
68
-
69
- /**
70
- * Internal mutation to create analysis record
71
- */
72
- export const createAnalysisRecord = internalMutation({
73
- args: {
74
- userId: v.id('users'),
75
- imageStorageIds: v.array(v.id('_storage')),
76
- analysisConfigId: v.optional(v.string()),
77
- preferences: v.object({
78
- goal: v.string(),
79
- feedbackStyle: v.string(),
80
- }),
81
- },
82
- handler: async (ctx, args) => {
83
- return await ctx.db.insert('imageAnalyses', {
84
- userId: args.userId,
85
- status: 'processing',
86
- imageStorageIds: args.imageStorageIds,
87
- analysisConfigId: args.analysisConfigId,
88
- preferences: args.preferences,
89
- });
90
- },
91
- });
92
-
93
- /**
94
- * Internal action to perform AI analysis
95
- */
96
- export const performAiAnalysis = internalAction({
97
- args: {
98
- analysisId: v.id('imageAnalyses'),
99
- },
100
- handler: async (ctx, args) => {
101
- try {
102
- // Get analysis record
103
- const analysis = await ctx.runQuery(
104
- internal.imageAnalysisFunctions.getAnalysisRecord,
105
- {
106
- analysisId: args.analysisId,
107
- },
108
- );
109
-
110
- if (!analysis) {
111
- throw new Error('Analysis record not found');
112
- }
113
-
114
- // Get analysis configuration (use default if not specified for backward compatibility)
115
- const configId = analysis.analysisConfigId || 'face_analysis';
116
-
117
- // Get image URLs from storage - parallelize for better performance
118
- const imageUrls = await Promise.all(
119
- analysis.imageStorageIds.map(async (storageId) => {
120
- const url = await ctx.storage.getUrl(storageId);
121
- return url;
122
- }),
123
- );
124
-
125
- // Convert image URLs to base64 for AI analysis - parallelize with timeout
126
- const FETCH_TIMEOUT_MS = 30000; // 30 seconds per image
127
- const imagesAsBase64 = await Promise.all(
128
- imageUrls
129
- .filter((url): url is string => url !== null)
130
- .map(async (url) => {
131
- const controller = new AbortController();
132
- const timeoutId = setTimeout(
133
- () => controller.abort(),
134
- FETCH_TIMEOUT_MS,
135
- );
136
- try {
137
- const response = await fetch(url, { signal: controller.signal });
138
- clearTimeout(timeoutId);
139
- if (!response.ok) {
140
- throw new Error(
141
- `Failed to fetch image: ${response.statusText}`,
142
- );
143
- }
144
- const arrayBuffer = await response.arrayBuffer();
145
- return Buffer.from(arrayBuffer).toString('base64');
146
- } catch (error) {
147
- clearTimeout(timeoutId);
148
- if (error instanceof Error && error.name === 'AbortError') {
149
- throw new Error(
150
- `Image fetch timed out after ${FETCH_TIMEOUT_MS}ms`,
151
- );
152
- }
153
- throw error;
154
- }
155
- }),
156
- );
157
-
158
- // Validate we have at least one image
159
- if (imagesAsBase64.length === 0) {
160
- throw new Error('No valid images found for analysis');
161
- }
162
-
163
- // Normalize preferences for backward compatibility
164
- const normalizedPreferences = {
165
- goal:
166
- 'goal' in analysis.preferences
167
- ? analysis.preferences.goal
168
- : (analysis.preferences as any).analysisGoal,
169
- feedbackStyle: analysis.preferences.feedbackStyle,
170
- };
171
-
172
- // Perform AI analysis
173
- const results = await generateImageAnalysis(
174
- imagesAsBase64,
175
- normalizedPreferences,
176
- configId,
177
- );
178
-
179
- // Update analysis record with results
180
- await ctx.runMutation(
181
- internal.imageAnalysisFunctions.updateAnalysisResults,
182
- {
183
- analysisId: args.analysisId,
184
- results,
185
- status: 'completed',
186
- },
187
- );
188
- } catch (error) {
189
- console.error('AI analysis failed:', error);
190
-
191
- // Update analysis record with error
192
- await ctx.runMutation(
193
- internal.imageAnalysisFunctions.updateAnalysisResults,
194
- {
195
- analysisId: args.analysisId,
196
- status: 'failed',
197
- errorMessage:
198
- error instanceof Error ? error.message : 'Unknown error',
199
- },
200
- );
201
- }
202
- },
203
- });
204
-
205
- /**
206
- * Internal query to get analysis record
207
- */
208
- export const getAnalysisRecord = internalQuery({
209
- args: {
210
- analysisId: v.id('imageAnalyses'),
211
- },
212
- handler: async (ctx, args) => {
213
- return await ctx.db.get(args.analysisId);
214
- },
215
- });
216
-
217
- /**
218
- * Internal mutation to update analysis results
219
- */
220
- export const updateAnalysisResults = internalMutation({
221
- args: {
222
- analysisId: v.id('imageAnalyses'),
223
- results: v.optional(v.any()),
224
- status: v.union(v.literal('completed'), v.literal('failed')),
225
- errorMessage: v.optional(v.string()),
226
- },
227
- handler: async (ctx, args) => {
228
- await ctx.db.patch(args.analysisId, {
229
- status: args.status,
230
- results: args.results,
231
- errorMessage: args.errorMessage,
232
- });
233
- },
234
- });
235
-
236
- /**
237
- * Public query to get analysis by ID
238
- */
239
- export const getAnalysisById = query({
240
- args: {
241
- analysisId: v.id('imageAnalyses'),
242
- },
243
- handler: async (ctx, args) => {
244
- const userId = await getAuthUserId(ctx);
245
- if (!userId) {
246
- throw new Error('Not authenticated');
247
- }
248
-
249
- const analysis = await ctx.db.get(args.analysisId);
250
- if (!analysis) {
251
- return null;
252
- }
253
-
254
- // Ensure user owns this analysis
255
- if (analysis.userId !== userId) {
256
- throw new Error('Unauthorized');
257
- }
258
-
259
- return analysis;
260
- },
261
- });
262
-
263
- /**
264
- * Public query to get full analysis with image URLs
265
- */
266
- export const getImageAnalysis = query({
267
- args: {
268
- analysisId: v.id('imageAnalyses'),
269
- },
270
- handler: async (ctx, args) => {
271
- const userId = await getAuthUserId(ctx);
272
- if (!userId) {
273
- throw new Error('Not authenticated');
274
- }
275
-
276
- const analysis = await ctx.db.get(args.analysisId);
277
- if (!analysis) {
278
- return null;
279
- }
280
-
281
- // Ensure user owns this analysis
282
- if (analysis.userId !== userId) {
283
- throw new Error('Unauthorized');
284
- }
285
-
286
- // Get image URLs - parallelize for better performance
287
- const imageUrls = await Promise.all(
288
- analysis.imageStorageIds.map(async (storageId) => {
289
- const url = await ctx.storage.getUrl(storageId);
290
- return url;
291
- }),
292
- );
293
-
294
- return {
295
- ...analysis,
296
- imageUrls: imageUrls.filter((url): url is string => url !== null),
297
- };
298
- },
299
- });
300
-
301
- /**
302
- * Public query to get user's analysis history with pagination
303
- */
304
- export const getUserImageAnalyses = query({
305
- args: {
306
- paginationOpts: v.optional(
307
- v.object({
308
- numItems: v.number(),
309
- cursor: v.union(v.string(), v.null()),
310
- }),
311
- ),
312
- },
313
- handler: async (ctx, args) => {
314
- const userId = await getAuthUserId(ctx);
315
- if (!userId) {
316
- throw new Error('Not authenticated');
317
- }
318
-
319
- return await ctx.db
320
- .query('imageAnalyses')
321
- .withIndex('by_userId', (q) => q.eq('userId', userId))
322
- .order('desc')
323
- .paginate(args.paginationOpts ?? { numItems: 50, cursor: null });
324
- },
325
- });
@@ -1,200 +0,0 @@
1
- 'use node';
2
-
3
- import { createGoogleGenerativeAI } from '@ai-sdk/google';
4
- import { createOpenAI } from '@ai-sdk/openai';
5
- import { generateObject } from 'ai';
6
-
7
- import { getAnalysisConfig } from './analysisConfigs';
8
- import { type AIProvider, getAIConfig } from './config';
9
-
10
- /**
11
- * Generic analysis result type
12
- */
13
- export type GenericAnalysisResult = {
14
- overallScore: number;
15
- overallMessage: string;
16
- confidence: number;
17
- traitScores: Record<
18
- string,
19
- {
20
- score: number;
21
- feedback: string;
22
- strengths: string[];
23
- improvements: string[];
24
- }
25
- >;
26
- recommendations: string[];
27
- analysisType: string;
28
- processingTime: number;
29
- };
30
-
31
- /**
32
- * Truncate an oversized base64 string while preserving a valid encoding.
33
- *
34
- * Base64 strings must have a length that is a multiple of 4 and must end with
35
- * optional padding characters ("="). A naive substring can break the encoding
36
- * and lead to downstream "unsupported image" errors from the OpenAI API. This
37
- * helper trims the string **only** when it exceeds the chosen limit and always
38
- * rounds the length down to the nearest multiple of 4 to keep the data valid.
39
- *
40
- * NOTE: The client already compresses/resize images before upload, so the limit
41
- * here is intentionally generous (4 MB of base64 text ≈ 3 MB binary). This
42
- * should fit within OpenAI's 30 MB image cap while safeguarding Convex memory.
43
- */
44
- function optimizeImageForMemory(base64: string): string {
45
- const MAX_CHARS = 4 * 1024 * 1024; // 4 MB worth of base64 characters
46
-
47
- if (base64.length <= MAX_CHARS) {
48
- return base64;
49
- }
50
-
51
- console.log(
52
- `[Memory Optimization] Truncating image from ${base64.length} to ${MAX_CHARS} chars (safe multiple-of-4)`,
53
- );
54
-
55
- // Round down to nearest multiple of 4 to preserve base64 integrity
56
- const safeLength = MAX_CHARS - (MAX_CHARS % 4);
57
- const truncated = base64.substring(0, safeLength);
58
-
59
- // Ensure proper padding – if the truncated string ends with partial padding
60
- // remove it and re-append correct padding.
61
- const withoutPartialPad = truncated.replace(/=+$/, '');
62
- const padNeeded = (4 - (withoutPartialPad.length % 4)) % 4;
63
- return withoutPartialPad + '='.repeat(padNeeded);
64
- }
65
-
66
- /**
67
- * Generate structured analysis using AI SDK (generic for any analysis type)
68
- */
69
- export async function generateImageAnalysis(
70
- imagesAsBase64: string[],
71
- preferences: {
72
- goal: string;
73
- feedbackStyle: string;
74
- },
75
- analysisConfigId = 'face_analysis',
76
- ): Promise<GenericAnalysisResult> {
77
- const config = getAIConfig();
78
- const analysisConfig = getAnalysisConfig(analysisConfigId);
79
- const startTime = Date.now();
80
-
81
- // Memory optimization: Limit to max 3 images and optimize them
82
- const maxImages = 3;
83
- const optimizedImages = imagesAsBase64
84
- .slice(0, maxImages)
85
- .map(optimizeImageForMemory);
86
-
87
- console.log(
88
- `[Memory Optimization] Processing ${optimizedImages.length} images (max ${maxImages})`,
89
- );
90
-
91
- // Create the appropriate AI provider
92
- const provider = createAIProvider(config.provider);
93
- const model = provider(config.model);
94
-
95
- // Build a simpler, shorter prompt to reduce memory usage
96
- const prompt = buildOptimizedPrompt(
97
- analysisConfig,
98
- preferences,
99
- optimizedImages.length,
100
- );
101
-
102
- // Convert base64 images to the format expected by AI SDK
103
- const imageMessages = optimizedImages.map((base64) => ({
104
- type: 'image' as const,
105
- image: `data:image/jpeg;base64,${base64}`,
106
- }));
107
-
108
- try {
109
- // Generate structured output using AI SDK with reduced complexity
110
- const { object } = await generateObject({
111
- model,
112
- schema: analysisConfig.schema,
113
- messages: [
114
- {
115
- role: 'user',
116
- content: [{ type: 'text', text: prompt }, ...imageMessages],
117
- },
118
- ],
119
- temperature: config.temperature,
120
- maxTokens: Math.min(config.maxTokens, 2000), // Reduce max tokens to save memory
121
- });
122
-
123
- // Add processing metadata
124
- const processingTime = Date.now() - startTime;
125
- return {
126
- ...object,
127
- processingTime,
128
- analysisType: `${analysisConfigId}_${preferences.goal}_${preferences.feedbackStyle}`,
129
- } as GenericAnalysisResult;
130
- } catch (error) {
131
- console.error('Error generating analysis:', error);
132
- throw new Error(
133
- `Failed to generate analysis: ${error instanceof Error ? error.message : 'Unknown error'}`,
134
- );
135
- }
136
- }
137
-
138
- /**
139
- * Build optimized, shorter prompt to reduce memory usage
140
- */
141
- function buildOptimizedPrompt(
142
- analysisConfig: any,
143
- preferences: { goal: string; feedbackStyle: string },
144
- imageCount: number,
145
- ): string {
146
- const traitNames = Object.keys(analysisConfig.traits).slice(0, 6); // Limit traits to reduce complexity
147
-
148
- return `Analyze the ${imageCount} image(s) for ${analysisConfig.name.toLowerCase()}.
149
-
150
- Goal: ${preferences.goal}
151
- Style: ${preferences.feedbackStyle}
152
-
153
- Provide scores (0-100) and feedback for: ${traitNames.join(', ')}.
154
-
155
- Include:
156
- - Overall score and message
157
- - Individual trait scores with feedback
158
- - 2-3 recommendations
159
- - Confidence level (0-1)
160
-
161
- Be concise and ${preferences.feedbackStyle}.`;
162
- }
163
-
164
- /**
165
- * Legacy function for backward compatibility (face analysis)
166
- * @deprecated Use generateImageAnalysis instead
167
- */
168
- export async function generateFacialAnalysis(
169
- imagesAsBase64: string[],
170
- preferences: {
171
- goal: string;
172
- feedbackStyle: string;
173
- },
174
- ): Promise<GenericAnalysisResult> {
175
- return generateImageAnalysis(imagesAsBase64, preferences, 'face_analysis');
176
- }
177
-
178
- /**
179
- * Create AI provider based on configuration
180
- */
181
- function createAIProvider(provider: AIProvider) {
182
- switch (provider) {
183
- case 'openai': {
184
- const apiKey = process.env.OPENAI_API_KEY;
185
- if (!apiKey) {
186
- throw new Error('OPENAI_API_KEY environment variable is required');
187
- }
188
- return createOpenAI({ apiKey });
189
- }
190
- case 'gemini': {
191
- const apiKey = process.env.GEMINI_API_KEY;
192
- if (!apiKey) {
193
- throw new Error('GEMINI_API_KEY environment variable is required');
194
- }
195
- return createGoogleGenerativeAI({ apiKey });
196
- }
197
- default:
198
- throw new Error(`Unsupported AI provider: ${provider}`);
199
- }
200
- }
@@ -1,74 +0,0 @@
1
- import { router, Stack } from 'expo-router';
2
- import React, { useMemo } from 'react';
3
- import { ScrollView, View } from 'react-native';
4
-
5
- import { FeatureButton, FocusAwareStatusBar } from '@/components/ui';
6
- import { translate } from '@/lib';
7
- import { useThemeConfig } from '@/lib/use-theme-config';
8
-
9
- export default function PaywallSelection() {
10
- const theme = useThemeConfig();
11
- const optionsTitle = translate('paywall.options_title');
12
- const screenOptions = useMemo(
13
- () => ({
14
- title: optionsTitle,
15
- headerShown: true,
16
- headerBackButtonDisplayMode: 'generic' as const,
17
- }),
18
- [optionsTitle],
19
- );
20
-
21
- const paywallOptions = [
22
- {
23
- id: 'remote',
24
- title: translate('paywall.remote_title'),
25
- icon: '💳',
26
- color: '#FBBF24', // Amber
27
- description: translate('paywall.remote_description'),
28
- route: '/paywall/remote',
29
- testID: 'remote-paywall-option',
30
- },
31
- {
32
- id: 'local',
33
- title: translate('paywall.local_title'),
34
- icon: '🛒',
35
- color: '#10B981', // Emerald
36
- description: translate('paywall.local_description'),
37
- route: '/paywall/local',
38
- testID: 'local-paywall-option',
39
- },
40
- ];
41
-
42
- const handleOptionPress = (route: string) => {
43
- router.push(route as any);
44
- };
45
-
46
- return (
47
- <>
48
- <FocusAwareStatusBar />
49
- <Stack.Screen options={screenOptions} />
50
- <ScrollView
51
- style={{ backgroundColor: theme.colors.background }}
52
- className="flex-1"
53
- showsVerticalScrollIndicator={false}
54
- >
55
- <View className="px-6 py-8">
56
- <View className="-mx-2 flex-row flex-wrap">
57
- {paywallOptions.map((option) => (
58
- <View key={option.id} className="w-1/2 px-2 pb-4">
59
- <FeatureButton
60
- title={option.title}
61
- icon={option.icon}
62
- color={option.color}
63
- description={option.description}
64
- testID={option.testID}
65
- onPress={() => handleOptionPress(option.route)}
66
- />
67
- </View>
68
- ))}
69
- </View>
70
- </View>
71
- </ScrollView>
72
- </>
73
- );
74
- }
@@ -1,25 +0,0 @@
1
- import { Stack } from 'expo-router';
2
- import React, { useMemo } from 'react';
3
-
4
- import LocalPaywall from '@/features/payments/app/local-paywall';
5
- import { RevenueCatAdapter } from '@/features/payments/services/revenuecat-adapter';
6
- import { translate } from '@/lib';
7
-
8
- export default function LocalPaywallScreen() {
9
- const paymentService = useMemo(() => new RevenueCatAdapter(), []);
10
- const localTitle = translate('paywall.local_title');
11
- const screenOptions = useMemo(
12
- () => ({
13
- title: localTitle,
14
- headerShown: false,
15
- }),
16
- [localTitle],
17
- );
18
-
19
- return (
20
- <>
21
- <Stack.Screen options={screenOptions} />
22
- <LocalPaywall paymentService={paymentService} />
23
- </>
24
- );
25
- }
@@ -1,23 +0,0 @@
1
- import { Stack } from 'expo-router';
2
- import React, { useMemo } from 'react';
3
-
4
- import RemotePaywall from '@/features/payments/app/remote-paywall';
5
- import { translate } from '@/lib';
6
-
7
- export default function RemotePaywallScreen() {
8
- const remoteTitle = translate('paywall.remote_title');
9
- const screenOptions = useMemo(
10
- () => ({
11
- title: remoteTitle,
12
- headerShown: true,
13
- }),
14
- [remoteTitle],
15
- );
16
-
17
- return (
18
- <>
19
- <Stack.Screen options={screenOptions} />
20
- <RemotePaywall />
21
- </>
22
- );
23
- }
@@ -1,47 +0,0 @@
1
- import Quiz from '@/features/quiz';
2
- import { QuestionMode } from '@/features/quiz/config';
3
-
4
- const questions = [
5
- {
6
- text: 'What is your favorite color?',
7
- mode: QuestionMode.AutoAdvance,
8
- options: [
9
- { id: 'red', text: 'Red', note: 'Red is the color of passion.' },
10
- { id: 'blue', text: 'Blue', note: 'Blue is the color of calm.' },
11
- { id: 'green', text: 'Green', note: 'Green is the color of nature.' },
12
- ],
13
- },
14
- {
15
- text: 'What is your favorite animal?',
16
- mode: QuestionMode.ManualAdvance,
17
- options: [
18
- { id: 'dog', text: 'Dog', note: 'Dogs are loyal.' },
19
- { id: 'cat', text: 'Cat', note: 'Cats are independent.' },
20
- { id: 'bird', text: 'Bird', note: 'Birds can fly.' },
21
- ],
22
- },
23
- {
24
- text: 'What is your favorite animal?',
25
- mode: QuestionMode.ManualAdvance,
26
- options: [
27
- { id: 'dog', text: 'Dog', note: 'Dogs are loyal.' },
28
- { id: 'cat', text: 'Cat', note: 'Cats are independent.' },
29
- { id: 'bird', text: 'Bird', note: 'Birds can fly.' },
30
- ],
31
- },
32
- {
33
- text: 'What is your favorite animal?',
34
- mode: QuestionMode.ManualAdvance,
35
- options: [
36
- { id: 'dog', text: 'Dog', note: 'Dogs are loyal.' },
37
- { id: 'cat', text: 'Cat', note: 'Cats are independent.' },
38
- { id: 'bird', text: 'Bird', note: 'Birds can fly.' },
39
- ],
40
- },
41
- ];
42
-
43
- const QuizScreen = () => {
44
- return <Quiz questions={questions} />;
45
- };
46
-
47
- export default QuizScreen;
@@ -1 +0,0 @@
1
- export { default } from '@/features/tracker-app/app';