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,429 @@
1
+ import React, { useCallback, useMemo, useState } from 'react';
2
+ import { Alert, Keyboard } from 'react-native';
3
+
4
+ import { chatbotApi } from '@/api-client';
5
+ import { getSupabase } from '@/api-client/supabase/client';
6
+ import { useToast } from '@/components/ui/utils';
7
+ import type { ReportReason } from '@/features/chatbot/constants/report-reasons';
8
+ import { useChatConfig } from '@/features/chatbot/hooks/use-chat-config';
9
+ import {
10
+ type AppAttachment,
11
+ MessageHandlerService,
12
+ } from '@/features/chatbot/services/message-handler-service';
13
+ import type { AppMessage } from '@/features/chatbot/types';
14
+
15
+ type UseChatHandlersOptions = {
16
+ conversationId: string | null;
17
+ initialMessages?: AppMessage[];
18
+ preferredProvider?: string;
19
+ preferredModel?: string;
20
+ searchMode?: string;
21
+ };
22
+
23
+ /**
24
+ * Hook to manage chat interactions, message handling, and reporting
25
+ */
26
+ export const useChatHandlers = (options: UseChatHandlersOptions) => {
27
+ const {
28
+ conversationId,
29
+ initialMessages = [],
30
+ preferredProvider = 'openai',
31
+ preferredModel = 'gpt-5-nano',
32
+ searchMode = 'provider',
33
+ } = options;
34
+
35
+ // console.log('[CHATBOT_FRONTEND] 🎣 useChatHandlers hook 1:', {
36
+ // conversationId,
37
+ // hasAuthToken: !!authToken,
38
+ // initialMessagesCount: initialMessages.length,
39
+ // preferredProvider,
40
+ // preferredModel,
41
+ // searchMode,
42
+ // file: 'src/features/chatbot/hooks/use-chat-handlers.ts',
43
+ // function: 'useChatHandlers',
44
+ // });
45
+
46
+ const [messageToReport, setMessageToReport] = useState<AppMessage | null>(
47
+ null,
48
+ );
49
+ const [isPreflightSending, setIsPreflightSending] = useState(false);
50
+ const [signedUrlMap, setSignedUrlMap] = useState<Record<string, string>>({});
51
+ const lastUserAttachments = React.useRef<AppAttachment[] | undefined>(
52
+ undefined,
53
+ );
54
+
55
+ // Persist attachments per live message ID to avoid disappearing images
56
+ const attachmentCacheRef = React.useRef<Record<string, AppAttachment[]>>({});
57
+
58
+ const storeUserMessageMutation = chatbotApi.useStoreMessage();
59
+ const clearConversationMutation = chatbotApi.useClearConversation();
60
+ const deleteUserMessageMutation = chatbotApi.useDeleteUserMessage();
61
+ const toast = useToast();
62
+ const handleInputChangeRef = React.useRef<(text: string) => void>(() => {});
63
+
64
+ // Define onStreamFailure BEFORE passing it to useChatConfig
65
+ const onStreamFailure = useCallback(
66
+ async (details: {
67
+ userMessageId: string | null;
68
+ text: string;
69
+ attachments?: {
70
+ type: 'image';
71
+ storageId: string;
72
+ fileName?: string;
73
+ mimeType?: string;
74
+ }[];
75
+ errorMessage?: string;
76
+ }) => {
77
+ const message =
78
+ details.errorMessage ?? 'Message failed to send. Please try again.';
79
+
80
+ // Show error toast to user
81
+ toast.error(message);
82
+
83
+ // Restore user's input text so they can retry
84
+ handleInputChangeRef.current(details.text ?? '');
85
+
86
+ // Delete the failed user message from backend
87
+ if (details.userMessageId && conversationId) {
88
+ try {
89
+ await deleteUserMessageMutation.mutateAsync({
90
+ messageId: details.userMessageId,
91
+ conversationId,
92
+ });
93
+ } catch (error) {
94
+ console.warn('[useChatHandlers] Failed to remove failed message', {
95
+ messageId: details.userMessageId,
96
+ error,
97
+ });
98
+ }
99
+ delete attachmentCacheRef.current[details.userMessageId];
100
+ }
101
+
102
+ // Clear attachment cache
103
+ lastUserAttachments.current = undefined;
104
+ },
105
+ [conversationId, deleteUserMessageMutation, toast],
106
+ );
107
+
108
+ const chatConfig = useChatConfig({
109
+ conversationId,
110
+ initialMessages,
111
+ preferredProvider,
112
+ preferredModel,
113
+ searchMode,
114
+ onStreamFailure,
115
+ });
116
+
117
+ handleInputChangeRef.current = chatConfig.handleInputChange;
118
+
119
+ const displayMessages = useMemo((): AppMessage[] => {
120
+ return MessageHandlerService.transformDisplayMessages(
121
+ chatConfig.messages,
122
+ initialMessages,
123
+ lastUserAttachments.current,
124
+ attachmentCacheRef.current,
125
+ ) as AppMessage[];
126
+ }, [initialMessages, chatConfig.messages]);
127
+
128
+ // Fetch signed URLs for any attachments missing urls (e.g., historical messages)
129
+ React.useEffect(() => {
130
+ const supabase = getSupabase();
131
+ const missing = new Set<string>();
132
+
133
+ for (const msg of displayMessages) {
134
+ if (!msg.attachments?.length) continue;
135
+ for (const att of msg.attachments) {
136
+ if (att.type !== 'image') continue;
137
+ if (att.url || signedUrlMap[att.storageId]) continue;
138
+ missing.add(att.storageId);
139
+ }
140
+ }
141
+
142
+ if (missing.size === 0) return;
143
+ let cancelled = false;
144
+
145
+ (async () => {
146
+ const updates: Record<string, string> = {};
147
+ for (const storageId of missing) {
148
+ const { data, error } = await supabase.storage
149
+ .from('chat-attachments')
150
+ .createSignedUrl(storageId, 60 * 60);
151
+ if (error) {
152
+ console.warn('[useChatHandlers] Failed to sign attachment', {
153
+ storageId,
154
+ error: error.message,
155
+ });
156
+ continue;
157
+ }
158
+ if (data?.signedUrl) {
159
+ updates[storageId] = data.signedUrl;
160
+ }
161
+ }
162
+
163
+ if (!cancelled && Object.keys(updates).length > 0) {
164
+ setSignedUrlMap((prev) => ({ ...prev, ...updates }));
165
+ }
166
+ })();
167
+
168
+ return () => {
169
+ cancelled = true;
170
+ };
171
+ }, [displayMessages, signedUrlMap]);
172
+
173
+ const messagesWithSignedUrls = useMemo((): AppMessage[] => {
174
+ if (!Object.keys(signedUrlMap).length) return displayMessages;
175
+ return displayMessages.map((msg) => {
176
+ if (!msg.attachments?.length) return msg;
177
+ return {
178
+ ...msg,
179
+ attachments: msg.attachments.map((att) =>
180
+ att.type === 'image'
181
+ ? { ...att, url: att.url ?? signedUrlMap[att.storageId] }
182
+ : att,
183
+ ),
184
+ };
185
+ });
186
+ }, [displayMessages, signedUrlMap]);
187
+
188
+ // After we compute display messages, cache any attachments by live message ID
189
+ React.useEffect(() => {
190
+ if (!displayMessages?.length) return;
191
+ const cache = attachmentCacheRef.current;
192
+ let changed = false;
193
+ for (const m of displayMessages) {
194
+ if (m.attachments && m.attachments.length) {
195
+ const existing = cache[m.id];
196
+ if (!existing || existing.length !== m.attachments.length) {
197
+ cache[m.id] = m.attachments as AppAttachment[];
198
+ changed = true;
199
+ }
200
+ }
201
+ }
202
+ if (changed) {
203
+ // noop: we mutate ref; next memo pass will read updated cache
204
+ }
205
+ }, [displayMessages]);
206
+
207
+ // useEffect(() => {
208
+ // console.log(
209
+ // '[Client useChatHandlers] DisplayMessages updated count:',
210
+ // displayMessages.length
211
+ // );
212
+ // if (displayMessages.length > 0) {
213
+ // const lastMsg = displayMessages[displayMessages.length - 1];
214
+ // if (lastMsg) {
215
+ // console.log(
216
+ // `[Client useChatHandlers] Last display message (ID: ${lastMsg.id}, Role: ${lastMsg.role}, Content Type: ${typeof lastMsg.content}):`,
217
+ // JSON.stringify(lastMsg.content)
218
+ // );
219
+ // }
220
+ // }
221
+ // }, [displayMessages]);
222
+
223
+ const handleReportMessage = useCallback((message: AppMessage) => {
224
+ setMessageToReport(message);
225
+ }, []);
226
+
227
+ const handleSubmitReport = useCallback(
228
+ async (reportData: {
229
+ messageId?: string;
230
+ reason: ReportReason;
231
+ details?: string;
232
+ }): Promise<boolean> => {
233
+ if (!reportData.messageId) {
234
+ console.warn(
235
+ '[Client useChatHandlers] Report dismissed: missing messageId',
236
+ );
237
+ return false;
238
+ }
239
+ try {
240
+ // TODO: Implement reporting via Supabase when needed
241
+ console.log('[Client useChatHandlers] Report submitted:', reportData);
242
+ Alert.alert('Report Submitted', 'Thank you for your report.', [
243
+ { text: 'OK' },
244
+ ]);
245
+ return true;
246
+ } catch (error) {
247
+ console.error(
248
+ '[Client useChatHandlers] Failed to submit report:',
249
+ error,
250
+ );
251
+ Alert.alert(
252
+ 'Report Failed',
253
+ "We couldn't submit your report. Please try again.",
254
+ [{ text: 'OK' }],
255
+ );
256
+ return false;
257
+ }
258
+ },
259
+ [],
260
+ );
261
+
262
+ const handleCancelReport = useCallback(() => {
263
+ setMessageToReport(null);
264
+ }, []);
265
+
266
+ const handleTextInputChange = useCallback(
267
+ (text: string) => {
268
+ chatConfig.handleInputChange(text);
269
+ },
270
+ [chatConfig],
271
+ );
272
+
273
+ const handleMessageSubmit = useCallback(
274
+ async (
275
+ attachmentsFromInputBar?: {
276
+ type: 'image';
277
+ storageId: string;
278
+ fileName?: string;
279
+ mimeType?: string;
280
+ url?: string;
281
+ }[],
282
+ ) => {
283
+ // Dismiss keyboard when sending a message
284
+ Keyboard.dismiss();
285
+
286
+ // Read the current input value just before submission to prevent stale closure bugs
287
+ const currentInput = chatConfig.input;
288
+ const trimmedInput = currentInput.trim();
289
+
290
+ // console.log(
291
+ // '[Client useChatHandlers handleMessageSubmit] Attempting to submit. Input:',
292
+ // currentInput,
293
+ // 'Attachments from InputBar:',
294
+ // attachmentsFromInputBar,
295
+ // 'ConvID:',
296
+ // conversationId
297
+ // );
298
+
299
+ if (!conversationId) {
300
+ console.error(
301
+ '[Client useChatHandlers handleMessageSubmit] Missing conversationId.',
302
+ );
303
+ Alert.alert(
304
+ 'Error',
305
+ 'Chat session not fully initialized. Cannot send message.',
306
+ );
307
+ return;
308
+ }
309
+
310
+ if (
311
+ !trimmedInput &&
312
+ (!attachmentsFromInputBar || attachmentsFromInputBar.length === 0)
313
+ ) {
314
+ console.log(
315
+ '[Client useChatHandlers handleMessageSubmit] Empty message and no attachments. Not submitting.',
316
+ );
317
+ return;
318
+ }
319
+
320
+ // Clear the input immediately and mark preflight sending so UI responds fast
321
+ setIsPreflightSending(true);
322
+ handleInputChangeRef.current('');
323
+
324
+ // Store attachments in ref for optimistic UI display
325
+ lastUserAttachments.current = attachmentsFromInputBar?.map((att) => ({
326
+ type: 'image' as const,
327
+ storageId: att.storageId,
328
+ fileName: att.fileName,
329
+ mimeType: att.mimeType,
330
+ url: att.url,
331
+ }));
332
+
333
+ // Store user message and retrieve the persisted ID before triggering streaming
334
+ const storedMessageId = await MessageHandlerService.storeUserMessage({
335
+ currentInput: trimmedInput,
336
+ conversationId,
337
+ attachmentsFromInputBar,
338
+ storeUserMessageMutation,
339
+ });
340
+
341
+ if (!storedMessageId) {
342
+ console.error(
343
+ '[Client useChatHandlers] Unable to store user message; aborting streaming request.',
344
+ );
345
+ setIsPreflightSending(false);
346
+ handleInputChangeRef.current(currentInput);
347
+ lastUserAttachments.current = undefined;
348
+ return;
349
+ }
350
+
351
+ // Submit to AI
352
+ const dummyFormEvent = {
353
+ preventDefault: () => {},
354
+ } as React.FormEvent<HTMLFormElement>;
355
+
356
+ try {
357
+ const submitPromise = chatConfig.handleSubmit(dummyFormEvent, {
358
+ body: {
359
+ conversationId: conversationId,
360
+ preferredModel,
361
+ userMessageId: storedMessageId,
362
+ text: trimmedInput,
363
+ attachments:
364
+ attachmentsFromInputBar && attachmentsFromInputBar.length > 0
365
+ ? attachmentsFromInputBar
366
+ : undefined,
367
+ },
368
+ });
369
+ // Handover loading state to chatConfig once stream starts
370
+ setIsPreflightSending(false);
371
+ await submitPromise;
372
+ } catch (submitError) {
373
+ console.error(
374
+ '[Client useChatHandlers] Streaming submission failed:',
375
+ submitError,
376
+ );
377
+ setIsPreflightSending(false);
378
+ handleInputChangeRef.current(currentInput);
379
+ return;
380
+ }
381
+
382
+ // console.log(
383
+ // '[Client useChatHandlers handleMessageSubmit] chatConfig.handleSubmit called for bot response.'
384
+ // );
385
+
386
+ // Note: We don't clear lastUserAttachments here anymore.
387
+ // Once initialMessages contains the stored user message (with attachments),
388
+ // the initialEquivalent?.attachments path takes over naturally.
389
+ },
390
+ [conversationId, storeUserMessageMutation, chatConfig, preferredModel],
391
+ );
392
+
393
+ const handleClearChat = useCallback(() => {
394
+ if (!conversationId) {
395
+ console.error('[handleClearChat] Missing conversationId');
396
+ return;
397
+ }
398
+
399
+ MessageHandlerService.clearChat({
400
+ conversationId,
401
+ clearConversationMutation,
402
+ onCleared: () => {
403
+ chatConfig.resetMessages();
404
+ },
405
+ });
406
+ // Reset local caches immediately for a clean slate
407
+ lastUserAttachments.current = undefined;
408
+ attachmentCacheRef.current = {};
409
+ }, [conversationId, clearConversationMutation, chatConfig]);
410
+
411
+ // When conversation changes, reset transient caches to prevent leakage
412
+ React.useEffect(() => {
413
+ lastUserAttachments.current = undefined;
414
+ attachmentCacheRef.current = {};
415
+ }, [conversationId]);
416
+
417
+ return {
418
+ ...chatConfig,
419
+ isLoading: chatConfig.isLoading || isPreflightSending,
420
+ messages: messagesWithSignedUrls,
421
+ messageToReport,
422
+ handleReportMessage,
423
+ handleSubmitReport,
424
+ handleCancelReport,
425
+ handleTextInputChange,
426
+ handleMessageSubmit,
427
+ handleClearChat,
428
+ };
429
+ };
@@ -0,0 +1,89 @@
1
+ import { useCallback, useEffect, useState } from 'react';
2
+
3
+ import { defaultCacheService } from '@/core/cache';
4
+
5
+ import type { Provider } from '../constants/models';
6
+
7
+ export type SearchMode = 'provider' | 'tavily' | 'none';
8
+
9
+ type ChatbotSettings = {
10
+ provider: Provider;
11
+ model: string;
12
+ searchMode: SearchMode;
13
+ };
14
+
15
+ const CACHE_KEY = 'chatbot_settings';
16
+
17
+ const DEFAULT_SETTINGS: ChatbotSettings = {
18
+ provider: 'openai',
19
+ model: 'gpt-4.1-nano',
20
+ searchMode: 'tavily',
21
+ };
22
+
23
+ export function useChatbotSettings() {
24
+ const [settings, setSettings] = useState<ChatbotSettings>(DEFAULT_SETTINGS);
25
+ const [isLoaded, setIsLoaded] = useState(false);
26
+
27
+ // Load settings from cache on mount
28
+ useEffect(() => {
29
+ const loadSettings = async () => {
30
+ try {
31
+ const cachedSettings =
32
+ await defaultCacheService.getItem<ChatbotSettings>(CACHE_KEY);
33
+ if (cachedSettings) {
34
+ setSettings(cachedSettings);
35
+ }
36
+ } catch (error) {
37
+ console.error('[useChatbotSettings] Failed to load settings:', error);
38
+ } finally {
39
+ setIsLoaded(true);
40
+ }
41
+ };
42
+
43
+ loadSettings();
44
+ }, []);
45
+
46
+ // Save settings to cache whenever they change
47
+ const updateSettings = useCallback(
48
+ async (newSettings: Partial<ChatbotSettings>) => {
49
+ const updatedSettings = { ...settings, ...newSettings };
50
+ setSettings(updatedSettings);
51
+
52
+ try {
53
+ await defaultCacheService.setItem(CACHE_KEY, updatedSettings);
54
+ } catch (error) {
55
+ console.error('[useChatbotSettings] Failed to save settings:', error);
56
+ }
57
+ },
58
+ [settings],
59
+ );
60
+
61
+ const updateProvider = useCallback(
62
+ (provider: Provider) => {
63
+ updateSettings({ provider });
64
+ },
65
+ [updateSettings],
66
+ );
67
+
68
+ const updateModel = useCallback(
69
+ (model: string) => {
70
+ updateSettings({ model });
71
+ },
72
+ [updateSettings],
73
+ );
74
+
75
+ const updateSearchMode = useCallback(
76
+ (searchMode: SearchMode) => {
77
+ updateSettings({ searchMode });
78
+ },
79
+ [updateSettings],
80
+ );
81
+
82
+ return {
83
+ settings,
84
+ isLoaded,
85
+ updateProvider,
86
+ updateModel,
87
+ updateSearchMode,
88
+ };
89
+ }
@@ -0,0 +1,90 @@
1
+ import { useEffect, useMemo, useRef, useState } from 'react';
2
+
3
+ import { chatbotApi } from '@/api-client';
4
+ import type {
5
+ AppMessage,
6
+ MessageAttachment,
7
+ MessageMetadata,
8
+ ToolCallRecord,
9
+ } from '@/features/chatbot/types';
10
+
11
+ /**
12
+ * Hook to manage conversation state and load historical messages
13
+ */
14
+ export const useConversation = (userId?: string) => {
15
+ const getOrCreateConversation = chatbotApi.useGetOrCreateConversation();
16
+ const [conversationId, setConversationId] = useState<string | null>(null);
17
+ const lastUserIdRef = useRef<string | undefined>(undefined);
18
+ const initializingRef = useRef(false);
19
+
20
+ useEffect(() => {
21
+ // Reset if user changed
22
+ if (userId !== lastUserIdRef.current) {
23
+ lastUserIdRef.current = userId;
24
+ initializingRef.current = false;
25
+ setConversationId(null);
26
+ }
27
+
28
+ const initializeConversation = async () => {
29
+ // Guard: skip if no user, already have conversation, or already initializing
30
+ if (!userId || conversationId || initializingRef.current) {
31
+ return;
32
+ }
33
+
34
+ initializingRef.current = true;
35
+ console.log(
36
+ '[Client useConversation] Initializing conversation for user:',
37
+ userId,
38
+ );
39
+
40
+ try {
41
+ const conversation = await getOrCreateConversation.mutateAsync();
42
+ if (conversation?.id) {
43
+ setConversationId(conversation.id);
44
+ console.log(
45
+ '[Client useConversation] Conversation ID set:',
46
+ conversation.id,
47
+ );
48
+ } else {
49
+ // No conversation returned, allow retry
50
+ initializingRef.current = false;
51
+ }
52
+ } catch (error) {
53
+ console.error(
54
+ '[Client useConversation] Failed to get/create conversation:',
55
+ error,
56
+ );
57
+ initializingRef.current = false;
58
+ }
59
+ };
60
+
61
+ initializeConversation();
62
+ // eslint-disable-next-line react-hooks/exhaustive-deps
63
+ }, [userId, conversationId]);
64
+
65
+ const historicalMessagesResult = chatbotApi.useListMessages(conversationId);
66
+
67
+ const initialMessages = useMemo((): AppMessage[] => {
68
+ const pages = historicalMessagesResult.data?.pages;
69
+ if (!pages || pages.length === 0) {
70
+ return [];
71
+ }
72
+
73
+ // Flatten all pages and get messages
74
+ const allMessages = pages.flatMap((page) => page?.messages || []);
75
+
76
+ return allMessages.map(
77
+ (msg): AppMessage => ({
78
+ id: msg.id,
79
+ role: msg.author_type === 'user' ? 'user' : 'assistant',
80
+ content: msg.text || '',
81
+ createdAt: new Date(msg.created_at),
82
+ attachments: msg.attachments as MessageAttachment[] | undefined,
83
+ toolCalls: msg.tool_calls as ToolCallRecord[] | undefined,
84
+ metadata: msg.metadata as MessageMetadata | undefined,
85
+ }),
86
+ );
87
+ }, [historicalMessagesResult.data]);
88
+
89
+ return { conversationId, initialMessages };
90
+ };