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,96 @@
1
+ import { defaultCacheService } from '@/core/cache';
2
+
3
+ export type SavedImageMetadata = {
4
+ id: string;
5
+ prompt: string;
6
+ provider: string;
7
+ model: string;
8
+ savedAt: number;
9
+ localUri: string;
10
+ remoteImageId?: string;
11
+ storageId?: string;
12
+ };
13
+
14
+ const GALLERY_CACHE_KEY = 'image_gallery_v1';
15
+
16
+ /**
17
+ * Service for managing the local image gallery with MMKV persistence
18
+ */
19
+ export class ImageGalleryService {
20
+ /**
21
+ * Get all saved images from the gallery
22
+ */
23
+ static async getAllImages(): Promise<SavedImageMetadata[]> {
24
+ try {
25
+ const images =
26
+ await defaultCacheService.getItem<SavedImageMetadata[]>(
27
+ GALLERY_CACHE_KEY,
28
+ );
29
+ return images || [];
30
+ } catch (error) {
31
+ console.error('Failed to load gallery images:', error);
32
+ return [];
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Add a new image to the gallery
38
+ */
39
+ static async addImage(
40
+ metadata: Omit<SavedImageMetadata, 'id' | 'savedAt'>,
41
+ ): Promise<void> {
42
+ try {
43
+ const existingImages = await ImageGalleryService.getAllImages();
44
+ const newImage: SavedImageMetadata = {
45
+ ...metadata,
46
+ id: `img_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
47
+ savedAt: Date.now(),
48
+ };
49
+
50
+ const updatedImages = [newImage, ...existingImages];
51
+ await defaultCacheService.setItem(GALLERY_CACHE_KEY, updatedImages);
52
+ } catch (error) {
53
+ console.error('Failed to add image to gallery:', error);
54
+ throw error;
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Remove an image from the gallery
60
+ */
61
+ static async removeImage(imageId: string): Promise<void> {
62
+ try {
63
+ const existingImages = await ImageGalleryService.getAllImages();
64
+ const updatedImages = existingImages.filter((img) => img.id !== imageId);
65
+ await defaultCacheService.setItem(GALLERY_CACHE_KEY, updatedImages);
66
+ } catch (error) {
67
+ console.error('Failed to remove image from gallery:', error);
68
+ throw error;
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Clear all images from the gallery
74
+ */
75
+ static async clearGallery(): Promise<void> {
76
+ try {
77
+ await defaultCacheService.setItem(GALLERY_CACHE_KEY, []);
78
+ } catch (error) {
79
+ console.error('Failed to clear gallery:', error);
80
+ throw error;
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Get the total number of saved images
86
+ */
87
+ static async getImageCount(): Promise<number> {
88
+ try {
89
+ const images = await ImageGalleryService.getAllImages();
90
+ return images.length;
91
+ } catch (error) {
92
+ console.error('Failed to get image count:', error);
93
+ return 0;
94
+ }
95
+ }
96
+ }
@@ -0,0 +1,120 @@
1
+ import { File, Paths } from 'expo-file-system';
2
+ import * as MediaLibrary from 'expo-media-library';
3
+ import { Alert, Platform } from 'react-native';
4
+
5
+ import { ImageGalleryService } from './image-gallery-service';
6
+
7
+ export type ImageSaveMetadata = {
8
+ prompt: string;
9
+ provider: string;
10
+ model: string;
11
+ remoteImageId?: string;
12
+ storageId?: string;
13
+ };
14
+
15
+ /**
16
+ * Service for handling image saving operations to the device gallery and local storage
17
+ */
18
+ export class ImageSaveService {
19
+ /**
20
+ * Save a base64 image data URI to the device gallery and local gallery
21
+ * @param imageDataUri - The data URI containing the image (e.g., 'data:image/png;base64,iVBORw0KGgo...')
22
+ * @param metadata - Optional metadata about the image generation
23
+ * @returns Promise that resolves when save is complete
24
+ */
25
+ static async saveImageToGallery(
26
+ imageDataUri: string,
27
+ metadata?: ImageSaveMetadata,
28
+ ): Promise<void> {
29
+ try {
30
+ // Check if running on web - FileSystem is not available
31
+ if (Platform.OS === 'web') {
32
+ // On web, just save to local gallery
33
+ if (metadata) {
34
+ await ImageGalleryService.addImage({
35
+ prompt: metadata.prompt,
36
+ provider: metadata.provider,
37
+ model: metadata.model,
38
+ localUri: imageDataUri,
39
+ remoteImageId: metadata.remoteImageId,
40
+ storageId: metadata.storageId,
41
+ });
42
+ Alert.alert('Success!', 'Image saved to your gallery.');
43
+ }
44
+ return;
45
+ }
46
+
47
+ // Request permissions (native only)
48
+ const { status } = await MediaLibrary.requestPermissionsAsync();
49
+ if (status !== 'granted') {
50
+ Alert.alert(
51
+ 'Permission Required',
52
+ 'We need permission to save photos to your gallery.',
53
+ );
54
+ return;
55
+ }
56
+
57
+ // Extract base64 data from data URI (supports multiple formats)
58
+ const match = imageDataUri.match(/^data:image\/(\w+);base64,(.+)$/);
59
+ if (!match) {
60
+ throw new Error('Invalid image data format');
61
+ }
62
+ const [, imageFormat, base64Code] = match;
63
+ const fileExtension = imageFormat === 'jpeg' ? 'jpg' : imageFormat;
64
+
65
+ // Create temporary file using new File API
66
+ const filename = `generated-image-${Date.now()}.${fileExtension}`;
67
+ const file = new File(Paths.cache, filename);
68
+
69
+ // Create and write the file
70
+ file.create();
71
+ await file.write(base64Code, { encoding: 'base64' });
72
+
73
+ // Save to device gallery
74
+ const asset = await MediaLibrary.createAssetAsync(file.uri);
75
+ await MediaLibrary.createAlbumAsync('MyAppImages', asset, false);
76
+
77
+ // Save to local gallery if metadata is provided
78
+ if (metadata) {
79
+ await ImageGalleryService.addImage({
80
+ prompt: metadata.prompt,
81
+ provider: metadata.provider,
82
+ model: metadata.model,
83
+ localUri: imageDataUri,
84
+ remoteImageId: metadata.remoteImageId,
85
+ storageId: metadata.storageId,
86
+ });
87
+ }
88
+
89
+ Alert.alert('Success!', 'Image saved to your gallery.');
90
+ } catch (error) {
91
+ console.error('Failed to save image:', error);
92
+ Alert.alert('Error', 'Could not save the image.');
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Save image only to local gallery (without device gallery)
98
+ * @param imageDataUri - The data URI containing the image
99
+ * @param metadata - Metadata about the image generation
100
+ * @returns Promise that resolves when save is complete
101
+ */
102
+ static async saveToLocalGallery(
103
+ imageDataUri: string,
104
+ metadata: ImageSaveMetadata,
105
+ ): Promise<void> {
106
+ try {
107
+ await ImageGalleryService.addImage({
108
+ prompt: metadata.prompt,
109
+ provider: metadata.provider,
110
+ model: metadata.model,
111
+ localUri: imageDataUri,
112
+ remoteImageId: metadata.remoteImageId,
113
+ storageId: metadata.storageId,
114
+ });
115
+ } catch (error) {
116
+ console.error('Failed to save to local gallery:', error);
117
+ throw error;
118
+ }
119
+ }
120
+ }
@@ -0,0 +1,291 @@
1
+ /**
2
+ * Generate Image Edge Function
3
+ *
4
+ * Handles AI image generation with JWT authentication.
5
+ * Supports OpenAI (DALL-E 3) and Google Gemini providers.
6
+ *
7
+ * Requirements: 6.1, 6.2, 6.3, 6.4, 6.5
8
+ */
9
+
10
+ import 'jsr:@supabase/functions-js/edge-runtime.d.ts';
11
+ import { createClient } from 'npm:@supabase/supabase-js@2';
12
+
13
+ // CORS headers for browser requests
14
+ const corsHeaders = {
15
+ 'Access-Control-Allow-Origin': '*',
16
+ 'Access-Control-Allow-Headers':
17
+ 'authorization, x-client-info, apikey, content-type',
18
+ 'Access-Control-Allow-Methods': 'POST, OPTIONS',
19
+ };
20
+
21
+ interface GenerateImageRequest {
22
+ prompt: string;
23
+ provider?: 'openai' | 'gemini';
24
+ model?: string;
25
+ size?: string;
26
+ quality?: 'standard' | 'hd';
27
+ }
28
+
29
+ interface GenerateImageResponse {
30
+ success: boolean;
31
+ imageUrl?: string;
32
+ imageDataUri?: string;
33
+ storageId?: string;
34
+ recordId?: string;
35
+ provider: string;
36
+ model: string;
37
+ error?: string;
38
+ }
39
+
40
+ Deno.serve(async (req: Request) => {
41
+ // Handle CORS preflight
42
+ if (req.method === 'OPTIONS') {
43
+ return new Response('ok', { headers: corsHeaders });
44
+ }
45
+
46
+ if (req.method !== 'POST') {
47
+ return new Response(JSON.stringify({ error: 'Method not allowed' }), {
48
+ status: 405,
49
+ headers: { ...corsHeaders, 'Content-Type': 'application/json' },
50
+ });
51
+ }
52
+
53
+ try {
54
+ // 6.1: Authenticate the request
55
+ const authHeader = req.headers.get('Authorization');
56
+ if (!authHeader) {
57
+ return new Response(JSON.stringify({ error: 'Missing authorization header' }), {
58
+ status: 401,
59
+ headers: { ...corsHeaders, 'Content-Type': 'application/json' },
60
+ });
61
+ }
62
+
63
+ const token = authHeader.replace('Bearer ', '');
64
+
65
+ // Create Supabase client with user's JWT
66
+ const supabaseUrl = Deno.env.get('SUPABASE_URL')!;
67
+ const supabasePublishableKey = Deno.env.get('SUPABASE_ANON_KEY')!;
68
+ const supabaseSecretKey = Deno.env.get('SUPABASE_SECRET_KEY')!;
69
+
70
+ const supabase = createClient(supabaseUrl, supabasePublishableKey, {
71
+ global: { headers: { Authorization: `Bearer ${token}` } },
72
+ });
73
+
74
+ // Admin client for storage operations
75
+ const supabaseAdmin = createClient(supabaseUrl, supabaseSecretKey);
76
+
77
+ // Verify the user
78
+ const { data: { user }, error: authError } = await supabase.auth.getUser();
79
+ if (authError || !user) {
80
+ return new Response(JSON.stringify({ error: 'Invalid or expired token' }), {
81
+ status: 401,
82
+ headers: { ...corsHeaders, 'Content-Type': 'application/json' },
83
+ });
84
+ }
85
+
86
+ // Parse request body
87
+ const body: GenerateImageRequest = await req.json();
88
+ const { prompt, provider = 'openai', model, size = '1024x1024', quality = 'standard' } = body;
89
+
90
+ if (!prompt || prompt.trim().length === 0) {
91
+ return new Response(JSON.stringify({ error: 'Prompt is required' }), {
92
+ status: 400,
93
+ headers: { ...corsHeaders, 'Content-Type': 'application/json' },
94
+ });
95
+ }
96
+
97
+ let imageData: { url?: string; b64_json?: string } | null = null;
98
+ let usedProvider: string;
99
+ let usedModel: string;
100
+
101
+ if (provider === 'gemini') {
102
+ // 6.3: Generate with Gemini provider
103
+ const geminiApiKey = Deno.env.get('GEMINI_API_KEY');
104
+ if (!geminiApiKey) {
105
+ return new Response(JSON.stringify({ error: 'Gemini API not configured' }), {
106
+ status: 500,
107
+ headers: { ...corsHeaders, 'Content-Type': 'application/json' },
108
+ });
109
+ }
110
+
111
+ usedModel = model || 'gemini-2.0-flash-preview-image-generation';
112
+ usedProvider = 'gemini';
113
+
114
+ // Use Gemini's image generation API
115
+ const geminiResponse = await fetch(
116
+ `https://generativelanguage.googleapis.com/v1beta/models/${usedModel}:generateContent?key=${geminiApiKey}`,
117
+ {
118
+ method: 'POST',
119
+ headers: { 'Content-Type': 'application/json' },
120
+ body: JSON.stringify({
121
+ contents: [
122
+ {
123
+ parts: [{ text: `Generate an image: ${prompt}` }],
124
+ },
125
+ ],
126
+ generationConfig: {
127
+ responseModalities: ['TEXT', 'IMAGE'],
128
+ },
129
+ }),
130
+ }
131
+ );
132
+
133
+ if (!geminiResponse.ok) {
134
+ const errorText = await geminiResponse.text();
135
+ console.error('Gemini API error:', errorText);
136
+ return new Response(
137
+ JSON.stringify({ error: 'Failed to generate image with Gemini' }),
138
+ {
139
+ status: 500,
140
+ headers: { ...corsHeaders, 'Content-Type': 'application/json' },
141
+ }
142
+ );
143
+ }
144
+
145
+ const geminiData = await geminiResponse.json();
146
+
147
+ // Extract image from Gemini response
148
+ const parts = geminiData.candidates?.[0]?.content?.parts || [];
149
+ const imagePart = parts.find((p: any) => p.inlineData?.mimeType?.startsWith('image/'));
150
+
151
+ if (imagePart?.inlineData) {
152
+ imageData = { b64_json: imagePart.inlineData.data };
153
+ } else {
154
+ return new Response(
155
+ JSON.stringify({ error: 'No image generated by Gemini' }),
156
+ {
157
+ status: 500,
158
+ headers: { ...corsHeaders, 'Content-Type': 'application/json' },
159
+ }
160
+ );
161
+ }
162
+ } else {
163
+ // 6.2: Generate with OpenAI provider (DALL-E 3)
164
+ const openaiApiKey = Deno.env.get('OPENAI_API_KEY');
165
+ if (!openaiApiKey) {
166
+ return new Response(JSON.stringify({ error: 'OpenAI API not configured' }), {
167
+ status: 500,
168
+ headers: { ...corsHeaders, 'Content-Type': 'application/json' },
169
+ });
170
+ }
171
+
172
+ usedModel = model || 'dall-e-3';
173
+ usedProvider = 'openai';
174
+
175
+ const openaiResponse = await fetch('https://api.openai.com/v1/images/generations', {
176
+ method: 'POST',
177
+ headers: {
178
+ 'Content-Type': 'application/json',
179
+ Authorization: `Bearer ${openaiApiKey}`,
180
+ },
181
+ body: JSON.stringify({
182
+ model: usedModel,
183
+ prompt,
184
+ n: 1,
185
+ size,
186
+ quality,
187
+ response_format: 'b64_json',
188
+ }),
189
+ });
190
+
191
+ if (!openaiResponse.ok) {
192
+ const errorData = await openaiResponse.json();
193
+ console.error('OpenAI API error:', errorData);
194
+ return new Response(
195
+ JSON.stringify({ error: errorData.error?.message || 'Failed to generate image' }),
196
+ {
197
+ status: 500,
198
+ headers: { ...corsHeaders, 'Content-Type': 'application/json' },
199
+ }
200
+ );
201
+ }
202
+
203
+ const openaiData = await openaiResponse.json();
204
+ imageData = openaiData.data?.[0];
205
+ }
206
+
207
+ if (!imageData) {
208
+ return new Response(JSON.stringify({ error: 'No image data received' }), {
209
+ status: 500,
210
+ headers: { ...corsHeaders, 'Content-Type': 'application/json' },
211
+ });
212
+ }
213
+
214
+ // 6.4: Store image in Supabase Storage and create generated_images record
215
+ let storageId: string | null = null;
216
+ let imageUrl: string | null = null;
217
+
218
+ if (imageData.b64_json) {
219
+ // Decode base64 and upload to storage
220
+ const imageBuffer = Uint8Array.from(atob(imageData.b64_json), (c) => c.charCodeAt(0));
221
+ const filename = `${user.id}/${crypto.randomUUID()}.png`;
222
+
223
+ const { data: uploadData, error: uploadError } = await supabaseAdmin.storage
224
+ .from('generated-images')
225
+ .upload(filename, imageBuffer, {
226
+ contentType: 'image/png',
227
+ upsert: false,
228
+ });
229
+
230
+ if (uploadError) {
231
+ console.error('Storage upload error:', uploadError);
232
+ } else {
233
+ storageId = uploadData.path;
234
+
235
+ // Get signed URL for the image
236
+ const { data: signedUrlData } = await supabaseAdmin.storage
237
+ .from('generated-images')
238
+ .createSignedUrl(filename, 3600); // 1 hour expiry
239
+
240
+ imageUrl = signedUrlData?.signedUrl || null;
241
+ }
242
+ } else if (imageData.url) {
243
+ imageUrl = imageData.url;
244
+ }
245
+
246
+ // Create generated_images record
247
+ const { data: record, error: recordError } = await supabase
248
+ .from('generated_images')
249
+ .insert({
250
+ user_id: user.id,
251
+ prompt,
252
+ image_url: imageUrl,
253
+ storage_id: storageId,
254
+ mime_type: 'image/png',
255
+ provider: usedProvider,
256
+ model: usedModel,
257
+ })
258
+ .select('id')
259
+ .single();
260
+
261
+ if (recordError) {
262
+ console.error('Error creating generated_images record:', recordError);
263
+ }
264
+
265
+ // 6.5: Return the image data URI and metadata
266
+ const response: GenerateImageResponse = {
267
+ success: true,
268
+ imageUrl: imageUrl || undefined,
269
+ imageDataUri: imageData.b64_json ? `data:image/png;base64,${imageData.b64_json}` : undefined,
270
+ storageId: storageId || undefined,
271
+ recordId: record?.id,
272
+ provider: usedProvider,
273
+ model: usedModel,
274
+ };
275
+
276
+ return new Response(JSON.stringify(response), {
277
+ headers: { ...corsHeaders, 'Content-Type': 'application/json' },
278
+ });
279
+ } catch (error) {
280
+ console.error('Generate image error:', error);
281
+ return new Response(
282
+ JSON.stringify({
283
+ error: error instanceof Error ? error.message : 'Internal server error',
284
+ }),
285
+ {
286
+ status: 500,
287
+ headers: { ...corsHeaders, 'Content-Type': 'application/json' },
288
+ }
289
+ );
290
+ }
291
+ });
@@ -0,0 +1,71 @@
1
+ -- Image Generator Feature Migration
2
+ -- Creates tables for AI-generated images
3
+ -- Run this migration when adding the image-generator feature via CLI
4
+
5
+ -- ============================================================================
6
+ -- GENERATED_IMAGES TABLE
7
+ -- Stores AI-generated images metadata
8
+ -- ============================================================================
9
+ CREATE TABLE IF NOT EXISTS generated_images (
10
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
11
+ user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
12
+ prompt TEXT NOT NULL,
13
+ image_url TEXT,
14
+ storage_id TEXT,
15
+ mime_type TEXT,
16
+ provider TEXT NOT NULL,
17
+ model TEXT NOT NULL,
18
+ created_at TIMESTAMPTZ DEFAULT now()
19
+ );
20
+
21
+ -- ============================================================================
22
+ -- INDEXES
23
+ -- ============================================================================
24
+ CREATE INDEX IF NOT EXISTS idx_generated_images_user_id ON generated_images(user_id);
25
+
26
+ -- ============================================================================
27
+ -- RLS POLICIES
28
+ -- ============================================================================
29
+ ALTER TABLE generated_images ENABLE ROW LEVEL SECURITY;
30
+
31
+ CREATE POLICY "generated_images_select_own" ON generated_images
32
+ FOR SELECT USING (auth.uid() = user_id);
33
+
34
+ CREATE POLICY "generated_images_insert_own" ON generated_images
35
+ FOR INSERT WITH CHECK (auth.uid() = user_id);
36
+
37
+ CREATE POLICY "generated_images_update_own" ON generated_images
38
+ FOR UPDATE USING (auth.uid() = user_id);
39
+
40
+ CREATE POLICY "generated_images_delete_own" ON generated_images
41
+ FOR DELETE USING (auth.uid() = user_id);
42
+
43
+ -- ============================================================================
44
+ -- STORAGE BUCKET - generated-images
45
+ -- ============================================================================
46
+ INSERT INTO storage.buckets (id, name, public, file_size_limit, allowed_mime_types)
47
+ VALUES (
48
+ 'generated-images',
49
+ 'generated-images',
50
+ false,
51
+ 10485760, -- 10MB limit
52
+ ARRAY['image/jpeg', 'image/png', 'image/webp']
53
+ ) ON CONFLICT (id) DO NOTHING;
54
+
55
+ -- Storage RLS for generated-images
56
+ CREATE POLICY "Users can view their own generated images"
57
+ ON storage.objects FOR SELECT TO authenticated
58
+ USING (bucket_id = 'generated-images' AND (storage.foldername(name))[1] = auth.uid()::text);
59
+
60
+ CREATE POLICY "Users can upload their own generated images"
61
+ ON storage.objects FOR INSERT TO authenticated
62
+ WITH CHECK (bucket_id = 'generated-images' AND (storage.foldername(name))[1] = auth.uid()::text);
63
+
64
+ CREATE POLICY "Users can update their own generated images"
65
+ ON storage.objects FOR UPDATE TO authenticated
66
+ USING (bucket_id = 'generated-images' AND (storage.foldername(name))[1] = auth.uid()::text)
67
+ WITH CHECK (bucket_id = 'generated-images' AND (storage.foldername(name))[1] = auth.uid()::text);
68
+
69
+ CREATE POLICY "Users can delete their own generated images"
70
+ ON storage.objects FOR DELETE TO authenticated
71
+ USING (bucket_id = 'generated-images' AND (storage.foldername(name))[1] = auth.uid()::text);
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "image-generator",
3
+ "version": "1.0.0",
4
+ "description": "AI-powered image generation with Supabase backend",
5
+ "copy": [
6
+ {
7
+ "from": "apps/native/src/app/image-generator",
8
+ "to": "apps/native/src/app/(root)/(protected)/image-generator"
9
+ },
10
+ {
11
+ "from": "apps/native/src/features/image-generator",
12
+ "to": "apps/native/src/features/image-generator"
13
+ },
14
+ {
15
+ "from": "packages/backend/supabase/functions/generate-image",
16
+ "to": "packages/backend/supabase/functions/generate-image"
17
+ },
18
+ {
19
+ "from": "packages/backend/supabase/migrations/image_generator.sql",
20
+ "to": "packages/backend/supabase/migrations/image_generator.sql"
21
+ }
22
+ ],
23
+ "nav": {
24
+ "href": "/(root)/(protected)/image-generator",
25
+ "label": "Image Generator",
26
+ "icon": "🎨",
27
+ "color": "#8B5CF6"
28
+ },
29
+ "target": "native",
30
+ "dependencies": {
31
+ "expo": [
32
+ "@supabase/supabase-js",
33
+ "@tanstack/react-query",
34
+ "expo-file-system",
35
+ "expo-media-library",
36
+ "expo-sharing"
37
+ ]
38
+ },
39
+ "env": [
40
+ {
41
+ "key": "OPENAI_API_KEY",
42
+ "description": "OpenAI API key for DALL-E image generation",
43
+ "example": "sk-...",
44
+ "link": "https://platform.openai.com/api-keys",
45
+ "file": "packages/backend/supabase/.env",
46
+ "required": true
47
+ }
48
+ ],
49
+ "manualSteps": [
50
+ {
51
+ "title": "Apply Supabase migration",
52
+ "description": "Run: cd packages/backend && supabase db push"
53
+ },
54
+ {
55
+ "title": "Deploy Edge Function",
56
+ "description": "Run: cd packages/backend && supabase functions deploy generate-image"
57
+ }
58
+ ]
59
+ }
Binary file