vibefast-cli 1.1.3 → 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 (300) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +63 -169
  3. package/dist/__tests__/recipes.test.js +25 -3
  4. package/dist/__tests__/recipes.test.js.map +1 -1
  5. package/dist/commands/add.d.ts +1 -1
  6. package/dist/commands/add.d.ts.map +1 -1
  7. package/dist/commands/add.js +547 -543
  8. package/dist/commands/add.js.map +1 -1
  9. package/dist/commands/checklist.d.ts +1 -1
  10. package/dist/commands/checklist.d.ts.map +1 -1
  11. package/dist/commands/checklist.js +40 -39
  12. package/dist/commands/checklist.js.map +1 -1
  13. package/dist/commands/doctor.d.ts +1 -1
  14. package/dist/commands/doctor.js +22 -22
  15. package/dist/commands/doctor.js.map +1 -1
  16. package/dist/commands/env.d.ts +1 -1
  17. package/dist/commands/env.d.ts.map +1 -1
  18. package/dist/commands/env.js +58 -53
  19. package/dist/commands/env.js.map +1 -1
  20. package/dist/commands/health.d.ts +1 -1
  21. package/dist/commands/health.d.ts.map +1 -1
  22. package/dist/commands/health.js +101 -93
  23. package/dist/commands/health.js.map +1 -1
  24. package/dist/commands/init.d.ts +1 -1
  25. package/dist/commands/init.d.ts.map +1 -1
  26. package/dist/commands/init.js +416 -296
  27. package/dist/commands/init.js.map +1 -1
  28. package/dist/commands/remove.d.ts +1 -1
  29. package/dist/commands/remove.d.ts.map +1 -1
  30. package/dist/commands/remove.js +77 -64
  31. package/dist/commands/remove.js.map +1 -1
  32. package/dist/commands/status.d.ts +1 -1
  33. package/dist/commands/status.d.ts.map +1 -1
  34. package/dist/commands/status.js +15 -14
  35. package/dist/commands/status.js.map +1 -1
  36. package/dist/core/__tests__/detect.test.js +68 -34
  37. package/dist/core/__tests__/detect.test.js.map +1 -1
  38. package/dist/core/ast.d.ts +14 -0
  39. package/dist/core/ast.d.ts.map +1 -0
  40. package/dist/core/ast.js +239 -0
  41. package/dist/core/ast.js.map +1 -0
  42. package/dist/core/codemod.d.ts.map +1 -1
  43. package/dist/core/codemod.js +62 -44
  44. package/dist/core/codemod.js.map +1 -1
  45. package/dist/core/config.d.ts +10 -0
  46. package/dist/core/config.d.ts.map +1 -0
  47. package/dist/core/config.js +51 -0
  48. package/dist/core/config.js.map +1 -0
  49. package/dist/core/detect.d.ts +8 -2
  50. package/dist/core/detect.d.ts.map +1 -1
  51. package/dist/core/detect.js +52 -21
  52. package/dist/core/detect.js.map +1 -1
  53. package/dist/core/errors.d.ts.map +1 -1
  54. package/dist/core/errors.js +9 -8
  55. package/dist/core/errors.js.map +1 -1
  56. package/dist/core/exec.d.ts +16 -0
  57. package/dist/core/exec.d.ts.map +1 -0
  58. package/dist/core/exec.js +48 -0
  59. package/dist/core/exec.js.map +1 -0
  60. package/dist/core/manualSteps.d.ts +7 -0
  61. package/dist/core/manualSteps.d.ts.map +1 -0
  62. package/dist/core/manualSteps.js +59 -0
  63. package/dist/core/manualSteps.js.map +1 -0
  64. package/dist/core/paths.d.ts +3 -1
  65. package/dist/core/paths.d.ts.map +1 -1
  66. package/dist/core/paths.js +14 -10
  67. package/dist/core/paths.js.map +1 -1
  68. package/dist/core/spinner.d.ts +1 -1
  69. package/dist/core/spinner.d.ts.map +1 -1
  70. package/dist/core/spinner.js +38 -8
  71. package/dist/core/spinner.js.map +1 -1
  72. package/dist/core/vosk.d.ts.map +1 -1
  73. package/dist/core/vosk.js +50 -39
  74. package/dist/core/vosk.js.map +1 -1
  75. package/docs/manual-testing.md +91 -0
  76. package/package.json +6 -3
  77. package/recipes/audio-recorder/apps/native/src/app/audio-recorder/index.tsx +5 -0
  78. package/recipes/audio-recorder/recipe.json +3 -3
  79. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/audio-player.tsx +301 -0
  80. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/audio-recorder.tsx +373 -0
  81. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/audio-waveform.tsx +270 -0
  82. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/index.ts +4 -0
  83. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/recording-list.tsx +89 -0
  84. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/audio-player-demo.tsx +66 -0
  85. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/audio-recorder-cloud.tsx +68 -0
  86. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/audio-recorder-interview.tsx +102 -0
  87. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/basic.tsx +27 -0
  88. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/index.ts +5 -0
  89. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/with-recording-list-demo.tsx +82 -0
  90. package/recipes/audio-recorder-supabase/packages/backend/src/services/recordings.ts +369 -0
  91. package/recipes/audio-recorder-supabase/packages/backend/supabase/migrations/recordings.sql +70 -0
  92. package/recipes/audio-recorder-supabase/recipe.json +35 -0
  93. package/recipes/audio-recorder-supabase@latest.zip +0 -0
  94. package/recipes/audio-recorder@latest.zip +0 -0
  95. package/recipes/charts/apps/native/src/features/charts/components/bar-chart.tsx +3 -3
  96. package/recipes/charts/apps/native/src/features/charts/components/candlestick-chart.tsx +2 -2
  97. package/recipes/charts/apps/native/src/features/charts/components/chart-card.tsx +5 -5
  98. package/recipes/charts/apps/native/src/features/charts/components/column-chart.tsx +3 -3
  99. package/recipes/charts/apps/native/src/features/charts/components/doughnut-chart.tsx +20 -4
  100. package/recipes/charts/apps/native/src/features/charts/components/line-chart.tsx +7 -6
  101. package/recipes/charts/apps/native/src/features/charts/components/radar-chart.tsx +6 -4
  102. package/recipes/charts/apps/native/src/features/charts/components/radial-bar-chart.tsx +1 -1
  103. package/recipes/charts/apps/native/src/features/charts/components/stacked-bar-chart.tsx +5 -4
  104. package/recipes/charts/recipe.json +4 -13
  105. package/recipes/charts@latest.zip +0 -0
  106. package/recipes/chatbot/apps/native/src/app/chatbot/index.tsx +1 -0
  107. package/recipes/chatbot/apps/native/src/features/chatbot/components/chat-markdown.tsx +86 -86
  108. package/recipes/chatbot/apps/native/src/features/chatbot/components/markdown/code-block.tsx +86 -53
  109. package/recipes/chatbot/recipe.json +26 -92
  110. package/recipes/chatbot-supabase/apps/native/src/api-client/supabase/chatbot.ts +515 -0
  111. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/app/index.tsx +257 -0
  112. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-header-buttons.tsx +59 -0
  113. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-input-bar.tsx +485 -0
  114. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-markdown.tsx +575 -0
  115. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-message-bubble.tsx +223 -0
  116. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-settings-modal.tsx +161 -0
  117. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/image-preview-list.tsx +116 -0
  118. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/markdown/code-block.tsx +165 -0
  119. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/markdown/index.ts +10 -0
  120. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/markdown/table-renderer.tsx +129 -0
  121. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/message-error-boundary.tsx +78 -0
  122. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/message-list.tsx +170 -0
  123. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/model-selector.tsx +283 -0
  124. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/report-content-modal.tsx +188 -0
  125. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/suggested-messages.tsx +67 -0
  126. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/constants/models.ts +20 -0
  127. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/constants/report-reasons.ts +9 -0
  128. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-attachment-cache.ts +142 -0
  129. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-chat-config.ts +458 -0
  130. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-chat-handlers.ts +429 -0
  131. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-chatbot-settings.ts +89 -0
  132. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-conversation.ts +90 -0
  133. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-image-picker.ts +122 -0
  134. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-keyboard-coordinator.ts +161 -0
  135. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-smart-scroll-manager.ts +213 -0
  136. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/models/index.ts +86 -0
  137. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/models/models.ts +162 -0
  138. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/models/providers.ts +62 -0
  139. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/models/types.ts +40 -0
  140. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/services/file-uploader.ts +287 -0
  141. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/services/message-handler-service.ts +189 -0
  142. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/types/index.ts +70 -0
  143. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/utils/chat-telemetry.ts +91 -0
  144. package/recipes/chatbot-supabase/packages/backend/src/services/conversations.ts +243 -0
  145. package/recipes/chatbot-supabase/packages/backend/src/services/messages.ts +327 -0
  146. package/recipes/chatbot-supabase/packages/backend/supabase/functions/chat-stream/index.ts +347 -0
  147. package/recipes/chatbot-supabase/packages/backend/supabase/migrations/chatbot.sql +104 -0
  148. package/recipes/chatbot-supabase/recipe.json +79 -0
  149. package/recipes/chatbot-supabase@latest.zip +0 -0
  150. package/recipes/chatbot.zip +0 -0
  151. package/recipes/chatbot@latest.zip +0 -0
  152. package/recipes/image-analysis/packages/backend/convex/imageAnalysis/index.ts +2 -2
  153. package/recipes/image-analysis/packages/backend/convex/{imageAnalysisFunctions.ts → imageAnalysis.ts} +5 -5
  154. package/recipes/image-analysis/recipe.json +15 -55
  155. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/analysis-options-screen.tsx +304 -0
  156. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/camera.tsx +221 -0
  157. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/image-capture-screen.tsx +333 -0
  158. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/loading-screen.tsx +214 -0
  159. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/loading.tsx +191 -0
  160. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/results.tsx +137 -0
  161. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/trait-details.tsx +172 -0
  162. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/use-analysis-data.ts +160 -0
  163. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/use-results-screen.ts +151 -0
  164. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/achievement-badge.tsx +77 -0
  165. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/achievement-card.tsx +75 -0
  166. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/achievement-unlocked-modal.tsx +162 -0
  167. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/achievements-section.tsx +44 -0
  168. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/advice-list.tsx +42 -0
  169. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/circular-progress.tsx +233 -0
  170. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/content-card.tsx +38 -0
  171. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/error-state.tsx +42 -0
  172. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/index.ts +9 -0
  173. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/loading-state.tsx +26 -0
  174. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/profile-image.tsx +60 -0
  175. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/results-header.tsx +62 -0
  176. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/score-display.tsx +54 -0
  177. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/share-options-modal.tsx +110 -0
  178. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/traits-grid.tsx +74 -0
  179. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/config/analysis-config.ts +80 -0
  180. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/config/master-analysis-config.ts +157 -0
  181. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/hooks/index.ts +1 -0
  182. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/hooks/use-analysis.ts +38 -0
  183. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/hooks/use-image-analysis.ts +208 -0
  184. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/services/analysis-service.ts +262 -0
  185. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/services/share-service.ts +176 -0
  186. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/services/trait-details-service.ts +289 -0
  187. package/recipes/image-analysis-supabase/packages/backend/src/services/image-analyses.ts +132 -0
  188. package/recipes/image-analysis-supabase/packages/backend/supabase/functions/analyze-image/index.ts +312 -0
  189. package/recipes/image-analysis-supabase/packages/backend/supabase/migrations/image_analysis.sql +42 -0
  190. package/recipes/image-analysis-supabase/recipe.json +57 -0
  191. package/recipes/image-analysis-supabase@latest.zip +0 -0
  192. package/recipes/image-analysis@latest.zip +0 -0
  193. package/recipes/image-generator/apps/native/src/features/image-generator/app/index.tsx +16 -2
  194. package/recipes/image-generator/apps/native/src/features/image-generator/components/image-model-selector.tsx +11 -5
  195. package/recipes/image-generator/apps/native/src/features/image-generator/hooks/use-image-generator.ts +11 -5
  196. package/recipes/image-generator/packages/backend/convex/imageGeneration/index.ts +2 -2
  197. package/recipes/image-generator/recipe.json +16 -39
  198. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/app/_layout.tsx +26 -0
  199. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/app/gallery.tsx +217 -0
  200. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/app/index.tsx +251 -0
  201. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/components/gallery-image.tsx +25 -0
  202. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/components/image-detail-modal.tsx +215 -0
  203. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/components/image-model-selector.tsx +216 -0
  204. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/components/image-placeholder.tsx +26 -0
  205. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/hooks/use-image-gallery.ts +71 -0
  206. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/hooks/use-image-generator-settings.ts +152 -0
  207. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/hooks/use-image-generator.ts +103 -0
  208. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/models/models.ts +66 -0
  209. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/services/image-gallery-service.ts +96 -0
  210. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/services/image-save-service.ts +120 -0
  211. package/recipes/image-generator-supabase/packages/backend/supabase/functions/generate-image/index.ts +291 -0
  212. package/recipes/image-generator-supabase/packages/backend/supabase/migrations/image_generator.sql +71 -0
  213. package/recipes/image-generator-supabase/recipe.json +59 -0
  214. package/recipes/image-generator-supabase@latest.zip +0 -0
  215. package/recipes/image-generator@latest.zip +0 -0
  216. package/recipes/ios-widget/recipe.json +15 -24
  217. package/recipes/ios-widget@latest.zip +0 -0
  218. package/recipes/onboarding/apps/native/src/features/onboarding/analytics/index.ts +9 -0
  219. package/recipes/onboarding/apps/native/src/features/onboarding/components/onboarding-with-analytics.tsx +141 -0
  220. package/recipes/onboarding/apps/native/src/features/onboarding/components/onboarding.tsx +173 -0
  221. package/recipes/onboarding/apps/native/src/features/onboarding/config/onboarding-flow-config.ts +189 -0
  222. package/recipes/onboarding/apps/native/src/features/onboarding/demo-one/app/index.tsx +42 -0
  223. package/recipes/onboarding/apps/native/src/features/onboarding/demo-one/data.ts +32 -0
  224. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/app/index.tsx +43 -0
  225. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/interactive-onboarding.tsx +222 -0
  226. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/ai-tone-step.tsx +133 -0
  227. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/currency-step.tsx +165 -0
  228. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-ai-step.tsx +199 -0
  229. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-chatbot-step.tsx +154 -0
  230. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-manual-step.tsx +156 -0
  231. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-scan-step.tsx +158 -0
  232. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/main-reason-step.tsx +139 -0
  233. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/notification-step.tsx +129 -0
  234. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/overspend-step.tsx +138 -0
  235. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/personalizing-step.tsx +190 -0
  236. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/rating-step.tsx +98 -0
  237. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/reminder-step.tsx +181 -0
  238. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/safety-step.tsx +110 -0
  239. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/struggle-step.tsx +139 -0
  240. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/welcome-step.tsx +217 -0
  241. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/ui/onboarding-header.tsx +58 -0
  242. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/constants.ts +179 -0
  243. package/recipes/onboarding/apps/native/src/features/onboarding/hooks/use-onboarding-analytics.ts +323 -0
  244. package/recipes/onboarding/apps/native/src/features/onboarding/services/onboarding-analytics.ts +432 -0
  245. package/recipes/onboarding/recipe.json +15 -0
  246. package/recipes/onboarding@latest.zip +0 -0
  247. package/recipes/payments/recipe.json +28 -61
  248. package/recipes/payments-supabase/apps/native/src/features/payments/README.md +200 -0
  249. package/recipes/payments-supabase/apps/native/src/features/payments/app/local-paywall.tsx +194 -0
  250. package/recipes/payments-supabase/apps/native/src/features/payments/app/remote-paywall.tsx +79 -0
  251. package/recipes/payments-supabase/apps/native/src/features/payments/components/payment-initializer.tsx +95 -0
  252. package/recipes/payments-supabase/apps/native/src/features/payments/components/paywall-error-state.tsx +60 -0
  253. package/recipes/payments-supabase/apps/native/src/features/payments/components/paywall-local-mode.tsx +116 -0
  254. package/recipes/payments-supabase/apps/native/src/features/payments/components/paywall-product-card.tsx +133 -0
  255. package/recipes/payments-supabase/apps/native/src/features/payments/components/paywall-remote-mode.tsx +146 -0
  256. package/recipes/payments-supabase/apps/native/src/features/payments/hooks/use-entitlement.ts +63 -0
  257. package/recipes/payments-supabase/apps/native/src/features/payments/index.ts +8 -0
  258. package/recipes/payments-supabase/apps/native/src/features/payments/services/revenuecat-adapter.ts +407 -0
  259. package/recipes/payments-supabase/packages/backend/src/services/payments.ts +201 -0
  260. package/recipes/payments-supabase/packages/backend/supabase/migrations/payments.sql +35 -0
  261. package/recipes/payments-supabase/recipe.json +51 -0
  262. package/recipes/payments-supabase@latest.zip +0 -0
  263. package/recipes/payments@latest.zip +0 -0
  264. package/recipes/quiz/apps/native/src/features/quiz/index.tsx +1 -2
  265. package/recipes/quiz/recipe.json +6 -9
  266. package/recipes/quiz@latest.zip +0 -0
  267. package/recipes/tracker-app/apps/native/src/features/tracker-app/app/index.tsx +1 -2
  268. package/recipes/tracker-app/recipe.json +7 -10
  269. package/recipes/tracker-app@latest.zip +0 -0
  270. package/recipes/voice-bot/recipe.json +8 -68
  271. package/recipes/voice-bot.zip +0 -0
  272. package/recipes/voice-bot@latest.zip +0 -0
  273. package/recipes/wake-word/recipe.json +10 -9
  274. package/recipes/wake-word.zip +0 -0
  275. package/recipes/wake-word@latest.zip +0 -0
  276. package/recipes/charts/apps/native/src/app/(root)/(protected)/charts/index.tsx +0 -3
  277. package/recipes/chatbot/packages/backend/convex/lib/rateLimit.ts +0 -100
  278. package/recipes/chatbot/packages/backend/convex/lib/telemetry.ts +0 -29
  279. package/recipes/chatbot/packages/backend/convex/ragKnowledge.ts +0 -0
  280. package/recipes/image-analysis/apps/native/assets/features/image-analyzer/front.jpg +0 -0
  281. package/recipes/image-analysis/apps/native/assets/features/image-analyzer/side.jpg +0 -0
  282. package/recipes/image-analysis/apps/native/assets/features/image-analyzer/threeQuarter.jpg +0 -0
  283. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/_layout.tsx +0 -5
  284. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/analysis-options.tsx +0 -50
  285. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/camera.tsx +0 -2
  286. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/index.tsx +0 -50
  287. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/loading.tsx +0 -50
  288. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/results.tsx +0 -2
  289. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/trait-details.tsx +0 -3
  290. package/recipes/image-analysis/packages/backend/convex/lib/ai/imageAnalysisAdapter.ts +0 -200
  291. package/recipes/payments/apps/native/src/app/(root)/(protected)/paywall/index.tsx +0 -74
  292. package/recipes/payments/apps/native/src/app/(root)/(protected)/paywall/local.tsx +0 -25
  293. package/recipes/payments/apps/native/src/app/(root)/(protected)/paywall/remote.tsx +0 -23
  294. package/recipes/quiz/apps/native/src/app/(root)/(protected)/quiz/index.tsx +0 -47
  295. package/recipes/tracker-app/apps/native/src/app/(root)/(protected)/tracker-app/index.tsx +0 -1
  296. package/recipes/voice-bot/apps/native/src/app/(root)/(protected)/voice-bot/index.tsx +0 -27
  297. package/recipes/voice-bot/packages/backend/convex/router.ts +0 -81
  298. /package/recipes/{chatbot/apps/native/src/app/(root)/(protected) → chatbot-supabase/apps/native/src/app}/chatbot/index.tsx +0 -0
  299. /package/recipes/{image-generator/apps/native/src/app/(root)/(protected) → image-generator-supabase/apps/native/src/app}/image-generator/gallery.tsx +0 -0
  300. /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,104 @@
