vibefast-cli 1.1.5 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (299) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +63 -169
  3. package/dist/commands/add.d.ts +1 -1
  4. package/dist/commands/add.d.ts.map +1 -1
  5. package/dist/commands/add.js +547 -589
  6. package/dist/commands/add.js.map +1 -1
  7. package/dist/commands/checklist.d.ts +1 -1
  8. package/dist/commands/checklist.d.ts.map +1 -1
  9. package/dist/commands/checklist.js +40 -39
  10. package/dist/commands/checklist.js.map +1 -1
  11. package/dist/commands/doctor.d.ts +1 -1
  12. package/dist/commands/doctor.js +22 -22
  13. package/dist/commands/doctor.js.map +1 -1
  14. package/dist/commands/env.d.ts +1 -1
  15. package/dist/commands/env.d.ts.map +1 -1
  16. package/dist/commands/env.js +58 -53
  17. package/dist/commands/env.js.map +1 -1
  18. package/dist/commands/health.d.ts +1 -1
  19. package/dist/commands/health.d.ts.map +1 -1
  20. package/dist/commands/health.js +101 -93
  21. package/dist/commands/health.js.map +1 -1
  22. package/dist/commands/init.d.ts +1 -1
  23. package/dist/commands/init.d.ts.map +1 -1
  24. package/dist/commands/init.js +416 -296
  25. package/dist/commands/init.js.map +1 -1
  26. package/dist/commands/remove.d.ts +1 -1
  27. package/dist/commands/remove.d.ts.map +1 -1
  28. package/dist/commands/remove.js +77 -64
  29. package/dist/commands/remove.js.map +1 -1
  30. package/dist/commands/status.d.ts +1 -1
  31. package/dist/commands/status.d.ts.map +1 -1
  32. package/dist/commands/status.js +15 -14
  33. package/dist/commands/status.js.map +1 -1
  34. package/dist/core/__tests__/detect.test.js +68 -34
  35. package/dist/core/__tests__/detect.test.js.map +1 -1
  36. package/dist/core/ast.d.ts +14 -0
  37. package/dist/core/ast.d.ts.map +1 -0
  38. package/dist/core/ast.js +239 -0
  39. package/dist/core/ast.js.map +1 -0
  40. package/dist/core/codemod.d.ts.map +1 -1
  41. package/dist/core/codemod.js +62 -44
  42. package/dist/core/codemod.js.map +1 -1
  43. package/dist/core/config.d.ts +10 -0
  44. package/dist/core/config.d.ts.map +1 -0
  45. package/dist/core/config.js +51 -0
  46. package/dist/core/config.js.map +1 -0
  47. package/dist/core/detect.d.ts +8 -2
  48. package/dist/core/detect.d.ts.map +1 -1
  49. package/dist/core/detect.js +52 -21
  50. package/dist/core/detect.js.map +1 -1
  51. package/dist/core/errors.d.ts.map +1 -1
  52. package/dist/core/errors.js +9 -8
  53. package/dist/core/errors.js.map +1 -1
  54. package/dist/core/exec.d.ts +16 -0
  55. package/dist/core/exec.d.ts.map +1 -0
  56. package/dist/core/exec.js +48 -0
  57. package/dist/core/exec.js.map +1 -0
  58. package/dist/core/manualSteps.d.ts +7 -0
  59. package/dist/core/manualSteps.d.ts.map +1 -0
  60. package/dist/core/manualSteps.js +59 -0
  61. package/dist/core/manualSteps.js.map +1 -0
  62. package/dist/core/paths.d.ts +3 -1
  63. package/dist/core/paths.d.ts.map +1 -1
  64. package/dist/core/paths.js +14 -10
  65. package/dist/core/paths.js.map +1 -1
  66. package/dist/core/spinner.d.ts +1 -1
  67. package/dist/core/spinner.d.ts.map +1 -1
  68. package/dist/core/spinner.js +38 -8
  69. package/dist/core/spinner.js.map +1 -1
  70. package/dist/core/vosk.d.ts.map +1 -1
  71. package/dist/core/vosk.js +50 -39
  72. package/dist/core/vosk.js.map +1 -1
  73. package/docs/manual-testing.md +91 -0
  74. package/package.json +6 -3
  75. package/recipes/audio-recorder/apps/native/src/app/audio-recorder/index.tsx +5 -0
  76. package/recipes/audio-recorder/recipe.json +3 -3
  77. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/audio-player.tsx +301 -0
  78. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/audio-recorder.tsx +373 -0
  79. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/audio-waveform.tsx +270 -0
  80. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/index.ts +4 -0
  81. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/recording-list.tsx +89 -0
  82. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/audio-player-demo.tsx +66 -0
  83. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/audio-recorder-cloud.tsx +68 -0
  84. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/audio-recorder-interview.tsx +102 -0
  85. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/basic.tsx +27 -0
  86. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/index.ts +5 -0
  87. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/with-recording-list-demo.tsx +82 -0
  88. package/recipes/audio-recorder-supabase/packages/backend/src/services/recordings.ts +369 -0
  89. package/recipes/audio-recorder-supabase/packages/backend/supabase/migrations/recordings.sql +70 -0
  90. package/recipes/audio-recorder-supabase/recipe.json +35 -0
  91. package/recipes/audio-recorder-supabase@latest.zip +0 -0
  92. package/recipes/audio-recorder@latest.zip +0 -0
  93. package/recipes/charts/apps/native/src/features/charts/components/bar-chart.tsx +3 -3
  94. package/recipes/charts/apps/native/src/features/charts/components/candlestick-chart.tsx +2 -2
  95. package/recipes/charts/apps/native/src/features/charts/components/chart-card.tsx +5 -5
  96. package/recipes/charts/apps/native/src/features/charts/components/column-chart.tsx +3 -3
  97. package/recipes/charts/apps/native/src/features/charts/components/doughnut-chart.tsx +20 -4
  98. package/recipes/charts/apps/native/src/features/charts/components/line-chart.tsx +7 -6
  99. package/recipes/charts/apps/native/src/features/charts/components/radar-chart.tsx +6 -4
  100. package/recipes/charts/apps/native/src/features/charts/components/radial-bar-chart.tsx +1 -1
  101. package/recipes/charts/apps/native/src/features/charts/components/stacked-bar-chart.tsx +5 -4
  102. package/recipes/charts/recipe.json +4 -13
  103. package/recipes/charts@latest.zip +0 -0
  104. package/recipes/chatbot/apps/native/src/app/chatbot/index.tsx +1 -0
  105. package/recipes/chatbot/apps/native/src/features/chatbot/components/chat-markdown.tsx +86 -86
  106. package/recipes/chatbot/apps/native/src/features/chatbot/components/markdown/code-block.tsx +86 -53
  107. package/recipes/chatbot/recipe.json +26 -92
  108. package/recipes/chatbot-supabase/apps/native/src/api-client/supabase/chatbot.ts +515 -0
  109. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/app/index.tsx +257 -0
  110. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-header-buttons.tsx +59 -0
  111. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-input-bar.tsx +485 -0
  112. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-markdown.tsx +575 -0
  113. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-message-bubble.tsx +223 -0
  114. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-settings-modal.tsx +161 -0
  115. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/image-preview-list.tsx +116 -0
  116. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/markdown/code-block.tsx +165 -0
  117. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/markdown/index.ts +10 -0
  118. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/markdown/table-renderer.tsx +129 -0
  119. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/message-error-boundary.tsx +78 -0
  120. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/message-list.tsx +170 -0
  121. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/model-selector.tsx +283 -0
  122. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/report-content-modal.tsx +188 -0
  123. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/suggested-messages.tsx +67 -0
  124. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/constants/models.ts +20 -0
  125. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/constants/report-reasons.ts +9 -0
  126. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-attachment-cache.ts +142 -0
  127. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-chat-config.ts +458 -0
  128. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-chat-handlers.ts +429 -0
  129. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-chatbot-settings.ts +89 -0
  130. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-conversation.ts +90 -0
  131. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-image-picker.ts +122 -0
  132. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-keyboard-coordinator.ts +161 -0
  133. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-smart-scroll-manager.ts +213 -0
  134. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/models/index.ts +86 -0
  135. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/models/models.ts +162 -0
  136. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/models/providers.ts +62 -0
  137. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/models/types.ts +40 -0
  138. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/services/file-uploader.ts +287 -0
  139. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/services/message-handler-service.ts +189 -0
  140. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/types/index.ts +70 -0
  141. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/utils/chat-telemetry.ts +91 -0
  142. package/recipes/chatbot-supabase/packages/backend/src/services/conversations.ts +243 -0
  143. package/recipes/chatbot-supabase/packages/backend/src/services/messages.ts +327 -0
  144. package/recipes/chatbot-supabase/packages/backend/supabase/functions/chat-stream/index.ts +347 -0
  145. package/recipes/chatbot-supabase/packages/backend/supabase/migrations/chatbot.sql +104 -0
  146. package/recipes/chatbot-supabase/recipe.json +79 -0
  147. package/recipes/chatbot-supabase@latest.zip +0 -0
  148. package/recipes/chatbot.zip +0 -0
  149. package/recipes/chatbot@latest.zip +0 -0
  150. package/recipes/image-analysis/packages/backend/convex/imageAnalysis/index.ts +2 -2
  151. package/recipes/image-analysis/packages/backend/convex/imageAnalysis.ts +0 -1
  152. package/recipes/image-analysis/recipe.json +15 -55
  153. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/analysis-options-screen.tsx +304 -0
  154. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/camera.tsx +221 -0
  155. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/image-capture-screen.tsx +333 -0
  156. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/loading-screen.tsx +214 -0
  157. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/loading.tsx +191 -0
  158. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/results.tsx +137 -0
  159. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/trait-details.tsx +172 -0
  160. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/use-analysis-data.ts +160 -0
  161. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/use-results-screen.ts +151 -0
  162. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/achievement-badge.tsx +77 -0
  163. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/achievement-card.tsx +75 -0
  164. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/achievement-unlocked-modal.tsx +162 -0
  165. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/achievements-section.tsx +44 -0
  166. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/advice-list.tsx +42 -0
  167. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/circular-progress.tsx +233 -0
  168. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/content-card.tsx +38 -0
  169. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/error-state.tsx +42 -0
  170. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/index.ts +9 -0
  171. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/loading-state.tsx +26 -0
  172. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/profile-image.tsx +60 -0
  173. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/results-header.tsx +62 -0
  174. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/score-display.tsx +54 -0
  175. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/share-options-modal.tsx +110 -0
  176. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/traits-grid.tsx +74 -0
  177. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/config/analysis-config.ts +80 -0
  178. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/config/master-analysis-config.ts +157 -0
  179. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/hooks/index.ts +1 -0
  180. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/hooks/use-analysis.ts +38 -0
  181. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/hooks/use-image-analysis.ts +208 -0
  182. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/services/analysis-service.ts +262 -0
  183. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/services/share-service.ts +176 -0
  184. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/services/trait-details-service.ts +289 -0
  185. package/recipes/image-analysis-supabase/packages/backend/src/services/image-analyses.ts +132 -0
  186. package/recipes/image-analysis-supabase/packages/backend/supabase/functions/analyze-image/index.ts +312 -0
  187. package/recipes/image-analysis-supabase/packages/backend/supabase/migrations/image_analysis.sql +42 -0
  188. package/recipes/image-analysis-supabase/recipe.json +57 -0
  189. package/recipes/image-analysis-supabase@latest.zip +0 -0
  190. package/recipes/image-analysis@latest.zip +0 -0
  191. package/recipes/image-generator/apps/native/src/features/image-generator/app/index.tsx +16 -2
  192. package/recipes/image-generator/apps/native/src/features/image-generator/components/image-model-selector.tsx +11 -5
  193. package/recipes/image-generator/apps/native/src/features/image-generator/hooks/use-image-generator.ts +11 -5
  194. package/recipes/image-generator/packages/backend/convex/imageGeneration/index.ts +2 -2
  195. package/recipes/image-generator/recipe.json +16 -39
  196. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/app/_layout.tsx +26 -0
  197. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/app/gallery.tsx +217 -0
  198. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/app/index.tsx +251 -0
  199. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/components/gallery-image.tsx +25 -0
  200. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/components/image-detail-modal.tsx +215 -0
  201. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/components/image-model-selector.tsx +216 -0
  202. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/components/image-placeholder.tsx +26 -0
  203. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/hooks/use-image-gallery.ts +71 -0
  204. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/hooks/use-image-generator-settings.ts +152 -0
  205. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/hooks/use-image-generator.ts +103 -0
  206. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/models/models.ts +66 -0
  207. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/services/image-gallery-service.ts +96 -0
  208. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/services/image-save-service.ts +120 -0
  209. package/recipes/image-generator-supabase/packages/backend/supabase/functions/generate-image/index.ts +291 -0
  210. package/recipes/image-generator-supabase/packages/backend/supabase/migrations/image_generator.sql +71 -0
  211. package/recipes/image-generator-supabase/recipe.json +59 -0
  212. package/recipes/image-generator-supabase@latest.zip +0 -0
  213. package/recipes/image-generator@latest.zip +0 -0
  214. package/recipes/ios-widget/recipe.json +15 -24
  215. package/recipes/ios-widget@latest.zip +0 -0
  216. package/recipes/onboarding/apps/native/src/features/onboarding/analytics/index.ts +9 -0
  217. package/recipes/onboarding/apps/native/src/features/onboarding/components/onboarding-with-analytics.tsx +141 -0
  218. package/recipes/onboarding/apps/native/src/features/onboarding/components/onboarding.tsx +173 -0
  219. package/recipes/onboarding/apps/native/src/features/onboarding/config/onboarding-flow-config.ts +189 -0
  220. package/recipes/onboarding/apps/native/src/features/onboarding/demo-one/app/index.tsx +42 -0
  221. package/recipes/onboarding/apps/native/src/features/onboarding/demo-one/data.ts +32 -0
  222. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/app/index.tsx +43 -0
  223. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/interactive-onboarding.tsx +222 -0
  224. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/ai-tone-step.tsx +133 -0
  225. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/currency-step.tsx +165 -0
  226. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-ai-step.tsx +199 -0
  227. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-chatbot-step.tsx +154 -0
  228. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-manual-step.tsx +156 -0
  229. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-scan-step.tsx +158 -0
  230. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/main-reason-step.tsx +139 -0
  231. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/notification-step.tsx +129 -0
  232. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/overspend-step.tsx +138 -0
  233. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/personalizing-step.tsx +190 -0
  234. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/rating-step.tsx +98 -0
  235. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/reminder-step.tsx +181 -0
  236. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/safety-step.tsx +110 -0
  237. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/struggle-step.tsx +139 -0
  238. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/welcome-step.tsx +217 -0
  239. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/ui/onboarding-header.tsx +58 -0
  240. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/constants.ts +179 -0
  241. package/recipes/onboarding/apps/native/src/features/onboarding/hooks/use-onboarding-analytics.ts +323 -0
  242. package/recipes/onboarding/apps/native/src/features/onboarding/services/onboarding-analytics.ts +432 -0
  243. package/recipes/onboarding/recipe.json +15 -0
  244. package/recipes/onboarding@latest.zip +0 -0
  245. package/recipes/payments/recipe.json +28 -61
  246. package/recipes/payments-supabase/apps/native/src/features/payments/README.md +200 -0
  247. package/recipes/payments-supabase/apps/native/src/features/payments/app/local-paywall.tsx +194 -0
  248. package/recipes/payments-supabase/apps/native/src/features/payments/app/remote-paywall.tsx +79 -0
  249. package/recipes/payments-supabase/apps/native/src/features/payments/components/payment-initializer.tsx +95 -0
  250. package/recipes/payments-supabase/apps/native/src/features/payments/components/paywall-error-state.tsx +60 -0
  251. package/recipes/payments-supabase/apps/native/src/features/payments/components/paywall-local-mode.tsx +116 -0
  252. package/recipes/payments-supabase/apps/native/src/features/payments/components/paywall-product-card.tsx +133 -0
  253. package/recipes/payments-supabase/apps/native/src/features/payments/components/paywall-remote-mode.tsx +146 -0
  254. package/recipes/payments-supabase/apps/native/src/features/payments/hooks/use-entitlement.ts +63 -0
  255. package/recipes/payments-supabase/apps/native/src/features/payments/index.ts +8 -0
  256. package/recipes/payments-supabase/apps/native/src/features/payments/services/revenuecat-adapter.ts +407 -0
  257. package/recipes/payments-supabase/packages/backend/src/services/payments.ts +201 -0
  258. package/recipes/payments-supabase/packages/backend/supabase/migrations/payments.sql +35 -0
  259. package/recipes/payments-supabase/recipe.json +51 -0
  260. package/recipes/payments-supabase@latest.zip +0 -0
  261. package/recipes/payments@latest.zip +0 -0
  262. package/recipes/quiz/apps/native/src/features/quiz/index.tsx +1 -2
  263. package/recipes/quiz/recipe.json +6 -9
  264. package/recipes/quiz@latest.zip +0 -0
  265. package/recipes/tracker-app/apps/native/src/features/tracker-app/app/index.tsx +1 -2
  266. package/recipes/tracker-app/recipe.json +7 -10
  267. package/recipes/tracker-app@latest.zip +0 -0
  268. package/recipes/voice-bot/recipe.json +8 -68
  269. package/recipes/voice-bot.zip +0 -0
  270. package/recipes/voice-bot@latest.zip +0 -0
  271. package/recipes/wake-word/recipe.json +10 -9
  272. package/recipes/wake-word.zip +0 -0
  273. package/recipes/wake-word@latest.zip +0 -0
  274. package/recipes/charts/apps/native/src/app/(root)/(protected)/charts/index.tsx +0 -3
  275. package/recipes/chatbot/packages/backend/convex/lib/rateLimit.ts +0 -100
  276. package/recipes/chatbot/packages/backend/convex/lib/telemetry.ts +0 -29
  277. package/recipes/chatbot/packages/backend/convex/ragKnowledge.ts +0 -0
  278. package/recipes/image-analysis/apps/native/assets/features/image-analyzer/front.jpg +0 -0
  279. package/recipes/image-analysis/apps/native/assets/features/image-analyzer/side.jpg +0 -0
  280. package/recipes/image-analysis/apps/native/assets/features/image-analyzer/threeQuarter.jpg +0 -0
  281. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/_layout.tsx +0 -5
  282. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/analysis-options.tsx +0 -50
  283. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/camera.tsx +0 -2
  284. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/index.tsx +0 -50
  285. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/loading.tsx +0 -50
  286. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/results.tsx +0 -2
  287. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/trait-details.tsx +0 -3
  288. package/recipes/image-analysis/packages/backend/convex/imageAnalysisFunctions.ts +0 -325
  289. package/recipes/image-analysis/packages/backend/convex/lib/ai/imageAnalysisAdapter.ts +0 -200
  290. package/recipes/payments/apps/native/src/app/(root)/(protected)/paywall/index.tsx +0 -74
  291. package/recipes/payments/apps/native/src/app/(root)/(protected)/paywall/local.tsx +0 -25
  292. package/recipes/payments/apps/native/src/app/(root)/(protected)/paywall/remote.tsx +0 -23
  293. package/recipes/quiz/apps/native/src/app/(root)/(protected)/quiz/index.tsx +0 -47
  294. package/recipes/tracker-app/apps/native/src/app/(root)/(protected)/tracker-app/index.tsx +0 -1
  295. package/recipes/voice-bot/apps/native/src/app/(root)/(protected)/voice-bot/index.tsx +0 -27
  296. package/recipes/voice-bot/packages/backend/convex/router.ts +0 -81
  297. /package/recipes/{chatbot/apps/native/src/app/(root)/(protected) → chatbot-supabase/apps/native/src/app}/chatbot/index.tsx +0 -0
  298. /package/recipes/{image-generator/apps/native/src/app/(root)/(protected) → image-generator-supabase/apps/native/src/app}/image-generator/gallery.tsx +0 -0
  299. /package/recipes/{image-generator/apps/native/src/app/(root)/(protected) → image-generator-supabase/apps/native/src/app}/image-generator/index.tsx +0 -0
