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
@@ -32,7 +32,7 @@ type Props = {
32
32
 
33
33
  export const RadarChart = ({ data, config = {}, style }: Props) => {
34
34
  const hasData = data.length > 0;
35
- const [containerWidth, setContainerWidth] = useState(300);
35
+ const [containerWidth, setContainerWidth] = useState(() => config.width ?? 300);
36
36
  const {
37
37
  height = 200,
38
38
  showLabels = true,
@@ -40,7 +40,7 @@ export const RadarChart = ({ data, config = {}, style }: Props) => {
40
40
  duration = 1000,
41
41
  maxValue,
42
42
  } = config;
43
- const chartWidth = containerWidth || config.width || 300;
43
+ const chartWidth = config.width ?? containerWidth;
44
44
 
45
45
  const theme = useThemeConfig();
46
46
  const primaryColor = theme.colors.primary as string;
@@ -76,8 +76,10 @@ export const RadarChart = ({ data, config = {}, style }: Props) => {
76
76
  const centerX = chartWidth / 2;
77
77
  const centerY = height / 2;
78
78
  const radius = Math.min(chartWidth, height) / 2 - 40;
79
- const maxVal =
80
- maxValue || (hasData ? Math.max(...data.map((d) => d.value)) : 1);
79
+ const maxVal = Math.max(
80
+ 1,
81
+ maxValue ?? (hasData ? Math.max(...data.map((d) => d.value)) : 1),
82
+ );
81
83
 
82
84
  const angleStep = hasData ? (2 * Math.PI) / data.length : 0;
83
85
  const points = useMemo(() => {
@@ -68,7 +68,7 @@ export const RadialBarChart = ({ data, config = {}, style }: Props) => {
68
68
 
69
69
  if (!data.length) return null;
70
70
 
71
- const maxValue = Math.max(...data.map((d) => d.value));
71
+ const maxValue = Math.max(1, ...data.map((d) => d.value));
72
72
  const size = containerSize || 200;
73
73
  const center = size / 2;
74
74
  const maxRadius = (size - padding * 2) / 2;
@@ -42,7 +42,7 @@ export const StackedBarChart = ({
42
42
  categories = [],
43
43
  horizontal = false,
44
44
  }: Props) => {
45
- const [containerWidth, setContainerWidth] = useState(300);
45
+ const [containerWidth, setContainerWidth] = useState(() => config.width ?? 300);
46
46
  const {
47
47
  height = 200,
48
48
  padding = 20,
@@ -51,7 +51,7 @@ export const StackedBarChart = ({
51
51
  animated = true,
52
52
  duration = 800,
53
53
  } = config;
54
- const chartWidth = containerWidth || config.width || 300;
54
+ const chartWidth = config.width ?? containerWidth;
55
55
 
56
56
  const theme = useThemeConfig();
57
57
  const primaryColor = theme.colors.primary as string;
@@ -87,9 +87,10 @@ export const StackedBarChart = ({
87
87
  if (!data.length) return null;
88
88
 
89
89
  const maxValue = Math.max(
90
+ 1,
90
91
  ...data.map((d) => d.values.reduce((s, v) => s + v, 0)),
91
92
  );
92
- const seriesCount = data[0]?.values.length || 0;
93
+ const seriesCount = Math.max(0, ...data.map((d) => d.values.length));
93
94
  const innerChartWidth = chartWidth - padding * 2;
94
95
  const chartHeight = height - padding * 2;
95
96
 
@@ -122,7 +123,7 @@ export const StackedBarChart = ({
122
123
  {item.values.map((value, valueIndex) => {
123
124
  const segmentWidth = (value / maxValue) * innerChartWidth;
124
125
  const w = segmentWidth * progress;
125
- const x = padding + cumulativeWidth;
126
+ const x = padding + cumulativeWidth * progress;
126
127
  cumulativeWidth += segmentWidth;
127
128
  return (
128
129
  <RoundedRect
@@ -1,12 +1,8 @@
1
1
  {
2
2
  "name": "charts",
3
- "version": "1.0.1",
4
- "description": "Beautiful data visualization with charts",
3
+ "version": "1.0.0",
4
+ "description": "Beautiful animated charts",
5
5
  "copy": [
6
- {
7
- "from": "apps/native/src/app/(root)/(protected)/charts",
8
- "to": "apps/native/src/app/(root)/(protected)/charts"
9
- },
10
6
  {
11
7
  "from": "apps/native/src/features/charts",
12
8
  "to": "apps/native/src/features/charts"
@@ -16,13 +12,8 @@
16
12
  "href": "/(root)/(protected)/charts",
17
13
  "label": "Charts",
18
14
  "icon": "📊",
19
- "color": "#6366F1"
15
+ "color": "#10B981"
20
16
  },
21
17
  "target": "native",
22
- "dependencies": {
23
- "expo": [
24
- "@shopify/react-native-skia",
25
- "react-native-svg"
26
- ]
27
- }
18
+ "dependencies": { "expo": ["react-native-reanimated", "react-native-svg"] }
28
19
  }
Binary file
@@ -0,0 +1 @@
1
+ export { default } from '@/features/chatbot/app';
@@ -19,9 +19,9 @@
19
19
  * and render rules, but the complex logic has been extracted into separate modules.
20
20
  */
21
21
 
22
- import * as Clipboard from 'expo-clipboard';
23
- import MarkdownIt from 'markdown-it';
24
- import React, { useCallback, useEffect, useMemo } from 'react';
22
+ import * as Clipboard from "expo-clipboard";
23
+ import MarkdownIt from "markdown-it";
24
+ import React, { useCallback, useEffect, useMemo } from "react";
25
25
  import {
26
26
  Image,
27
27
  type ImageStyle,
@@ -33,18 +33,18 @@ import {
33
33
  UIManager,
34
34
  View,
35
35
  type ViewStyle,
36
- } from 'react-native';
37
- import Markdown, { type RenderRules } from 'react-native-markdown-display';
36
+ } from "react-native";
37
+ import Markdown, { type RenderRules } from "react-native-markdown-display";
38
38
  import {
39
39
  atomOneDark,
40
40
  github,
41
- } from 'react-syntax-highlighter/styles/hljs';
41
+ } from "react-syntax-highlighter/dist/esm/styles/hljs";
42
42
 
43
- import { useToast } from '@/components/ui/utils';
44
- import { translate } from '@/lib';
45
- import { useThemeConfig } from '@/lib/use-theme-config';
43
+ import { useToast } from "@/components/ui/utils";
44
+ import { translate } from "@/lib";
45
+ import { useThemeConfig } from "@/lib/use-theme-config";
46
46
 
47
- import { CodeBlock, TableRenderer } from './markdown';
47
+ import { CodeBlock, TableRenderer } from "./markdown";
48
48
 
49
49
  const md = new MarkdownIt({
50
50
  linkify: true,
@@ -70,8 +70,8 @@ export const ChatMarkdown = React.memo(function ChatMarkdown({
70
70
 
71
71
  useEffect(() => {
72
72
  if (
73
- Platform.OS === 'android' &&
74
- typeof UIManager.setLayoutAnimationEnabledExperimental === 'function'
73
+ Platform.OS === "android" &&
74
+ typeof UIManager.setLayoutAnimationEnabledExperimental === "function"
75
75
  ) {
76
76
  UIManager.setLayoutAnimationEnabledExperimental(true);
77
77
  }
@@ -88,11 +88,11 @@ export const ChatMarkdown = React.memo(function ChatMarkdown({
88
88
  const monospaceFontFamily = useMemo(
89
89
  () =>
90
90
  Platform.select({
91
- ios: 'Menlo-Regular',
92
- android: 'monospace',
93
- default: 'Menlo',
94
- }) ?? 'Menlo',
95
- [],
91
+ ios: "Menlo-Regular",
92
+ android: "monospace",
93
+ default: "Menlo",
94
+ }) ?? "Menlo",
95
+ []
96
96
  );
97
97
 
98
98
  const columnDividerWidth = StyleSheet.hairlineWidth || 0.5;
@@ -114,15 +114,15 @@ export const ChatMarkdown = React.memo(function ChatMarkdown({
114
114
  paragraph: { marginBottom: 10, color: textColor },
115
115
  link: {
116
116
  color: linkColor,
117
- textDecorationLine: 'underline' as TextStyle['textDecorationLine'],
118
- fontWeight: '500' as TextStyle['fontWeight'],
117
+ textDecorationLine: "underline" as TextStyle["textDecorationLine"],
118
+ fontWeight: "500" as TextStyle["fontWeight"],
119
119
  },
120
120
  strong: {
121
- fontWeight: '600' as TextStyle['fontWeight'],
121
+ fontWeight: "600" as TextStyle["fontWeight"],
122
122
  color: textColor,
123
123
  },
124
124
  em: {
125
- fontStyle: 'italic' as TextStyle['fontStyle'],
125
+ fontStyle: "italic" as TextStyle["fontStyle"],
126
126
  color: textColor,
127
127
  },
128
128
  code_inline: {
@@ -155,7 +155,7 @@ export const ChatMarkdown = React.memo(function ChatMarkdown({
155
155
  },
156
156
  list_item: { marginBottom: 6, color: textColor },
157
157
  table: {
158
- minWidth: '100%',
158
+ minWidth: "100%",
159
159
  backgroundColor: theme.colors.background,
160
160
  },
161
161
  thead: {
@@ -165,7 +165,7 @@ export const ChatMarkdown = React.memo(function ChatMarkdown({
165
165
  tr: {
166
166
  borderBottomWidth: StyleSheet.hairlineWidth,
167
167
  borderColor,
168
- flexDirection: 'row',
168
+ flexDirection: "row",
169
169
  },
170
170
  th: {
171
171
  flexGrow: 0,
@@ -188,42 +188,42 @@ export const ChatMarkdown = React.memo(function ChatMarkdown({
188
188
  },
189
189
  heading1: {
190
190
  fontSize: 28,
191
- fontWeight: '700',
191
+ fontWeight: "700",
192
192
  color: textColor,
193
193
  marginTop: 18,
194
194
  marginBottom: 12,
195
195
  },
196
196
  heading2: {
197
197
  fontSize: 22,
198
- fontWeight: '700',
198
+ fontWeight: "700",
199
199
  color: textColor,
200
200
  marginTop: 16,
201
201
  marginBottom: 10,
202
202
  },
203
203
  heading3: {
204
204
  fontSize: 19,
205
- fontWeight: '600',
205
+ fontWeight: "600",
206
206
  color: textColor,
207
207
  marginTop: 14,
208
208
  marginBottom: 8,
209
209
  },
210
210
  heading4: {
211
211
  fontSize: 17,
212
- fontWeight: '600',
212
+ fontWeight: "600",
213
213
  color: textColor,
214
214
  marginTop: 12,
215
215
  marginBottom: 8,
216
216
  },
217
217
  heading5: {
218
218
  fontSize: 16,
219
- fontWeight: '600',
219
+ fontWeight: "600",
220
220
  color: textColor,
221
221
  marginTop: 10,
222
222
  marginBottom: 6,
223
223
  },
224
224
  heading6: {
225
225
  fontSize: 15,
226
- fontWeight: '600' as TextStyle['fontWeight'],
226
+ fontWeight: "600" as TextStyle["fontWeight"],
227
227
  color: textColor,
228
228
  marginTop: 8,
229
229
  marginBottom: 6,
@@ -236,10 +236,10 @@ export const ChatMarkdown = React.memo(function ChatMarkdown({
236
236
  imageWrapper: {
237
237
  marginVertical: 8,
238
238
  borderRadius: 14,
239
- overflow: 'hidden',
239
+ overflow: "hidden",
240
240
  },
241
241
  image: {
242
- width: '100%',
242
+ width: "100%",
243
243
  aspectRatio: 16 / 9,
244
244
  backgroundColor: codeBg,
245
245
  },
@@ -247,7 +247,7 @@ export const ChatMarkdown = React.memo(function ChatMarkdown({
247
247
  marginTop: 6,
248
248
  fontSize: 13,
249
249
  color: subtleText,
250
- textAlign: 'center',
250
+ textAlign: "center",
251
251
  },
252
252
  };
253
253
 
@@ -270,7 +270,7 @@ export const ChatMarkdown = React.memo(function ChatMarkdown({
270
270
  ...baseTheme,
271
271
  hljs: {
272
272
  ...baseTheme.hljs,
273
- background: 'transparent',
273
+ background: "transparent",
274
274
  color: codeFg,
275
275
  },
276
276
  };
@@ -279,27 +279,27 @@ export const ChatMarkdown = React.memo(function ChatMarkdown({
279
279
  const codeContentStyle = useMemo(
280
280
  () =>
281
281
  ({
282
- backgroundColor: 'transparent',
282
+ backgroundColor: "transparent",
283
283
  padding: 0,
284
284
  margin: 0,
285
- }) satisfies ViewStyle,
286
- [],
285
+ } satisfies ViewStyle),
286
+ []
287
287
  );
288
288
 
289
289
  const tableScrollViewStyle = useMemo(
290
290
  () =>
291
291
  ({
292
292
  marginVertical: 0,
293
- }) satisfies ViewStyle,
294
- [],
293
+ } satisfies ViewStyle),
294
+ []
295
295
  );
296
296
 
297
297
  const tableScrollContentStyle = useMemo(
298
298
  () =>
299
299
  ({
300
300
  flexGrow: 1,
301
- }) satisfies ViewStyle,
302
- [],
301
+ } satisfies ViewStyle),
302
+ []
303
303
  );
304
304
 
305
305
  const tableOuterStyle = useMemo(
@@ -308,84 +308,84 @@ export const ChatMarkdown = React.memo(function ChatMarkdown({
308
308
  borderWidth: 1,
309
309
  borderColor,
310
310
  borderRadius: 10,
311
- overflow: 'hidden',
311
+ overflow: "hidden",
312
312
  marginVertical: 12,
313
313
  backgroundColor: theme.colors.background,
314
- }) satisfies ViewStyle,
315
- [borderColor, theme.colors.background],
314
+ } satisfies ViewStyle),
315
+ [borderColor, theme.colors.background]
316
316
  );
317
317
 
318
318
  const codeActionsContainerStyle = useMemo(
319
319
  () =>
320
320
  ({
321
- position: 'absolute',
321
+ position: "absolute",
322
322
  top: 12,
323
323
  right: 12,
324
- flexDirection: 'row',
325
- alignItems: 'center',
326
- justifyContent: 'flex-end',
327
- }) satisfies ViewStyle,
328
- [],
324
+ flexDirection: "row",
325
+ alignItems: "center",
326
+ justifyContent: "flex-end",
327
+ } satisfies ViewStyle),
328
+ []
329
329
  );
330
330
 
331
331
  const codeActionButtonBaseStyle = useMemo(
332
332
  () =>
333
333
  ({
334
- alignItems: 'center',
335
- justifyContent: 'center',
334
+ alignItems: "center",
335
+ justifyContent: "center",
336
336
  borderRadius: 999,
337
337
  borderWidth: 1,
338
338
  borderColor,
339
339
  paddingHorizontal: 12,
340
340
  paddingVertical: 8,
341
341
  backgroundColor: isDarkMode
342
- ? 'rgba(15, 15, 15, 0.9)'
343
- : 'rgba(255, 255, 255, 0.92)',
344
- }) satisfies ViewStyle,
345
- [borderColor, isDarkMode],
342
+ ? "rgba(15, 15, 15, 0.9)"
343
+ : "rgba(255, 255, 255, 0.92)",
344
+ } satisfies ViewStyle),
345
+ [borderColor, isDarkMode]
346
346
  );
347
347
 
348
348
  const codeToggleButtonSpacingStyle = useMemo(
349
349
  () =>
350
350
  ({
351
351
  marginLeft: 16,
352
- }) satisfies ViewStyle,
353
- [],
352
+ } satisfies ViewStyle),
353
+ []
354
354
  );
355
355
 
356
356
  const codeContentContainerStyle = useMemo(
357
357
  () =>
358
358
  ({
359
- overflow: 'hidden',
359
+ overflow: "hidden",
360
360
  marginTop: 24,
361
- }) satisfies ViewStyle,
362
- [],
361
+ } satisfies ViewStyle),
362
+ []
363
363
  );
364
364
 
365
365
  const copyCodeToClipboard = useCallback(
366
366
  async (code: string) => {
367
367
  try {
368
368
  if (!code.trim()) {
369
- toast.warning(translate('chatbot.empty_message'), {
369
+ toast.warning(translate("chatbot.empty_message"), {
370
370
  duration: 2000,
371
371
  });
372
372
  return;
373
373
  }
374
374
 
375
375
  await Clipboard.setStringAsync(code);
376
- toast.success(translate('chatbot.copy_code_success'), {
376
+ toast.success(translate("chatbot.copy_code_success"), {
377
377
  duration: 2000,
378
378
  });
379
379
  } catch (error) {
380
- console.error('[CHATBOT_FRONTEND] Copy code failed:', {
381
- error: error instanceof Error ? error.message : 'Unknown error',
380
+ console.error("[CHATBOT_FRONTEND] Copy code failed:", {
381
+ error: error instanceof Error ? error.message : "Unknown error",
382
382
  });
383
- toast.error(translate('chatbot.copy_code_failed'), {
383
+ toast.error(translate("chatbot.copy_code_failed"), {
384
384
  duration: 3000,
385
385
  });
386
386
  }
387
387
  },
388
- [toast],
388
+ [toast]
389
389
  );
390
390
 
391
391
  // Memoize CodeBlock wrapper to pass all required props
@@ -393,7 +393,7 @@ export const ChatMarkdown = React.memo(function ChatMarkdown({
393
393
  (
394
394
  containerStyle: ViewStyle | TextStyle | ImageStyle,
395
395
  content: string,
396
- language: string,
396
+ language: string
397
397
  ) => {
398
398
  return (
399
399
  <CodeBlock
@@ -422,7 +422,7 @@ export const ChatMarkdown = React.memo(function ChatMarkdown({
422
422
  codeToggleButtonSpacingStyle,
423
423
  codeContentContainerStyle,
424
424
  copyCodeToClipboard,
425
- ],
425
+ ]
426
426
  );
427
427
 
428
428
  const handleLinkPress = useCallback((url: string) => {
@@ -443,25 +443,25 @@ export const ChatMarkdown = React.memo(function ChatMarkdown({
443
443
  _children: React.ReactNode[],
444
444
  _parent: any[],
445
445
  styles: Record<string, any>,
446
- _inheritedStyles: Record<string, any> = {},
446
+ _inheritedStyles: Record<string, any> = {}
447
447
  ) => {
448
- const rawContent = typeof node.content === 'string' ? node.content : '';
449
- const trimmed = rawContent.replace(/\n+$/u, '');
448
+ const rawContent = typeof node.content === "string" ? node.content : "";
449
+ const trimmed = rawContent.replace(/\n+$/u, "");
450
450
  const content = trimmed.length > 0 ? trimmed : rawContent;
451
451
 
452
452
  const info = node.attributes?.info;
453
453
  const className = node.attributes?.class;
454
454
  const languageFromInfo =
455
- typeof info === 'string' && info.trim().length > 0
455
+ typeof info === "string" && info.trim().length > 0
456
456
  ? info.trim().split(/\s+/u)[0]
457
457
  : undefined;
458
458
  const languageFromClass =
459
- typeof className === 'string'
460
- ? (className.match(/language-([\w-]+)/u)?.[1] ??
461
- className.match(/lang-([\w-]+)/u)?.[1])
459
+ typeof className === "string"
460
+ ? className.match(/language-([\w-]+)/u)?.[1] ??
461
+ className.match(/lang-([\w-]+)/u)?.[1]
462
462
  : undefined;
463
- const language = (languageFromInfo ?? languageFromClass ?? '')
464
- .replace(/^language-/u, '')
463
+ const language = (languageFromInfo ?? languageFromClass ?? "")
464
+ .replace(/^language-/u, "")
465
465
  .toLowerCase();
466
466
 
467
467
  const containerStyle =
@@ -476,7 +476,7 @@ export const ChatMarkdown = React.memo(function ChatMarkdown({
476
476
 
477
477
  return {
478
478
  code: (node, _children, _parent, styles) => {
479
- const content = node.content || '';
479
+ const content = node.content || "";
480
480
  return (
481
481
  <Text key={node.key} style={styles.code_inline}>
482
482
  {content}
@@ -501,21 +501,21 @@ export const ChatMarkdown = React.memo(function ChatMarkdown({
501
501
  },
502
502
  link: (node, children, _parent, styles) => {
503
503
  const href =
504
- typeof node.attributes?.href === 'string' ? node.attributes.href : '';
504
+ typeof node.attributes?.href === "string" ? node.attributes.href : "";
505
505
  const titleAttr =
506
- typeof node.attributes?.title === 'string'
506
+ typeof node.attributes?.title === "string"
507
507
  ? node.attributes.title.trim()
508
- : '';
508
+ : "";
509
509
  const fallbackLabel =
510
- typeof node.content === 'string' && node.content.trim().length > 0
510
+ typeof node.content === "string" && node.content.trim().length > 0
511
511
  ? node.content.trim()
512
512
  : href;
513
513
  const linkLabel =
514
514
  titleAttr.length > 0
515
515
  ? titleAttr
516
516
  : children && children.length > 0
517
- ? children
518
- : fallbackLabel;
517
+ ? children
518
+ : fallbackLabel;
519
519
 
520
520
  return (
521
521
  <Text
@@ -537,7 +537,7 @@ export const ChatMarkdown = React.memo(function ChatMarkdown({
537
537
  return null;
538
538
  }
539
539
 
540
- const alt = node.attributes?.alt ?? '';
540
+ const alt = node.attributes?.alt ?? "";
541
541
  const caption = alt.trim().length > 0 ? alt : undefined;
542
542
 
543
543
  return (
@@ -547,7 +547,7 @@ export const ChatMarkdown = React.memo(function ChatMarkdown({
547
547
  style={styles.image}
548
548
  resizeMode="cover"
549
549
  accessible
550
- accessibilityLabel={caption ?? 'Markdown image'}
550
+ accessibilityLabel={caption ?? "Markdown image"}
551
551
  />
552
552
  {caption ? (
553
553
  <Text style={styles.image_caption}>{caption}</Text>