1
+ -- Chatbot Feature Migration
2
+ -- Creates tables for conversations and messages
3
+ -- Run this migration when adding the chatbot feature via CLI
4
+
5
+ -- ============================================================================
6
+ -- CONVERSATIONS TABLE
7
+ -- Stores chat conversations for each user
8
+ -- ============================================================================
9
+ CREATE TABLE IF NOT EXISTS conversations (
10
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
11
+ user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
12
+ name TEXT,
13
+ created_at TIMESTAMPTZ DEFAULT now()
14
+ );
15
+
16
+ -- ============================================================================
17
+ -- MESSAGES TABLE
18
+ -- Stores individual messages within conversations
19
+ -- ============================================================================
20
+ CREATE TABLE IF NOT EXISTS messages (
21
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
22
+ conversation_id UUID NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
23
+ user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
24
+ author_type TEXT NOT NULL CHECK (author_type IN ('user', 'bot')),
25
+ text TEXT,
26
+ ai_provider TEXT,
27
+ ai_model TEXT,
28
+ tool_calls JSONB,
29
+ metadata JSONB,
30
+ attachments JSONB,
31
+ created_at TIMESTAMPTZ DEFAULT now()
32
+ );
33
+
34
+ -- ============================================================================
35
+ -- INDEXES
36
+ -- ============================================================================
37
+ CREATE INDEX IF NOT EXISTS idx_conversations_user_id ON conversations(user_id);
38
+ CREATE INDEX IF NOT EXISTS idx_messages_conversation_id ON messages(conversation_id);
39
+ CREATE INDEX IF NOT EXISTS idx_messages_user_id ON messages(user_id);
40
+ CREATE INDEX IF NOT EXISTS idx_messages_created_at ON messages(created_at);
41
+
42
+ -- ============================================================================
43
+ -- RLS POLICIES - CONVERSATIONS
44
+ -- ============================================================================
45
+ ALTER TABLE conversations ENABLE ROW LEVEL SECURITY;
46
+
47
+ CREATE POLICY "conversations_select_own" ON conversations
48
+ FOR SELECT USING (auth.uid() = user_id);
49
+
50
+ CREATE POLICY "conversations_insert_own" ON conversations
51
+ FOR INSERT WITH CHECK (auth.uid() = user_id);
52
+
53
+ CREATE POLICY "conversations_update_own" ON conversations
54
+ FOR UPDATE USING (auth.uid() = user_id);
55
+
56
+ CREATE POLICY "conversations_delete_own" ON conversations
57
+ FOR DELETE USING (auth.uid() = user_id);
58
+
59
+ -- ============================================================================
60
+ -- RLS POLICIES - MESSAGES
61
+ -- ============================================================================
62
+ ALTER TABLE messages ENABLE ROW LEVEL SECURITY;
63
+
64
+ CREATE POLICY "messages_select_own" ON messages
65
+ FOR SELECT USING (auth.uid() = user_id);
66
+
67
+ CREATE POLICY "messages_insert_own" ON messages
68
+ FOR INSERT WITH CHECK (auth.uid() = user_id);
69
+
70
+ CREATE POLICY "messages_update_own" ON messages
71
+ FOR UPDATE USING (auth.uid() = user_id);
72
+
73
+ CREATE POLICY "messages_delete_own" ON messages
74
+ FOR DELETE USING (auth.uid() = user_id);
75
+
76
+ -- ============================================================================
77
+ -- STORAGE BUCKET - chat-attachments
78
+ -- ============================================================================
79
+ INSERT INTO storage.buckets (id, name, public, file_size_limit, allowed_mime_types)
80
+ VALUES (
81
+ 'chat-attachments',
82
+ 'chat-attachments',
83
+ false,
84
+ 52428800, -- 50MB limit
85
+ ARRAY['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'application/pdf', 'text/plain', 'audio/mpeg', 'audio/wav', 'video/mp4']
86
+ ) ON CONFLICT (id) DO NOTHING;
87
+
88
+ -- Storage RLS for chat-attachments
89
+ CREATE POLICY "Users can view their own chat attachments"
90
+ ON storage.objects FOR SELECT TO authenticated
91
+ USING (bucket_id = 'chat-attachments' AND name LIKE auth.uid() || '/%');
92
+
93
+ CREATE POLICY "Users can upload their own chat attachments"
94
+ ON storage.objects FOR INSERT TO authenticated
95
+ WITH CHECK (bucket_id = 'chat-attachments' AND name LIKE auth.uid() || '/%');
96
+
97
+ CREATE POLICY "Users can update their own chat attachments"
98
+ ON storage.objects FOR UPDATE TO authenticated
99
+ USING (bucket_id = 'chat-attachments' AND name LIKE auth.uid() || '/%')
100
+ WITH CHECK (bucket_id = 'chat-attachments' AND name LIKE auth.uid() || '/%');
101
+
102
+ CREATE POLICY "Users can delete their own chat attachments"
103
+ ON storage.objects FOR DELETE TO authenticated
104
+ USING (bucket_id = 'chat-attachments' AND name LIKE auth.uid() || '/%');
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "chatbot",
3
+ "version": "1.0.0",
4
+ "description": "AI-powered chat assistant with Supabase backend",
5
+ "copy": [
6
+ {
7
+ "from": "apps/native/src/app/chatbot",
8
+ "to": "apps/native/src/app/(root)/(protected)/chatbot"
9
+ },
10
+ {
11
+ "from": "apps/native/src/features/chatbot",
12
+ "to": "apps/native/src/features/chatbot"
13
+ },
14
+ {
15
+ "from": "apps/native/src/api-client/supabase/chatbot.ts",
16
+ "to": "apps/native/src/api-client/supabase/chatbot.ts"
17
+ },
18
+ {
19
+ "from": "packages/backend/supabase/functions/chat-stream",
20
+ "to": "packages/backend/supabase/functions/chat-stream"
21
+ },
22
+ {
23
+ "from": "packages/backend/supabase/migrations/chatbot.sql",
24
+ "to": "packages/backend/supabase/migrations/chatbot.sql"
25
+ },
26
+ {
27
+ "from": "packages/backend/src/services/conversations.ts",
28
+ "to": "packages/backend/src/services/conversations.ts"
29
+ },
30
+ {
31
+ "from": "packages/backend/src/services/messages.ts",
32
+ "to": "packages/backend/src/services/messages.ts"
33
+ }
34
+ ],
35
+ "nav": {
36
+ "href": "/(root)/(protected)/chatbot",
37
+ "label": "AI Chatbot",
38
+ "icon": "💬",
39
+ "color": "#F43F5E"
40
+ },
41
+ "target": "native",
42
+ "dependencies": {
43
+ "expo": [
44
+ "@supabase/supabase-js",
45
+ "@tanstack/react-query",
46
+ "expo-clipboard",
47
+ "markdown-it",
48
+ "react-native-markdown-display",
49
+ "react-syntax-highlighter",
50
+ "react-native-reanimated",
51
+ "react-native-safe-area-context"
52
+ ]
53
+ },
54
+ "env": [
55
+ {
56
+ "key": "OPENAI_API_KEY",
57
+ "description": "OpenAI API key for chat completions",
58
+ "example": "sk-...",
59
+ "link": "https://platform.openai.com/api-keys",
60
+ "file": "packages/backend/supabase/.env",
61
+ "required": true
62
+ }
63
+ ],
64
+ "manualSteps": [
65
+ {
66
+ "title": "Apply Supabase migration",
67
+ "description": "Run: cd packages/backend && supabase db push",
68
+ "content": "supabase db push"
69
+ },
70
+ {
71
+ "title": "Deploy Edge Function",
72
+ "description": "Run: cd packages/backend && supabase functions deploy chat-stream",
73
+ "content": "supabase functions deploy chat-stream"
74
+ }
75
+ ],
76
+ "postInstall": {
77
+ "message": "📦 Remember to run 'supabase db push' and 'supabase functions deploy chat-stream'"
78
+ }
79
+ }
Binary file
Binary file
@@ -2,7 +2,7 @@
2
2
  * Image Analysis Domain Aggregator