@@ -0,0 +1,216 @@
1
+ import { Feather } from '@expo/vector-icons';
2
+ import type React from 'react';
3
+ import { ScrollView, View } from 'react-native';
4
+
5
+ import { Pressable, Text } from '@/components/ui';
6
+ import colors from '@/components/ui/colors';
7
+ import { Modal, useModal } from '@/components/ui/core/overlays/modal';
8
+ import { GeminiIcon } from '@/components/ui/icons/gemini';
9
+ import { OpenaiIcon } from '@/components/ui/icons/openai';
10
+ import type { ImageGeneratorSettings } from '@/features/image-generator/hooks/use-image-generator-settings';
11
+ import {
12
+ DEFAULT_IMAGE_MODEL_ID,
13
+ getImageModelById,
14
+ getImageModelsByProvider,
15
+ getProviderDisplayName,
16
+ IMAGE_MODEL_PROVIDERS,
17
+ type ImageModel,
18
+ type ImageModelId,
19
+ type ImageProvider,
20
+ } from '@/features/image-generator/models/models';
21
+ import { useThemeConfig } from '@/lib/use-theme-config';
22
+
23
+ const getProviderIcon = (provider: ImageProvider): React.ComponentType<any> => {
24
+ switch (provider) {
25
+ case 'openai':
26
+ return OpenaiIcon;
27
+ case 'gemini':
28
+ return GeminiIcon;
29
+ default:
30
+ return OpenaiIcon;
31
+ }
32
+ };
33
+
34
+ type ModelSelectorProps = {
35
+ testID?: string;
36
+ settings: ImageGeneratorSettings;
37
+ onModelChange: (modelId: ImageModelId) => Promise<void> | void;
38
+ };
39
+
40
+ /**
41
+ * Image model selector component that mirrors the chatbot model selector UX.
42
+ */
43
+ export const ImageModelSelector: React.FC<ModelSelectorProps> = ({
44
+ testID = 'image-model-selector',
45
+ settings,
46
+ onModelChange,
47
+ }) => {
48
+ const theme = useThemeConfig();
49
+ const modal = useModal();
50
+
51
+ const currentModel =
52
+ getImageModelById(settings.modelId) ??
53
+ getImageModelById(DEFAULT_IMAGE_MODEL_ID);
54
+
55
+ const getCurrentModelIcon = () => {
56
+ if (!currentModel) return null;
57
+ const IconComponent = getProviderIcon(currentModel.provider);
58
+ return <IconComponent />;
59
+ };
60
+
61
+ const handleModelSelect = async (modelId: ImageModelId) => {
62
+ try {
63
+ await onModelChange(modelId);
64
+ } finally {
65
+ modal.dismiss();
66
+ }
67
+ };
68
+
69
+ return (
70
+ <>
71
+ <Pressable
72
+ onPress={modal.present}
73
+ className="flex-row items-center gap-2.5 rounded-full border border-neutral-300/60 bg-neutral-50 px-4 py-3.5 dark:border-neutral-800 dark:bg-neutral-800"
74
+ style={{ alignSelf: 'flex-start' }}
75
+ accessibilityLabel={`Current model: ${currentModel?.name ?? 'Select image model'}`}
76
+ accessibilityHint="Tap to change image generation model"
77
+ accessibilityRole="button"
78
+ testID={testID}
79
+ >
80
+ <View className="size-4">{getCurrentModelIcon()}</View>
81
+ <View className="max-w-[140px]">
82
+ <Text
83
+ className="text-xs font-medium text-neutral-800 dark:text-neutral-200"
84
+ numberOfLines={1}
85
+ >
86
+ {currentModel?.name ?? 'Select image model'}
87
+ </Text>
88
+ {/* <Text className="text-[10px] text-neutral-500 dark:text-neutral-400">
89
+ {currentModel?.provider
90
+ ? getProviderDisplayName(currentModel.provider)
91
+ : getProviderDisplayName("gemini")}
92
+ </Text> */}
93
+ </View>
94
+ <Feather
95
+ name="chevron-down"
96
+ size={14}
97
+ color={theme.dark ? colors.neutral[400] : colors.neutral[500]}
98
+ />
99
+ </Pressable>
100
+
101
+ <Modal ref={modal.ref} index={0} snapPoints={['70%']}>
102
+ <ScrollView
103
+ className="flex-1"
104
+ showsVerticalScrollIndicator={false}
105
+ contentContainerStyle={{ paddingHorizontal: 24 }}
106
+ >
107
+ <View className="mb-6 mt-2 items-center">
108
+ <Text className="text-xl font-semibold text-neutral-900 dark:text-neutral-50">
109
+ Choose an image model
110
+ </Text>
111
+ <Text className="mt-1 text-center text-xs text-neutral-500 dark:text-neutral-400">
112
+ Different models balance quality, speed, and style. Pick the one
113
+ that fits your workflow.
114
+ </Text>
115
+ </View>
116
+
117
+ {IMAGE_MODEL_PROVIDERS.map((provider) => {
118
+ const providerModels = getImageModelsByProvider(provider);
119
+ if (!providerModels.length) {
120
+ return null;
121
+ }
122
+
123
+ const ProviderIcon = getProviderIcon(provider);
124
+
125
+ return (
126
+ <View key={provider} className="mb-6">
127
+ <View className="mb-4 flex-row items-center gap-3">
128
+ <View className="size-6">
129
+ <ProviderIcon />
130
+ </View>
131
+ <Text className="text-lg font-semibold text-neutral-900 dark:text-neutral-100">
132
+ {getProviderDisplayName(provider)}
133
+ </Text>
134
+ </View>
135
+
136
+ {providerModels.map((model) => (
137
+ <ModelOption
138
+ key={model.id}
139
+ model={model}
140
+ isSelected={settings.modelId === model.id}
141
+ onSelect={() => handleModelSelect(model.id)}
142
+ testID={`${testID}-${model.id}`}
143
+ />
144
+ ))}
145
+ </View>
146
+ );
147
+ })}
148
+ </ScrollView>
149
+ </Modal>
150
+ </>
151
+ );
152
+ };
153
+
154
+ type ModelOptionProps = {
155
+ model: ImageModel;
156
+ isSelected: boolean;
157
+ onSelect: () => void;
158
+ testID?: string;
159
+ };
160
+
161
+ const ModelOption: React.FC<ModelOptionProps> = ({
162
+ model,
163
+ isSelected,
164
+ onSelect,
165
+ testID,
166
+ }) => {
167
+ const theme = useThemeConfig();
168
+
169
+ return (
170
+ <Pressable
171
+ onPress={onSelect}
172
+ className="mb-3 rounded-2xl border p-4"
173
+ style={{
174
+ borderColor: isSelected
175
+ ? theme.dark
176
+ ? 'rgba(96, 165, 250, 0.7)'
177
+ : 'rgba(37, 99, 235, 0.7)'
178
+ : theme.dark
179
+ ? 'rgba(71, 85, 105, 0.6)'
180
+ : 'rgba(203, 213, 225, 0.7)',
181
+ backgroundColor: isSelected
182
+ ? theme.dark
183
+ ? 'rgba(37, 99, 235, 0.14)'
184
+ : 'rgba(219, 234, 254, 0.6)'
185
+ : theme.dark
186
+ ? 'rgba(51, 65, 85, 0.35)'
187
+ : 'rgba(248, 250, 252, 0.5)',
188
+ }}
189
+ accessibilityLabel={`Select ${model.name}`}
190
+ accessibilityHint="Applies this model for future image generations"
191
+ accessibilityState={{ selected: isSelected }}
192
+ testID={testID}
193
+ >
194
+ <View className="flex-row items-center justify-between">
195
+ <Text className="text-base font-semibold text-neutral-900 dark:text-neutral-100">
196
+ {model.name}
197
+ </Text>
198
+ <View className="flex-row items-center gap-2">
199
+ {model.isPremium ? (
200
+ <Text className="rounded-full bg-amber-100 px-2 py-0.5 text-[10px] font-semibold text-amber-700 dark:bg-amber-400/20 dark:text-amber-200">
201
+ Premium
202
+ </Text>
203
+ ) : null}
204
+ {model.defaultSize || model.defaultAspectRatio ? (
205
+ <Text className="text-[10px] font-medium uppercase tracking-wide text-neutral-500 dark:text-neutral-400">
206
+ {model.defaultSize ?? model.defaultAspectRatio}
207
+ </Text>
208
+ ) : null}
209
+ </View>
210
+ </View>
211
+ <Text className="mt-2 text-xs text-neutral-600 dark:text-neutral-300">
212
+ {model.description}
213
+ </Text>
214
+ </Pressable>
215
+ );
216
+ };
@@ -0,0 +1,26 @@
1
+ import { MaterialIcons } from '@expo/vector-icons';
2
+ import type React from 'react';
3
+ import { View } from 'react-native';
4
+
5
+ import { Text } from '@/components/ui';
6
+ /**
7
+ * Placeholder component to show before first generation
8
+ */
9
+ export const ImagePlaceholder: React.FC = () => (
10
+ <View
11
+ className="aspect-square w-full items-center justify-center rounded-lg border-2 border-dashed border-neutral-300 bg-neutral-50 dark:border-neutral-600 dark:bg-neutral-800"
12
+ testID="image-placeholder"
13
+ >
14
+ <View className="items-center justify-center">
15
+ <MaterialIcons
16
+ name="brush"
17
+ size={32}
18
+ color="#9CA3AF"
19
+ style={{ marginBottom: 8 }}
20
+ />
21
+ <Text className="text-center text-neutral-500 dark:text-neutral-400">
22
+ Generated image will appear here
23
+ </Text>
24
+ </View>
25
+ </View>
26
+ );
@@ -0,0 +1,71 @@
1
+ import { useCallback, useEffect, useState } from 'react';
2
+
3
+ import {
4
+ ImageGalleryService,
5
+ type SavedImageMetadata,
6
+ } from '@/features/image-generator/services/image-gallery-service';
7
+
8
+ /**
9
+ * Hook for managing the local image gallery
10
+ */
11
+ export const useImageGallery = () => {
12
+ const [images, setImages] = useState<SavedImageMetadata[]>([]);
13
+ const [isLoading, setIsLoading] = useState(true);
14
+ const [error, setError] = useState<string | null>(null);
15
+
16
+ // Load images from storage
17
+ const loadImages = useCallback(async () => {
18
+ try {
19
+ setIsLoading(true);
20
+ setError(null);
21
+ const galleryImages = await ImageGalleryService.getAllImages();
22
+ setImages(galleryImages);
23
+ } catch (_err) {
24
+ console.error('Failed to load gallery images:', _err);
25
+ setError('Failed to load images');
26
+ } finally {
27
+ setIsLoading(false);
28
+ }
29
+ }, []);
30
+
31
+ // Remove an image from the gallery
32
+ const removeImage = useCallback(async (imageId: string) => {
33
+ try {
34
+ await ImageGalleryService.removeImage(imageId);
35
+ setImages((prev) => prev.filter((img) => img.id !== imageId));
36
+ } catch (_err) {
37
+ console.error('Failed to remove image:', _err);
38
+ setError('Failed to remove image');
39
+ }
40
+ }, []);
41
+
42
+ // Clear all images from the gallery
43
+ const clearGallery = useCallback(async () => {
44
+ try {
45
+ await ImageGalleryService.clearGallery();
46
+ setImages([]);
47
+ } catch (_err) {
48
+ console.error('Failed to clear gallery:', _err);
49
+ setError('Failed to clear gallery');
50
+ }
51
+ }, []);
52
+
53
+ // Refresh the gallery
54
+ const refreshGallery = useCallback(() => {
55
+ loadImages();
56
+ }, [loadImages]);
57
+
58
+ // Load images on mount
59
+ useEffect(() => {
60
+ loadImages();
61
+ }, [loadImages]);
62
+
63
+ return {
64
+ images,
65
+ isLoading,
66
+ error,
67
+ removeImage,
68
+ clearGallery,
69
+ refreshGallery,
70
+ };
71
+ };
@@ -0,0 +1,152 @@
1
+ import { useCallback, useEffect, useState } from 'react';
2
+
3
+ import { defaultCacheService } from '@/core/cache';
4
+ import {
5
+ DEFAULT_IMAGE_MODEL_ID,
6
+ getImageModelById,
7
+ getImageModelsByProvider,
8
+ IMAGE_MODELS,
9
+ type ImageModelId,
10
+ type ImageProvider,
11
+ } from '@/features/image-generator/models/models';
12
+
13
+ export type ImageGeneratorSettings = {
14
+ provider: ImageProvider;
15
+ modelId: ImageModelId;
16
+ };
17
+
18
+ const CACHE_KEY = 'image_generator_settings_v3';
19
+
20
+ const DEFAULT_SETTINGS: ImageGeneratorSettings = {
21
+ provider: 'gemini',
22
+ modelId: DEFAULT_IMAGE_MODEL_ID,
23
+ };
24
+
25
+ type RawSettings = Partial<ImageGeneratorSettings> & {
26
+ provider?: unknown;
27
+ modelId?: unknown;
28
+ };
29
+
30
+ const providerHasModel = (
31
+ provider: ImageProvider,
32
+ modelId: ImageModelId,
33
+ ): boolean => {
34
+ return getImageModelsByProvider(provider).some(
35
+ (model) => model.id === modelId,
36
+ );
37
+ };
38
+
39
+ const normalizeSettings = (
40
+ rawSettings: RawSettings | null | undefined,
41
+ ): ImageGeneratorSettings => {
42
+ if (!rawSettings) {
43
+ return DEFAULT_SETTINGS;
44
+ }
45
+
46
+ const provider =
47
+ rawSettings.provider === 'openai' || rawSettings.provider === 'gemini'
48
+ ? rawSettings.provider
49
+ : DEFAULT_SETTINGS.provider;
50
+
51
+ const modelId =
52
+ typeof rawSettings.modelId === 'string' &&
53
+ IMAGE_MODELS.some((model) => model.id === rawSettings.modelId) &&
54
+ providerHasModel(provider, rawSettings.modelId)
55
+ ? (rawSettings.modelId as ImageModelId)
56
+ : DEFAULT_IMAGE_MODEL_ID;
57
+
58
+ return {
59
+ provider,
60
+ modelId,
61
+ };
62
+ };
63
+
64
+ /**
65
+ * Hook for managing image generator settings with persistent storage
66
+ */
67
+ export const useImageGeneratorSettings = () => {
68
+ const [settings, setSettings] =
69
+ useState<ImageGeneratorSettings>(DEFAULT_SETTINGS);
70
+ const [isLoaded, setIsLoaded] = useState(false);
71
+
72
+ // Load settings from cache on mount
73
+ useEffect(() => {
74
+ const loadSettings = async () => {
75
+ try {
76
+ const cached =
77
+ await defaultCacheService.getItem<RawSettings>(CACHE_KEY);
78
+ setSettings(normalizeSettings(cached));
79
+ } catch (error) {
80
+ console.error('Failed to load image generator settings:', error);
81
+ } finally {
82
+ setIsLoaded(true);
83
+ }
84
+ };
85
+
86
+ loadSettings();
87
+ }, []);
88
+
89
+ // Save settings to cache
90
+ const saveSettings = useCallback(
91
+ async (newSettings: ImageGeneratorSettings) => {
92
+ try {
93
+ const normalized = normalizeSettings(newSettings);
94
+ await defaultCacheService.setItem(CACHE_KEY, normalized);
95
+ setSettings(normalized);
96
+ } catch (error) {
97
+ console.error('Failed to save image generator settings:', error);
98
+ }
99
+ },
100
+ [],
101
+ );
102
+
103
+ const updateProvider = useCallback(
104
+ async (provider: ImageProvider) => {
105
+ const providerModels = getImageModelsByProvider(provider);
106
+ const defaultModelForProvider =
107
+ providerModels[0]?.id ?? DEFAULT_IMAGE_MODEL_ID;
108
+ const newSettings: ImageGeneratorSettings = {
109
+ provider,
110
+ modelId: defaultModelForProvider,
111
+ };
112
+ await saveSettings(newSettings);
113
+ },
114
+ [saveSettings],
115
+ );
116
+
117
+ const updateModel = useCallback(
118
+ async (model: string) => {
119
+ if (typeof model !== 'string') {
120
+ return;
121
+ }
122
+
123
+ const selectedModel = getImageModelById(model as ImageModelId);
124
+ if (!selectedModel) {
125
+ console.warn(
126
+ `[useImageGeneratorSettings] Unsupported model "${model}" received. Reverting to default.`,
127
+ );
128
+ await saveSettings(DEFAULT_SETTINGS);
129
+ return;
130
+ }
131
+
132
+ const provider =
133
+ settings.provider === selectedModel.provider
134
+ ? settings.provider
135
+ : selectedModel.provider;
136
+
137
+ const newSettings: ImageGeneratorSettings = {
138
+ provider,
139
+ modelId: selectedModel.id,
140
+ };
141
+ await saveSettings(newSettings);
142
+ },
143
+ [settings.provider, saveSettings],
144
+ );
145
+
146
+ return {
147
+ settings,
148
+ isLoaded,
149
+ updateProvider,
150
+ updateModel,
151
+ };
152
+ };
@@ -0,0 +1,103 @@
1
+ import { useState } from 'react';
2
+ import { Alert } from 'react-native';
3
+
4
+ import { useGenerateImage } from '@/api-client/supabase/image-generator';
5
+
6
+ import { getImageModelById } from '../models/models';
7
+ import type { ImageGeneratorSettings } from './use-image-generator-settings';
8
+
9
+ type GeneratedImageData = {
10
+ imageUri: string;
11
+ prompt: string;
12
+ provider: string;
13
+ model: string;
14
+ mimeType: string;
15
+ imageId?: string;
16
+ storageId?: string;
17
+ };
18
+
19
+ /**
20
+ * Custom hook for managing image generation state and operations
21
+ */
22
+ type UseImageGeneratorOptions = {
23
+ settings: ImageGeneratorSettings;
24
+ isSettingsLoaded: boolean;
25
+ };
26
+
27
+ export const useImageGenerator = ({
28
+ settings,
29
+ isSettingsLoaded,
30
+ }: UseImageGeneratorOptions) => {
31
+ const [prompt, setPrompt] = useState('');
32
+ const [isLoading, setIsLoading] = useState(false);
33
+ const [generatedImageData, setGeneratedImageData] =
34
+ useState<GeneratedImageData | null>(null);
35
+
36
+ const generateImageMutation = useGenerateImage();
37
+
38
+ /**
39
+ * Generate an image based on the current prompt
40
+ */
41
+ const handleGenerateImage = async () => {
42
+ if (!prompt.trim()) {
43
+ Alert.alert('Warning', 'Please enter a prompt to generate an image');
44
+ return;
45
+ }
46
+
47
+ if (!isSettingsLoaded) {
48
+ Alert.alert('Please wait', 'Settings are still loading...');
49
+ return;
50
+ }
51
+
52
+ setIsLoading(true);
53
+ try {
54
+ const selectedModel = getImageModelById(settings.modelId);
55
+ const provider = selectedModel?.provider ?? settings.provider;
56
+ const result = await generateImageMutation.mutateAsync({
57
+ prompt: prompt.trim(),
58
+ provider,
59
+ model: selectedModel?.id ?? settings.modelId,
60
+ });
61
+
62
+ const imageUri = result.imageDataUri || result.imageUrl || '';
63
+ const mimeType = result.mimeType || (result.imageUrl ? 'image/png' : '');
64
+
65
+ console.log('Frontend received result:', {
66
+ provider: result.provider,
67
+ model: result.model,
68
+ mimeType,
69
+ dataUriLength: imageUri.length,
70
+ dataUriPreview: imageUri.substring(0, 100),
71
+ });
72
+
73
+ setGeneratedImageData({
74
+ imageUri,
75
+ prompt: result.prompt ?? prompt.trim(),
76
+ provider: result.provider ?? provider,
77
+ model: result.model ?? settings.modelId,
78
+ mimeType: mimeType || 'image/png',
79
+ imageId: result.imageId,
80
+ storageId: result.storageId,
81
+ });
82
+ // Alert.alert('Success', 'Image generated successfully!');
83
+ } catch (error) {
84
+ console.error('Image generation error:', error);
85
+ Alert.alert(
86
+ 'Error',
87
+ error instanceof Error ? error.message : 'Failed to generate image',
88
+ );
89
+ } finally {
90
+ setIsLoading(false);
91
+ }
92
+ };
93
+
94
+ return {
95
+ prompt,
96
+ setPrompt,
97
+ isLoading,
98
+ generatedImageData,
99
+ generatedImageUri: generatedImageData?.imageUri || null, // backward compatibility
100
+ handleGenerateImage,
101
+ settings,
102
+ };
103
+ };
@@ -0,0 +1,66 @@
1
+ export type ImageProvider = 'openai' | 'gemini';
2
+
3
+ export type ImageModelId =
4
+ | 'dall-e-3'
5
+ | 'gpt-image-1'
6
+ | 'gemini-2.5-flash-image';
7
+
8
+ export type ImageModel = {
9
+ id: ImageModelId;
10
+ name: string;
11
+ description: string;
12
+ provider: ImageProvider;
13
+ defaultSize?: string;
14
+ defaultAspectRatio?: string;
15
+ isPremium?: boolean;
16
+ };
17
+
18
+ export const IMAGE_MODELS: ImageModel[] = [
19
+ {
20
+ id: 'dall-e-3',
21
+ name: 'DALL·E 3',
22
+ description:
23
+ 'OpenAI’s flagship model for creative storytelling and illustration.',
24
+ provider: 'openai',
25
+ defaultSize: '1024x1024',
26
+ },
27
+ {
28
+ id: 'gpt-image-1',
29
+ name: 'GPT Image 1',
30
+ description:
31
+ 'GPT-4o based image model with strong photorealism and typography.',
32
+ provider: 'openai',
33
+ defaultSize: '1024x1024',
34
+ },
35
+ {
36
+ id: 'gemini-2.5-flash-image',
37
+ name: 'Gemini 2.5 Flash Image',
38
+ description:
39
+ 'Fast and cost-effective image generation ideal for rapid iteration.',
40
+ provider: 'gemini',
41
+ defaultSize: '1024x1024',
42
+ },
43
+ ];
44
+
45
+ export const DEFAULT_IMAGE_MODEL_ID: ImageModelId = 'gemini-2.5-flash-image';
46
+
47
+ export const getImageModelsByProvider = (provider: ImageProvider) =>
48
+ IMAGE_MODELS.filter((model) => model.provider === provider);
49
+
50
+ export const getImageModelById = (id: ImageModelId) =>
51
+ IMAGE_MODELS.find((model) => model.id === id) ?? null;
52
+
53
+ export const getProviderDisplayName = (provider: ImageProvider) => {
54
+ switch (provider) {
55
+ case 'openai':
56
+ return 'OpenAI';
57
+ case 'gemini':
58
+ return 'Google Gemini';
59
+ default:
60
+ return provider;
61
+ }
62
+ };
63
+
64
+ export const IMAGE_MODEL_PROVIDERS: ImageProvider[] = Array.from(
65
+ new Set(IMAGE_MODELS.map((model) => model.provider)),
66
+ ) as ImageProvider[];