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,122 @@
1
+ import * as ImagePicker from 'expo-image-picker';
2
+ import { useCallback } from 'react';
3
+ import { Alert } from 'react-native';
4
+
5
+ // Basic image type without upload state (added later in chat-input-bar)
6
+ export type BasicSelectedImage = {
7
+ uri: string;
8
+ type: string;
9
+ fileName?: string;
10
+ };
11
+
12
+ export const useImagePicker = () => {
13
+ const handleImagePicker = useCallback(async (): Promise<
14
+ BasicSelectedImage[]
15
+ > => {
16
+ try {
17
+ // Request permissions
18
+ const { status } =
19
+ await ImagePicker.requestMediaLibraryPermissionsAsync();
20
+ if (status !== 'granted') {
21
+ Alert.alert(
22
+ 'Permission Required',
23
+ 'Sorry, we need camera roll permissions to upload images.',
24
+ );
25
+ return [];
26
+ }
27
+
28
+ // Launch image picker with multiple selection enabled
29
+ const result = await ImagePicker.launchImageLibraryAsync({
30
+ mediaTypes: ImagePicker.MediaTypeOptions.Images,
31
+ allowsMultipleSelection: true,
32
+ allowsEditing: false,
33
+ aspect: [4, 3],
34
+ quality: 0.8,
35
+ });
36
+
37
+ if (!result.canceled && result.assets && result.assets.length > 0) {
38
+ return result.assets.map((asset) => ({
39
+ uri: asset.uri,
40
+ type: asset.mimeType || 'image/jpeg',
41
+ fileName: asset.fileName || undefined,
42
+ }));
43
+ }
44
+
45
+ return [];
46
+ } catch (error) {
47
+ console.error('Error picking images:', error);
48
+ Alert.alert('Error', 'Failed to pick images. Please try again.');
49
+ return [];
50
+ }
51
+ }, []);
52
+
53
+ const handleCamera =
54
+ useCallback(async (): Promise<BasicSelectedImage | null> => {
55
+ try {
56
+ // Request permissions
57
+ const { status } = await ImagePicker.requestCameraPermissionsAsync();
58
+ if (status !== 'granted') {
59
+ Alert.alert(
60
+ 'Permission Required',
61
+ 'Sorry, we need camera permissions to take photos.',
62
+ );
63
+ return null;
64
+ }
65
+
66
+ // Launch camera
67
+ const result = await ImagePicker.launchCameraAsync({
68
+ allowsEditing: true,
69
+ aspect: [4, 3],
70
+ quality: 0.8,
71
+ });
72
+
73
+ if (!result.canceled && result.assets[0]) {
74
+ const asset = result.assets[0];
75
+ return {
76
+ uri: asset.uri,
77
+ type: asset.mimeType || 'image/jpeg',
78
+ fileName: asset.fileName || undefined,
79
+ };
80
+ }
81
+
82
+ return null;
83
+ } catch (error) {
84
+ console.error('Error taking photo:', error);
85
+ Alert.alert('Error', 'Failed to take photo. Please try again.');
86
+ return null;
87
+ }
88
+ }, []);
89
+
90
+ const showImageSourceOptions = useCallback(
91
+ (onImagesSelected: (images: BasicSelectedImage[]) => void) => {
92
+ Alert.alert('Select Image', 'Choose an image source:', [
93
+ {
94
+ text: 'Camera',
95
+ onPress: async () => {
96
+ const image = await handleCamera();
97
+ if (image) {
98
+ onImagesSelected([image]);
99
+ }
100
+ },
101
+ },
102
+ {
103
+ text: 'Photo Library',
104
+ onPress: async () => {
105
+ const images = await handleImagePicker();
106
+ if (images.length > 0) {
107
+ onImagesSelected(images);
108
+ }
109
+ },
110
+ },
111
+ { text: 'Cancel', style: 'cancel' },
112
+ ]);
113
+ },
114
+ [handleCamera, handleImagePicker],
115
+ );
116
+
117
+ return {
118
+ handleImagePicker,
119
+ handleCamera,
120
+ showImageSourceOptions,
121
+ };
122
+ };
@@ -0,0 +1,161 @@
1
+ import { useCallback, useEffect, useRef } from 'react';
2
+ import { Keyboard } from 'react-native';
3
+ import {
4
+ useAnimatedStyle,
5
+ useSharedValue,
6
+ withTiming,
7
+ } from 'react-native-reanimated';
8
+ import { runOnUI } from 'react-native-worklets';
9
+
10
+ /**
11
+ * Hook to coordinate keyboard animations and padding.
12
+ * Eliminates race conditions in keyboard show/hide animations.
13
+ *
14
+ * Features:
15
+ * - Smooth 300ms animations using Reanimated
16
+ * - Cancels pending animations to prevent stacking
17
+ * - Synchronized timing with message submission
18
+ * - Automatic cleanup on unmount
19
+ * - 60fps animations using runOnUI
20
+ *
21
+ * Requirements addressed:
22
+ * - 3.1: Animate padding smoothly over 300ms
23
+ * - 3.2: Reset padding to zero with synchronized timing
24
+ * - 3.3: Dismiss keyboard and reset padding atomically on message send
25
+ * - 3.4: Cancel pending animations to prevent stacking
26
+ * - 3.5: Prioritize submission action over animation completion
27
+ *
28
+ * @returns Object containing animated padding value, reset function, and animated style
29
+ *
30
+ * @example
31
+ * ```tsx
32
+ * const keyboardCoordinator = useKeyboardCoordinator();
33
+ *
34
+ * // Use animated style for keyboard padding
35
+ * <Animated.View style={keyboardCoordinator.keyboardPadding} />
36
+ *
37
+ * // Reset padding when sending message
38
+ * const handleSubmit = async () => {
39
+ * await sendMessage();
40
+ * keyboardCoordinator.resetPadding();
41
+ * };
42
+ * ```
43
+ */
44
+ export function useKeyboardCoordinator() {
45
+ // Animated padding value for smooth transitions
46
+ const height = useSharedValue(0);
47
+
48
+ // Pending timeout reference for cancellation
49
+ const pendingTimeout = useRef<NodeJS.Timeout | null>(null);
50
+
51
+ /**
52
+ * Cancel any pending animations.
53
+ * Prevents animation stacking when rapid keyboard events occur.
54
+ */
55
+ const cancelPending = useCallback(() => {
56
+ if (pendingTimeout.current) {
57
+ clearTimeout(pendingTimeout.current);
58
+ pendingTimeout.current = null;
59
+ }
60
+ }, []);
61
+
62
+ /**
63
+ * Reset padding to zero with smooth animation.
64
+ * Uses runOnUI for 60fps performance.
65
+ * Cancels any pending animations before starting new one.
66
+ */
67
+ const resetPadding = useCallback(() => {
68
+ cancelPending();
69
+
70
+ runOnUI(() => {
71
+ 'worklet';
72
+ height.value = withTiming(0, { duration: 300 });
73
+ })();
74
+ }, [height, cancelPending]);
75
+
76
+ /**
77
+ * Handle keyboard show event.
78
+ * Animates padding to match keyboard height.
79
+ *
80
+ * @param keyboardHeight - Height of the keyboard in pixels
81
+ */
82
+ const onKeyboardShow = useCallback(
83
+ (keyboardHeight: number) => {
84
+ cancelPending();
85
+
86
+ runOnUI(() => {
87
+ 'worklet';
88
+ height.value = withTiming(keyboardHeight, { duration: 300 });
89
+ })();
90
+ },
91
+ [height, cancelPending],
92
+ );
93
+
94
+ /**
95
+ * Handle keyboard hide event.
96
+ * Resets padding to zero with animation.
97
+ */
98
+ const onKeyboardHide = useCallback(() => {
99
+ resetPadding();
100
+ }, [resetPadding]);
101
+
102
+ /**
103
+ * Animated style for keyboard padding.
104
+ * Apply this to a View component to create dynamic spacing.
105
+ */
106
+ const keyboardPadding = useAnimatedStyle(() => {
107
+ return {
108
+ height: height.value,
109
+ };
110
+ }, [height]);
111
+
112
+ /**
113
+ * Set up keyboard event listeners.
114
+ * Uses keyboardWillShow for instant response (iOS) and keyboardDidShow as fallback (Android).
115
+ * Automatically cleans up on unmount.
116
+ */
117
+ useEffect(() => {
118
+ // Use 'Will' events for instant response on iOS, 'Did' events as fallback for Android
119
+ const showListener = Keyboard.addListener('keyboardWillShow', (e) => {
120
+ onKeyboardShow(e.endCoordinates.height);
121
+ });
122
+
123
+ const hideListener = Keyboard.addListener(
124
+ 'keyboardWillHide',
125
+ onKeyboardHide,
126
+ );
127
+
128
+ // Fallback for Android (doesn't have 'Will' events)
129
+ const showListenerAndroid = Keyboard.addListener('keyboardDidShow', (e) => {
130
+ onKeyboardShow(e.endCoordinates.height);
131
+ });
132
+
133
+ const hideListenerAndroid = Keyboard.addListener(
134
+ 'keyboardDidHide',
135
+ onKeyboardHide,
136
+ );
137
+
138
+ return () => {
139
+ showListener.remove();
140
+ hideListener.remove();
141
+ showListenerAndroid.remove();
142
+ hideListenerAndroid.remove();
143
+ cancelPending();
144
+ };
145
+ }, [onKeyboardShow, onKeyboardHide, cancelPending]);
146
+
147
+ return {
148
+ // Animated values
149
+ height,
150
+ keyboardPadding,
151
+
152
+ // Methods
153
+ resetPadding,
154
+ cancelPending,
155
+ onKeyboardShow,
156
+ onKeyboardHide,
157
+
158
+ // Refs
159
+ pendingTimeout,
160
+ };
161
+ }
@@ -0,0 +1,213 @@
1
+ import type { FlashListRef } from '@shopify/flash-list';
2
+ import { useCallback, useEffect, useMemo, useRef } from 'react';
3
+ import type { NativeScrollEvent, NativeSyntheticEvent } from 'react-native';
4
+
5
+ const NEAR_BOTTOM_THRESHOLD = 120;
6
+ const SCROLL_DEBOUNCE_MS = 32;
7
+
8
+ /**
9
+ * Smart scroll manager for chat message lists.
10
+ * Handles auto-scrolling based on user intent and scroll position.
11
+ *
12
+ * Features:
13
+ * - Auto-scroll when user is near bottom (within 120px threshold)
14
+ * - Cancel auto-scroll when user manually scrolls up
15
+ * - Always scroll on user message send
16
+ * - Track drag state to prevent scroll interruption
17
+ * - Debounced scroll scheduling (32ms) for performance
18
+ *
19
+ * Requirements addressed:
20
+ * - 4.1: Auto-scroll when near bottom (120px threshold)
21
+ * - 4.2: Don't auto-scroll when user scrolls up to read history
22
+ * - 4.3: Always scroll to bottom when user sends a message
23
+ * - 4.4: Maintain scroll position during streaming if reading history
24
+ * - 4.5: Cancel pending auto-scroll operations when user drags
25
+ *
26
+ * @param flashListRef - Reference to the FlashList component
27
+ * @returns Object containing scroll state refs, methods, and event handlers
28
+ *
29
+ * @example
30
+ * ```tsx
31
+ * const flashListRef = useRef<FlashListRef<Message> | null>(null);
32
+ * const scrollManager = useSmartScrollManager(flashListRef);
33
+ *
34
+ * // In useEffect for message updates
35
+ * useEffect(() => {
36
+ * scrollManager.handleMessagesUpdate(messages, previousMessageIds, { logTelemetry: true });
37
+ * previousMessageIds.current = messages.map(m => m.id);
38
+ * }, [messages, scrollManager]);
39
+ *
40
+ * // In FlashList props
41
+ * <FlashList
42
+ * onScroll={scrollManager.onScroll}
43
+ * onScrollBeginDrag={scrollManager.onScrollBeginDrag}
44
+ * onScrollEndDrag={scrollManager.onScrollEndDrag}
45
+ * onMomentumScrollBegin={scrollManager.onMomentumScrollBegin}
46
+ * onMomentumScrollEnd={scrollManager.onMomentumScrollEnd}
47
+ * />
48
+ * ```
49
+ */
50
+ export function useSmartScrollManager<T>(
51
+ flashListRef: React.RefObject<FlashListRef<T> | null>,
52
+ ) {
53
+ // Track if user is near bottom (within 120px)
54
+ const isNearBottom = useRef(true);
55
+
56
+ // Track if user is actively dragging
57
+ const isDragging = useRef(false);
58
+
59
+ // Pending scroll timeout for debouncing
60
+ const pendingScroll = useRef<ReturnType<typeof setTimeout> | null>(null);
61
+
62
+ /**
63
+ * Cancel any pending scroll operation
64
+ */
65
+ const cancelScroll = useCallback(() => {
66
+ if (pendingScroll.current) {
67
+ clearTimeout(pendingScroll.current);
68
+ pendingScroll.current = null;
69
+ }
70
+ }, []);
71
+
72
+ /**
73
+ * Scroll to bottom immediately
74
+ * Includes null check to prevent crashes when component unmounts
75
+ */
76
+ const scrollToBottom = useCallback(() => {
77
+ if (flashListRef.current) {
78
+ try {
79
+ flashListRef.current.scrollToEnd({ animated: true });
80
+ isNearBottom.current = true;
81
+ } catch (error) {
82
+ // Silently catch scroll errors (e.g., if list is unmounting)
83
+ console.warn('[ScrollManager] Scroll failed:', error);
84
+ }
85
+ }
86
+ }, [flashListRef]);
87
+
88
+ /**
89
+ * Schedule scroll to bottom with 32ms debounce
90
+ * Cancels any pending scroll before scheduling new one
91
+ * This prevents rapid scroll calls during streaming
92
+ */
93
+ const scheduleScroll = useCallback(() => {
94
+ // If already pending, don't schedule another one
95
+ if (pendingScroll.current) {
96
+ return;
97
+ }
98
+
99
+ pendingScroll.current = setTimeout(() => {
100
+ pendingScroll.current = null;
101
+ scrollToBottom();
102
+ }, SCROLL_DEBOUNCE_MS);
103
+ }, [scrollToBottom]);
104
+
105
+ /**
106
+ * Handle scroll events to track near-bottom state
107
+ * Updates isNearBottom based on 120px threshold
108
+ */
109
+ const onScroll = useCallback(
110
+ (event: NativeSyntheticEvent<NativeScrollEvent>) => {
111
+ const { contentOffset, contentSize, layoutMeasurement } =
112
+ event.nativeEvent;
113
+ const visibleBottom = contentOffset.y + layoutMeasurement.height;
114
+ const distanceFromBottom = Math.max(
115
+ contentSize.height - visibleBottom,
116
+ 0,
117
+ );
118
+
119
+ isNearBottom.current = distanceFromBottom < NEAR_BOTTOM_THRESHOLD;
120
+ },
121
+ [],
122
+ );
123
+
124
+ /**
125
+ * Handle scroll begin drag - cancel pending scrolls
126
+ */
127
+ const onScrollBeginDrag = useCallback(() => {
128
+ isDragging.current = true;
129
+ cancelScroll();
130
+ }, [cancelScroll]);
131
+
132
+ /**
133
+ * Handle scroll end drag - mark dragging as complete
134
+ */
135
+ const onScrollEndDrag = useCallback(() => {
136
+ isDragging.current = false;
137
+ }, []);
138
+
139
+ /**
140
+ * Handle momentum scroll begin - treat as dragging
141
+ */
142
+ const onMomentumScrollBegin = useCallback(() => {
143
+ isDragging.current = true;
144
+ }, []);
145
+
146
+ /**
147
+ * Handle momentum scroll end - mark dragging as complete
148
+ */
149
+ const onMomentumScrollEnd = useCallback(() => {
150
+ isDragging.current = false;
151
+ }, []);
152
+
153
+ const markNearBottom = useCallback(() => {
154
+ isNearBottom.current = true;
155
+ }, []);
156
+
157
+ /**
158
+ * Determine if should auto-scroll based on current state
159
+ * Always scrolls on user message send
160
+ * Otherwise only scrolls if user is near bottom and not dragging
161
+ */
162
+ const shouldAutoScroll = useCallback((userSentMessage: boolean): boolean => {
163
+ if (userSentMessage) {
164
+ return true;
165
+ }
166
+ return !isDragging.current && isNearBottom.current;
167
+ }, []);
168
+
169
+ /**
170
+ * Cleanup pending scrolls on unmount
171
+ */
172
+ useEffect(() => {
173
+ return () => {
174
+ cancelScroll();
175
+ };
176
+ }, [cancelScroll]);
177
+
178
+ // Memoize the return object to prevent unnecessary re-renders
179
+ return useMemo(
180
+ () => ({
181
+ // State refs
182
+ isNearBottom,
183
+ isDragging,
184
+ pendingScroll,
185
+
186
+ // Methods
187
+ scheduleScroll,
188
+ markNearBottom,
189
+ cancelScroll,
190
+ scrollToBottom,
191
+ shouldAutoScroll,
192
+
193
+ // Event handlers
194
+ onScroll,
195
+ onScrollBeginDrag,
196
+ onScrollEndDrag,
197
+ onMomentumScrollBegin,
198
+ onMomentumScrollEnd,
199
+ }),
200
+ [
201
+ scheduleScroll,
202
+ markNearBottom,
203
+ cancelScroll,
204
+ scrollToBottom,
205
+ shouldAutoScroll,
206
+ onScroll,
207
+ onScrollBeginDrag,
208
+ onScrollEndDrag,
209
+ onMomentumScrollBegin,
210
+ onMomentumScrollEnd,
211
+ ],
212
+ );
213
+ }
@@ -0,0 +1,86 @@
1
+ // Import directly to avoid circular dependencies
2
+ import { DEFAULT_MODELS, MODELS } from './models';
3
+ import type { ModelConfig, ModelId, Provider } from './types';
4
+
5
+ // Re-export models and model functions
6
+ export { DEFAULT_MODELS, getModelDescription, MODELS } from './models';
7
+ // Re-export provider functions
8
+ export {
9
+ getAllProviders,
10
+ getProviderConfig,
11
+ getProviderDisplayName,
12
+ getProviderIcon,
13
+ PROVIDER_CONFIGS,
14
+ } from './providers';
15
+ // Re-export types
16
+ export type {
17
+ ModelCategory,
18
+ ModelConfig,
19
+ ModelId,
20
+ ModelOptions,
21
+ ModelSpeed,
22
+ Provider,
23
+ ProviderConfig,
24
+ } from './types';
25
+
26
+ // Additional helper functions
27
+ export const getModelById = (id: string): ModelConfig | undefined => {
28
+ return MODELS.find((model) => model.id === id);
29
+ };
30
+
31
+ export const getModelsByProvider = (provider: Provider): ModelConfig[] => {
32
+ return MODELS.filter((model) => model.provider === provider);
33
+ };
34
+
35
+ export const getPremiumModels = (): ModelConfig[] => {
36
+ return MODELS.filter((model) => model.category === 'premium');
37
+ };
38
+
39
+ export const getFreeModels = (): ModelConfig[] => {
40
+ return MODELS.filter((model) => model.category === 'free');
41
+ };
42
+
43
+ export const getDefaultModel = (provider: Provider): ModelConfig => {
44
+ const defaultId = DEFAULT_MODELS[provider];
45
+ const model = getModelById(defaultId);
46
+ if (!model) {
47
+ throw new Error(
48
+ `Default model ${defaultId} not found for provider ${provider}`,
49
+ );
50
+ }
51
+ return model;
52
+ };
53
+
54
+ export const isPremiumModel = (modelId: string): boolean => {
55
+ const model = getModelById(modelId);
56
+ return model?.category === 'premium';
57
+ };
58
+
59
+ export const getAllModelIds = (): string[] => {
60
+ return MODELS.map((model: any) => model.id);
61
+ };
62
+
63
+ export const getModelsByCategory = (
64
+ category: 'premium' | 'free' | 'specialized',
65
+ ) => {
66
+ return MODELS.filter((model: any) => model.category === category);
67
+ };
68
+
69
+ export const getModelProvider = (modelId: string): string | undefined => {
70
+ const model = getModelById(modelId);
71
+ return model?.provider;
72
+ };
73
+
74
+ // Backward compatibility exports (to match old API) - lazy evaluation
75
+ export const ALL_MODELS = (): string[] => {
76
+ return MODELS.map((model: any) => model.id);
77
+ };
78
+
79
+ export const PREMIUM_MODELS = (): string[] => {
80
+ return MODELS.filter((model: any) => model.category === 'premium').map(
81
+ (model: any) => model.id,
82
+ );
83
+ };
84
+
85
+ // Legacy type for backward compatibility
86
+ export type ModelType = ModelId;