3
3
  *
4
4
  * Single entry point for all image analysis Convex functions.
5
- * Re-exports functions from imageAnalysisFunctions.ts to decouple
5
+ * Re-exports functions from imageAnalysis.ts to decouple
6
6
  * frontend from internal file structure.
7
7
  */
8
8
 
@@ -15,4 +15,4 @@ export {
15
15
  getUserImageAnalyses,
16
16
  performAiAnalysis,
17
17
  updateAnalysisResults,
18
- } from '../imageAnalysisFunctions';
18
+ } from '../imageAnalysis';
@@ -44,7 +44,7 @@ export const analyzeImages = action({
44
44
 
45
45
  // Create analysis record
46
46
  const analysisId = await ctx.runMutation(
47
- internal.imageAnalysisFunctions.createAnalysisRecord,
47
+ internal.imageAnalysis.createAnalysisRecord,
48
48
  {
49
49
  userId,
50
50
  imageStorageIds,
@@ -56,7 +56,7 @@ export const analyzeImages = action({
56
56
  // Schedule background AI analysis
57
57
  await ctx.scheduler.runAfter(
58
58
  0,
59
- internal.imageAnalysisFunctions.performAiAnalysis,
59
+ internal.imageAnalysis.performAiAnalysis,
60
60
  {
61
61
  analysisId,
62
62
  },
@@ -101,7 +101,7 @@ export const performAiAnalysis = internalAction({
101
101
  try {
102
102
  // Get analysis record
103
103
  const analysis = await ctx.runQuery(
104
- internal.imageAnalysisFunctions.getAnalysisRecord,
104
+ internal.imageAnalysis.getAnalysisRecord,
105
105
  {
106
106
  analysisId: args.analysisId,
107
107
  },
@@ -178,7 +178,7 @@ export const performAiAnalysis = internalAction({
178
178
 
179
179
  // Update analysis record with results
180
180
  await ctx.runMutation(
181
- internal.imageAnalysisFunctions.updateAnalysisResults,
181
+ internal.imageAnalysis.updateAnalysisResults,
182
182
  {
183
183
  analysisId: args.analysisId,
184
184
  results,
@@ -190,7 +190,7 @@ export const performAiAnalysis = internalAction({
190
190
 
191
191
  // Update analysis record with error
192
192
  await ctx.runMutation(
193
- internal.imageAnalysisFunctions.updateAnalysisResults,
193
+ internal.imageAnalysis.updateAnalysisResults,
194
194
  {
195
195
  analysisId: args.analysisId,
196
196
  status: 'failed',
@@ -1,12 +1,8 @@
1
1
  {
2
2
  "name": "image-analysis",
3
- "version": "1.1.0",
4
- "description": "Multi-step AI image analyzer with configurable flows",
3
+ "version": "1.0.0",
4
+ "description": "AI-powered image analysis",
5
5
  "copy": [
6
- {
7
- "from": "apps/native/src/app/(root)/(protected)/analysis",
8
- "to": "apps/native/src/app/(root)/(protected)/analysis"
9
- },
10
6
  {
11
7
  "from": "apps/native/src/features/image-analyzer",
12
8
  "to": "apps/native/src/features/image-analyzer"
@@ -15,64 +11,28 @@
15
11
  "from": "apps/native/src/api-client/image-analyzer.ts",
16
12
  "to": "apps/native/src/api-client/image-analyzer.ts"
17
13
  },
18
- {
19
- "from": "apps/native/assets/features/image-analyzer",
20
- "to": "apps/native/assets/features/image-analyzer"
21
- },
22
- {
23
- "from": "packages/backend/convex/imageAnalysis",
24
- "to": "packages/backend/convex/imageAnalysis"
25
- },
26
14
  {
27
15
  "from": "packages/backend/convex/imageAnalysis.ts",
28
16
  "to": "packages/backend/convex/imageAnalysis.ts"
29
17
  },
30
18
  {
31
- "from": "packages/backend/convex/lib/ai/imageAnalysisAdapter.ts",
32
- "to": "packages/backend/convex/lib/ai/imageAnalysisAdapter.ts"
19
+ "from": "packages/backend/convex/imageAnalysis",
20
+ "to": "packages/backend/convex/imageAnalysis"
33
21
  }
34
22
  ],
23
+ "configuration": {
24
+ "apiClient": {
25
+ "exports": [
26
+ "export { type ImageAnalyzerApi, imageAnalyzerApi } from './image-analyzer';"
27
+ ]
28
+ }
29
+ },
35
30
  "nav": {
36
- "href": "/(root)/(protected)/analysis/face-analysis",
37
- "label": "AI Analysis",
31
+ "href": "/(root)/(protected)/image-analyzer",
32
+ "label": "Image Analyzer",
38
33
  "icon": "🔍",
39
- "color": "#06B6D4"
34
+ "color": "#3B82F6"
40
35
  },
41
36
  "target": "native",
42
- "env": [
43
- {
44
- "key": "OPENAI_API_KEY",
45
- "description": "OpenAI API key for image analysis (optional - use this OR Gemini, not both).",
46
- "example": "sk-abc123def456",
47
- "link": "https://platform.openai.com/account/api-keys",
48
- "file": "packages/backend/.env.local"
49
- },
50
- {
51
- "key": "GEMINI_API_KEY",
52
- "description": "Google Generative AI key for image analysis (optional - use this OR OpenAI, not both).",
53
- "example": "AIzaSyExampleGeminiKey",
54
- "link": "https://aistudio.google.com/api-keys",
55
- "file": "packages/backend/.env.local"
56
- },
57
- {
58
- "key": "AI_PROVIDER",
59
- "description": "Provider to use for the analyzer (openai or gemini). Defaults to openai if unset.",
60
- "example": "openai",
61
- "file": "packages/backend/.env.local",
62
- "required": false
63
- }
64
- ],
65
- "manualSteps": [
66
- {
67
- "title": "Get an LLM API key (OpenAI or Gemini)",
68
- "description": "Start with either provider (OpenAI or Gemini). You can add both later if you like.\\n\\n Option A: OpenAI\\n Visit https://platform.openai.com/account/api-keys and create an API key\\n\\n Option B: Google Gemini\\n Visit https://aistudio.google.com/api-keys and create an API key",
69
- "link": "https://platform.openai.com/account/api-keys"
70
- },
71
- {
72
- "title": "Add keys to backend .env file",
73
- "description": "Open packages/backend/.env.local and add at least one provider key. You can add both. Optionally set AI_PROVIDER to choose it explicitly.",
74
- "file": "packages/backend/.env.local",
75
- "content": "# Add one or both:\\nOPENAI_API_KEY=sk-...\\nGEMINI_API_KEY=AIzaSy...\\n# Optional override:\\nAI_PROVIDER=openai\\n"
76
- }
77
- ]
37
+ "dependencies": { "expo": ["expo-camera", "expo-image-picker"] }
78
38
  }
@@ -0,0 +1,304 @@
1
+ import { Stack, useLocalSearchParams, useRouter } from 'expo-router';
2
+ import { useCallback, useMemo, useState } from 'react';
3
+ import {
4
+ Alert,
5
+ Platform,
6
+ ScrollView,
7
+ TouchableOpacity,
8
+ View,
9
+ } from 'react-native';
10
+
11
+ import { Image, ScreenFooter, Text } from '@/components/ui';
12
+ import type {
13
+ AnalysisGoal,
14
+ FeedbackStyle,
15
+ } from '@/features/image-analyzer/config/analysis-config';
16
+ import type { AnalysisFlowConfig } from '@/features/image-analyzer/config/master-analysis-config';
17
+ import { useNavigationOptions } from '@/lib/hooks/use-navigation-options';
18
+
19
+ type DynamicAnalysisOptionsProps = {
20
+ config: AnalysisFlowConfig;
21
+ };
22
+
23
+ // Enhanced goal options with colors and icons
24
+ const createEnhancedGoals = (goals: readonly AnalysisGoal[]) => [
25
+ {
26
+ ...goals[0],
27
+ color: 'bg-blue-500',
28
+ icon: '🔍',
29
+ accent:
30
+ 'border-blue-200 bg-blue-50 dark:border-blue-700 dark:bg-blue-900/20',
31
+ },
32
+ {
33
+ ...goals[1],
34
+ color: 'bg-green-500',
35
+ icon: '⚡',
36
+ accent:
37
+ 'border-green-200 bg-green-50 dark:border-green-700 dark:bg-green-900/20',
38
+ },
39
+ {
40
+ ...goals[2],
41
+ color: 'bg-purple-500',
42
+ icon: '🎯',
43
+ accent:
44
+ 'border-purple-200 bg-purple-50 dark:border-purple-700 dark:bg-purple-900/20',
45
+ },
46
+ {
47
+ ...goals[3],
48
+ color: 'bg-orange-500',
49
+ icon: '⚖️',
50
+ accent:
51
+ 'border-orange-200 bg-orange-50 dark:border-orange-700 dark:bg-orange-900/20',
52
+ },
53
+ ];
54
+
55
+ // Enhanced feedback styles with colors and icons
56
+ const createEnhancedStyles = (styles: readonly FeedbackStyle[]) => [
57
+ {
58
+ ...styles[0],
59
+ color: 'bg-neutral-600',
60
+ icon: '👔',
61
+ accent:
62
+ 'border-neutral-200 bg-neutral-50 dark:border-neutral-700 dark:bg-neutral-900/20',
63
+ },
64
+ {
65
+ ...styles[1],
66
+ color: 'bg-cyan-500',
67
+ icon: '😊',
68
+ accent:
69
+ 'border-cyan-200 bg-cyan-50 dark:border-cyan-700 dark:bg-cyan-900/20',
70
+ },
71
+ {
72
+ ...styles[2],
73
+ color: 'bg-indigo-600',
74
+ icon: '🔬',
75
+ accent:
76
+ 'border-indigo-200 bg-indigo-50 dark:border-indigo-700 dark:bg-indigo-900/20',
77
+ },
78
+ {
79
+ ...styles[3],
80
+ color: 'bg-pink-500',
81
+ icon: '🎨',
82
+ accent:
83
+ 'border-pink-200 bg-pink-50 dark:border-pink-700 dark:bg-pink-900/20',
84
+ },
85
+ ];
86
+
87
+ export function DynamicAnalysisOptionsScreen({
88
+ config,
89
+ }: DynamicAnalysisOptionsProps) {
90
+ const router = useRouter();
91
+ const { standard } = useNavigationOptions();
92
+ const params = useLocalSearchParams();
93
+
94
+ const imageUris: string[] = useMemo(
95
+ () => (params.imageUris ? JSON.parse(params.imageUris as string) : []),
96
+ [params.imageUris],
97
+ );
98
+
99
+ const [selectedGoal, setSelectedGoal] = useState<string>('');
100
+ const [selectedFeedbackStyle, setSelectedFeedbackStyle] =
101
+ useState<string>('');
102
+
103
+ // Create enhanced versions with emojis and colors
104
+ const enhancedGoals = useMemo(
105
+ () => createEnhancedGoals(config.availableGoals),
106
+ [config.availableGoals],
107
+ );
108
+ const enhancedStyles = useMemo(
109
+ () => createEnhancedStyles(config.availableFeedbackStyles),
110
+ [config.availableFeedbackStyles],
111
+ );
112
+
113
+ const handleStartAnalysis = useCallback(() => {
114
+ if (!selectedGoal || !selectedFeedbackStyle) {
115
+ Alert.alert(
116
+ 'Selection Required',
117
+ 'Please select both an analysis goal and feedback style to continue.',
118
+ );
119
+ return;
120
+ }
121
+
122
+ router.push({
123
+ pathname: '/analysis/[type]/loading' as any,
124
+ params: {
125
+ type: config.id,
126
+ imageUris: JSON.stringify(imageUris),
127
+ analysisGoal: selectedGoal,
128
+ feedbackStyle: selectedFeedbackStyle,
129
+ },
130
+ });
131
+ }, [selectedGoal, selectedFeedbackStyle, imageUris, router, config.id]);
132
+
133
+ const handleBackToCapture = useCallback(() => {
134
+ router.back();
135
+ }, [router]);
136
+
137
+ const selectedGoalData = enhancedGoals.find(
138
+ (goal) => goal.id === selectedGoal,
139
+ );
140
+ const selectedStyleData = enhancedStyles.find(
141
+ (style) => style.id === selectedFeedbackStyle,
142
+ );
143
+
144
+ return (
145
+ <>
146
+ <Stack.Screen
147
+ options={{
148
+ ...standard,
149
+ title: config.name,
150
+ headerBackButtonDisplayMode: 'generic',
151
+ }}
152
+ />
153
+ <ScrollView
154
+ className="flex-1"
155
+ contentContainerStyle={{
156
+ paddingBottom: 120,
157
+ paddingTop: Platform.OS === 'ios' ? 78 : 0,
158
+ }}
159
+ showsVerticalScrollIndicator={false}
160
+ >
161
+ <View className="px-6 py-8">
162
+ {/* Image Preview Section */}
163
+ <View className="mb-8 rounded-2xl bg-white p-4 py-2 shadow-sm dark:bg-neutral-800">
164
+ <Text className="mb-4 ml-1 text-base font-medium text-neutral-800 dark:text-neutral-200">
165
+ 📷 {imageUris.length} Images Ready
166
+ </Text>
167
+ <ScrollView horizontal showsHorizontalScrollIndicator={false}>
168
+ <View className="flex-row gap-3">
169
+ {imageUris.map((uri, index) => (
170
+ <View key={index} className="items-center">
171
+ <View className="size-20 overflow-hidden rounded-xl bg-neutral-200 dark:bg-neutral-700">
172
+ <Image
173
+ source={{ uri }}
174
+ className="size-full"
175
+ contentFit="cover"
176
+ />
177
+ </View>
178
+ <Text className="mt-1 text-xs text-neutral-500">
179
+ {index + 1}
180
+ </Text>
181
+ </View>
182
+ ))}
183
+ </View>
184
+ </ScrollView>
185
+ </View>
186
+
187
+ {/* Analysis Goal Selection */}
188
+ <View className="mb-8">
189
+ <Text className="mb-4 text-xl font-bold text-neutral-900 dark:text-white">
190
+ Analysis Goal
191
+ </Text>
192
+ <View className="gap-y-3">
193
+ {enhancedGoals.map((goal) => (
194
+ <TouchableOpacity
195
+ key={goal.id}
196
+ onPress={() => setSelectedGoal(goal.id)}
197
+ className={`flex-row items-center rounded-2xl border-2 p-4 ${
198
+ selectedGoal === goal.id
199
+ ? goal.accent
200
+ : 'border-neutral-200 bg-white dark:border-neutral-700 dark:bg-neutral-800'
201
+ }`}
202
+ >
203
+ <View
204
+ className={`mr-4 size-12 items-center justify-center rounded-xl ${goal.color}`}
205
+ >
206
+ <Text className="text-xl">{goal.icon}</Text>
207
+ </View>
208
+ <View className="flex-1">
209
+ <Text className="mb-1 text-base font-semibold text-neutral-900 dark:text-white">
210
+ {goal.label}
211
+ </Text>
212
+ <Text className="text-sm text-neutral-600 dark:text-neutral-400">
213
+ {goal.description}
214
+ </Text>
215
+ </View>
216
+ </TouchableOpacity>
217
+ ))}
218
+ </View>
219
+ </View>
220
+
221
+ {/* Feedback Style Selection */}
222
+ <View className="mb-8">
223
+ <Text className="mb-4 text-xl font-bold text-neutral-900 dark:text-white">
224
+ Feedback Tone
225
+ </Text>
226
+ <View className="gap-y-3">
227
+ {enhancedStyles.map((style) => (
228
+ <TouchableOpacity
229
+ key={style.id}
230
+ onPress={() => setSelectedFeedbackStyle(style.id)}
231
+ className={`flex-row items-center rounded-2xl border-2 p-4 ${
232
+ selectedFeedbackStyle === style.id
233
+ ? style.accent
234
+ : 'border-neutral-200 bg-white dark:border-neutral-700 dark:bg-neutral-800'
235
+ }`}
236
+ >
237
+ <View
238
+ className={`mr-4 size-12 items-center justify-center rounded-xl ${style.color}`}
239
+ >
240
+ <Text className="text-xl">{style.icon}</Text>
241
+ </View>
242
+ <View className="flex-1">
243
+ <Text className="mb-1 text-base font-semibold text-neutral-900 dark:text-white">
244
+ {style.label}
245
+ </Text>
246
+ <Text className="text-sm text-neutral-600 dark:text-neutral-400">
247
+ {style.description}
248
+ </Text>
249
+ </View>
250
+ </TouchableOpacity>
251
+ ))}
252
+ </View>
253
+ </View>
254
+
255
+ {/* Selection Summary */}
256
+ <View className="mb-8 min-h-[100px]">
257
+ {selectedGoal || selectedFeedbackStyle ? (
258
+ <View className="rounded-2xl border border-border p-4 dark:bg-card">
259
+ <Text className="mb-2 text-lg font-semibold">
260
+ Your Selection
261
+ </Text>
262
+ {selectedGoalData && (
263
+ <Text className="mb-2 text-sm">
264
+ {selectedGoalData.icon} Goal: {selectedGoalData.label}
265
+ </Text>
266
+ )}
267
+ {selectedStyleData && (
268
+ <Text className="text-sm">
269
+ {selectedStyleData.icon} Tone: {selectedStyleData.label}
270
+ </Text>
271
+ )}
272
+ </View>
273
+ ) : (
274
+ <View className="rounded-2xl border border-neutral-200 bg-neutral-50 p-4 opacity-50 dark:border-neutral-700 dark:bg-neutral-800">
275
+ <Text className="mb-2 text-lg font-semibold text-neutral-500 dark:text-neutral-400">
276
+ Your Selection
277
+ </Text>
278
+ <Text className="text-sm text-neutral-400 dark:text-neutral-500">
279
+ Please select your analysis preferences above
280
+ </Text>
281
+ </View>
282
+ )}
283
+ </View>
284
+ </View>
285
+ </ScrollView>
286
+
287
+ <ScreenFooter
288
+ buttons={[
289
+ {
290
+ text: 'Back to Capture',
291
+ onPress: handleBackToCapture,
292
+ variant: 'outline',
293
+ },
294
+ {
295
+ text: 'Start Analysis',
296
+ onPress: handleStartAnalysis,
297
+ variant: 'default',
298
+ disabled: !selectedGoal || !selectedFeedbackStyle,
299
+ },
300
+ ]}
301
+ />
302
+ </>
303
+ );
304
+ }