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.
- package/CHANGELOG.md +32 -0
- package/README.md +63 -169
- package/dist/commands/add.d.ts +1 -1
- package/dist/commands/add.d.ts.map +1 -1
- package/dist/commands/add.js +547 -589
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/checklist.d.ts +1 -1
- package/dist/commands/checklist.d.ts.map +1 -1
- package/dist/commands/checklist.js +40 -39
- package/dist/commands/checklist.js.map +1 -1
- package/dist/commands/doctor.d.ts +1 -1
- package/dist/commands/doctor.js +22 -22
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/env.d.ts +1 -1
- package/dist/commands/env.d.ts.map +1 -1
- package/dist/commands/env.js +58 -53
- package/dist/commands/env.js.map +1 -1
- package/dist/commands/health.d.ts +1 -1
- package/dist/commands/health.d.ts.map +1 -1
- package/dist/commands/health.js +101 -93
- package/dist/commands/health.js.map +1 -1
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +416 -296
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/remove.d.ts +1 -1
- package/dist/commands/remove.d.ts.map +1 -1
- package/dist/commands/remove.js +77 -64
- package/dist/commands/remove.js.map +1 -1
- package/dist/commands/status.d.ts +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +15 -14
- package/dist/commands/status.js.map +1 -1
- package/dist/core/__tests__/detect.test.js +68 -34
- package/dist/core/__tests__/detect.test.js.map +1 -1
- package/dist/core/ast.d.ts +14 -0
- package/dist/core/ast.d.ts.map +1 -0
- package/dist/core/ast.js +239 -0
- package/dist/core/ast.js.map +1 -0
- package/dist/core/codemod.d.ts.map +1 -1
- package/dist/core/codemod.js +62 -44
- package/dist/core/codemod.js.map +1 -1
- package/dist/core/config.d.ts +10 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +51 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/detect.d.ts +8 -2
- package/dist/core/detect.d.ts.map +1 -1
- package/dist/core/detect.js +52 -21
- package/dist/core/detect.js.map +1 -1
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +9 -8
- package/dist/core/errors.js.map +1 -1
- package/dist/core/exec.d.ts +16 -0
- package/dist/core/exec.d.ts.map +1 -0
- package/dist/core/exec.js +48 -0
- package/dist/core/exec.js.map +1 -0
- package/dist/core/manualSteps.d.ts +7 -0
- package/dist/core/manualSteps.d.ts.map +1 -0
- package/dist/core/manualSteps.js +59 -0
- package/dist/core/manualSteps.js.map +1 -0
- package/dist/core/paths.d.ts +3 -1
- package/dist/core/paths.d.ts.map +1 -1
- package/dist/core/paths.js +14 -10
- package/dist/core/paths.js.map +1 -1
- package/dist/core/spinner.d.ts +1 -1
- package/dist/core/spinner.d.ts.map +1 -1
- package/dist/core/spinner.js +38 -8
- package/dist/core/spinner.js.map +1 -1
- package/dist/core/vosk.d.ts.map +1 -1
- package/dist/core/vosk.js +50 -39
- package/dist/core/vosk.js.map +1 -1
- package/docs/manual-testing.md +91 -0
- package/package.json +6 -3
- package/recipes/audio-recorder/apps/native/src/app/audio-recorder/index.tsx +5 -0
- package/recipes/audio-recorder/recipe.json +3 -3
- package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/audio-player.tsx +301 -0
- package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/audio-recorder.tsx +373 -0
- package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/audio-waveform.tsx +270 -0
- package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/index.ts +4 -0
- package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/recording-list.tsx +89 -0
- package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/audio-player-demo.tsx +66 -0
- package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/audio-recorder-cloud.tsx +68 -0
- package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/audio-recorder-interview.tsx +102 -0
- package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/basic.tsx +27 -0
- package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/index.ts +5 -0
- package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/with-recording-list-demo.tsx +82 -0
- package/recipes/audio-recorder-supabase/packages/backend/src/services/recordings.ts +369 -0
- package/recipes/audio-recorder-supabase/packages/backend/supabase/migrations/recordings.sql +70 -0
- package/recipes/audio-recorder-supabase/recipe.json +35 -0
- package/recipes/audio-recorder-supabase@latest.zip +0 -0
- package/recipes/audio-recorder@latest.zip +0 -0
- package/recipes/charts/apps/native/src/features/charts/components/bar-chart.tsx +3 -3
- package/recipes/charts/apps/native/src/features/charts/components/candlestick-chart.tsx +2 -2
- package/recipes/charts/apps/native/src/features/charts/components/chart-card.tsx +5 -5
- package/recipes/charts/apps/native/src/features/charts/components/column-chart.tsx +3 -3
- package/recipes/charts/apps/native/src/features/charts/components/doughnut-chart.tsx +20 -4
- package/recipes/charts/apps/native/src/features/charts/components/line-chart.tsx +7 -6
- package/recipes/charts/apps/native/src/features/charts/components/radar-chart.tsx +6 -4
- package/recipes/charts/apps/native/src/features/charts/components/radial-bar-chart.tsx +1 -1
- package/recipes/charts/apps/native/src/features/charts/components/stacked-bar-chart.tsx +5 -4
- package/recipes/charts/recipe.json +4 -13
- package/recipes/charts@latest.zip +0 -0
- package/recipes/chatbot/apps/native/src/app/chatbot/index.tsx +1 -0
- package/recipes/chatbot/apps/native/src/features/chatbot/components/chat-markdown.tsx +86 -86
- package/recipes/chatbot/apps/native/src/features/chatbot/components/markdown/code-block.tsx +86 -53
- package/recipes/chatbot/recipe.json +26 -92
- package/recipes/chatbot-supabase/apps/native/src/api-client/supabase/chatbot.ts +515 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/app/index.tsx +257 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-header-buttons.tsx +59 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-input-bar.tsx +485 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-markdown.tsx +575 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-message-bubble.tsx +223 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-settings-modal.tsx +161 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/image-preview-list.tsx +116 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/markdown/code-block.tsx +165 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/markdown/index.ts +10 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/markdown/table-renderer.tsx +129 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/message-error-boundary.tsx +78 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/message-list.tsx +170 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/model-selector.tsx +283 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/report-content-modal.tsx +188 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/suggested-messages.tsx +67 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/constants/models.ts +20 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/constants/report-reasons.ts +9 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-attachment-cache.ts +142 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-chat-config.ts +458 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-chat-handlers.ts +429 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-chatbot-settings.ts +89 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-conversation.ts +90 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-image-picker.ts +122 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-keyboard-coordinator.ts +161 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-smart-scroll-manager.ts +213 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/models/index.ts +86 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/models/models.ts +162 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/models/providers.ts +62 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/models/types.ts +40 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/services/file-uploader.ts +287 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/services/message-handler-service.ts +189 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/types/index.ts +70 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/utils/chat-telemetry.ts +91 -0
- package/recipes/chatbot-supabase/packages/backend/src/services/conversations.ts +243 -0
- package/recipes/chatbot-supabase/packages/backend/src/services/messages.ts +327 -0
- package/recipes/chatbot-supabase/packages/backend/supabase/functions/chat-stream/index.ts +347 -0
- package/recipes/chatbot-supabase/packages/backend/supabase/migrations/chatbot.sql +104 -0
- package/recipes/chatbot-supabase/recipe.json +79 -0
- package/recipes/chatbot-supabase@latest.zip +0 -0
- package/recipes/chatbot.zip +0 -0
- package/recipes/chatbot@latest.zip +0 -0
- package/recipes/image-analysis/packages/backend/convex/imageAnalysis/index.ts +2 -2
- package/recipes/image-analysis/packages/backend/convex/imageAnalysis.ts +0 -1
- package/recipes/image-analysis/recipe.json +15 -55
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/analysis-options-screen.tsx +304 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/camera.tsx +221 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/image-capture-screen.tsx +333 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/loading-screen.tsx +214 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/loading.tsx +191 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/results.tsx +137 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/trait-details.tsx +172 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/use-analysis-data.ts +160 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/use-results-screen.ts +151 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/achievement-badge.tsx +77 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/achievement-card.tsx +75 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/achievement-unlocked-modal.tsx +162 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/achievements-section.tsx +44 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/advice-list.tsx +42 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/circular-progress.tsx +233 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/content-card.tsx +38 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/error-state.tsx +42 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/index.ts +9 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/loading-state.tsx +26 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/profile-image.tsx +60 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/results-header.tsx +62 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/score-display.tsx +54 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/share-options-modal.tsx +110 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/traits-grid.tsx +74 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/config/analysis-config.ts +80 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/config/master-analysis-config.ts +157 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/hooks/index.ts +1 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/hooks/use-analysis.ts +38 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/hooks/use-image-analysis.ts +208 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/services/analysis-service.ts +262 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/services/share-service.ts +176 -0
- package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/services/trait-details-service.ts +289 -0
- package/recipes/image-analysis-supabase/packages/backend/src/services/image-analyses.ts +132 -0
- package/recipes/image-analysis-supabase/packages/backend/supabase/functions/analyze-image/index.ts +312 -0
- package/recipes/image-analysis-supabase/packages/backend/supabase/migrations/image_analysis.sql +42 -0
- package/recipes/image-analysis-supabase/recipe.json +57 -0
- package/recipes/image-analysis-supabase@latest.zip +0 -0
- package/recipes/image-analysis@latest.zip +0 -0
- package/recipes/image-generator/apps/native/src/features/image-generator/app/index.tsx +16 -2
- package/recipes/image-generator/apps/native/src/features/image-generator/components/image-model-selector.tsx +11 -5
- package/recipes/image-generator/apps/native/src/features/image-generator/hooks/use-image-generator.ts +11 -5
- package/recipes/image-generator/packages/backend/convex/imageGeneration/index.ts +2 -2
- package/recipes/image-generator/recipe.json +16 -39
- package/recipes/image-generator-supabase/apps/native/src/features/image-generator/app/_layout.tsx +26 -0
- package/recipes/image-generator-supabase/apps/native/src/features/image-generator/app/gallery.tsx +217 -0
- package/recipes/image-generator-supabase/apps/native/src/features/image-generator/app/index.tsx +251 -0
- package/recipes/image-generator-supabase/apps/native/src/features/image-generator/components/gallery-image.tsx +25 -0
- package/recipes/image-generator-supabase/apps/native/src/features/image-generator/components/image-detail-modal.tsx +215 -0
- package/recipes/image-generator-supabase/apps/native/src/features/image-generator/components/image-model-selector.tsx +216 -0
- package/recipes/image-generator-supabase/apps/native/src/features/image-generator/components/image-placeholder.tsx +26 -0
- package/recipes/image-generator-supabase/apps/native/src/features/image-generator/hooks/use-image-gallery.ts +71 -0
- package/recipes/image-generator-supabase/apps/native/src/features/image-generator/hooks/use-image-generator-settings.ts +152 -0
- package/recipes/image-generator-supabase/apps/native/src/features/image-generator/hooks/use-image-generator.ts +103 -0
- package/recipes/image-generator-supabase/apps/native/src/features/image-generator/models/models.ts +66 -0
- package/recipes/image-generator-supabase/apps/native/src/features/image-generator/services/image-gallery-service.ts +96 -0
- package/recipes/image-generator-supabase/apps/native/src/features/image-generator/services/image-save-service.ts +120 -0
- package/recipes/image-generator-supabase/packages/backend/supabase/functions/generate-image/index.ts +291 -0
- package/recipes/image-generator-supabase/packages/backend/supabase/migrations/image_generator.sql +71 -0
- package/recipes/image-generator-supabase/recipe.json +59 -0
- package/recipes/image-generator-supabase@latest.zip +0 -0
- package/recipes/image-generator@latest.zip +0 -0
- package/recipes/ios-widget/recipe.json +15 -24
- package/recipes/ios-widget@latest.zip +0 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/analytics/index.ts +9 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/components/onboarding-with-analytics.tsx +141 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/components/onboarding.tsx +173 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/config/onboarding-flow-config.ts +189 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/demo-one/app/index.tsx +42 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/demo-one/data.ts +32 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/app/index.tsx +43 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/interactive-onboarding.tsx +222 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/ai-tone-step.tsx +133 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/currency-step.tsx +165 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-ai-step.tsx +199 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-chatbot-step.tsx +154 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-manual-step.tsx +156 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-scan-step.tsx +158 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/main-reason-step.tsx +139 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/notification-step.tsx +129 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/overspend-step.tsx +138 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/personalizing-step.tsx +190 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/rating-step.tsx +98 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/reminder-step.tsx +181 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/safety-step.tsx +110 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/struggle-step.tsx +139 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/welcome-step.tsx +217 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/ui/onboarding-header.tsx +58 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/constants.ts +179 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/hooks/use-onboarding-analytics.ts +323 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/services/onboarding-analytics.ts +432 -0
- package/recipes/onboarding/recipe.json +15 -0
- package/recipes/onboarding@latest.zip +0 -0
- package/recipes/payments/recipe.json +28 -61
- package/recipes/payments-supabase/apps/native/src/features/payments/README.md +200 -0
- package/recipes/payments-supabase/apps/native/src/features/payments/app/local-paywall.tsx +194 -0
- package/recipes/payments-supabase/apps/native/src/features/payments/app/remote-paywall.tsx +79 -0
- package/recipes/payments-supabase/apps/native/src/features/payments/components/payment-initializer.tsx +95 -0
- package/recipes/payments-supabase/apps/native/src/features/payments/components/paywall-error-state.tsx +60 -0
- package/recipes/payments-supabase/apps/native/src/features/payments/components/paywall-local-mode.tsx +116 -0
- package/recipes/payments-supabase/apps/native/src/features/payments/components/paywall-product-card.tsx +133 -0
- package/recipes/payments-supabase/apps/native/src/features/payments/components/paywall-remote-mode.tsx +146 -0
- package/recipes/payments-supabase/apps/native/src/features/payments/hooks/use-entitlement.ts +63 -0
- package/recipes/payments-supabase/apps/native/src/features/payments/index.ts +8 -0
- package/recipes/payments-supabase/apps/native/src/features/payments/services/revenuecat-adapter.ts +407 -0
- package/recipes/payments-supabase/packages/backend/src/services/payments.ts +201 -0
- package/recipes/payments-supabase/packages/backend/supabase/migrations/payments.sql +35 -0
- package/recipes/payments-supabase/recipe.json +51 -0
- package/recipes/payments-supabase@latest.zip +0 -0
- package/recipes/payments@latest.zip +0 -0
- package/recipes/quiz/apps/native/src/features/quiz/index.tsx +1 -2
- package/recipes/quiz/recipe.json +6 -9
- package/recipes/quiz@latest.zip +0 -0
- package/recipes/tracker-app/apps/native/src/features/tracker-app/app/index.tsx +1 -2
- package/recipes/tracker-app/recipe.json +7 -10
- package/recipes/tracker-app@latest.zip +0 -0
- package/recipes/voice-bot/recipe.json +8 -68
- package/recipes/voice-bot.zip +0 -0
- package/recipes/voice-bot@latest.zip +0 -0
- package/recipes/wake-word/recipe.json +10 -9
- package/recipes/wake-word.zip +0 -0
- package/recipes/wake-word@latest.zip +0 -0
- package/recipes/charts/apps/native/src/app/(root)/(protected)/charts/index.tsx +0 -3
- package/recipes/chatbot/packages/backend/convex/lib/rateLimit.ts +0 -100
- package/recipes/chatbot/packages/backend/convex/lib/telemetry.ts +0 -29
- package/recipes/chatbot/packages/backend/convex/ragKnowledge.ts +0 -0
- package/recipes/image-analysis/apps/native/assets/features/image-analyzer/front.jpg +0 -0
- package/recipes/image-analysis/apps/native/assets/features/image-analyzer/side.jpg +0 -0
- package/recipes/image-analysis/apps/native/assets/features/image-analyzer/threeQuarter.jpg +0 -0
- package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/_layout.tsx +0 -5
- package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/analysis-options.tsx +0 -50
- package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/camera.tsx +0 -2
- package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/index.tsx +0 -50
- package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/loading.tsx +0 -50
- package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/results.tsx +0 -2
- package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/trait-details.tsx +0 -3
- package/recipes/image-analysis/packages/backend/convex/imageAnalysisFunctions.ts +0 -325
- package/recipes/image-analysis/packages/backend/convex/lib/ai/imageAnalysisAdapter.ts +0 -200
- package/recipes/payments/apps/native/src/app/(root)/(protected)/paywall/index.tsx +0 -74
- package/recipes/payments/apps/native/src/app/(root)/(protected)/paywall/local.tsx +0 -25
- package/recipes/payments/apps/native/src/app/(root)/(protected)/paywall/remote.tsx +0 -23
- package/recipes/quiz/apps/native/src/app/(root)/(protected)/quiz/index.tsx +0 -47
- package/recipes/tracker-app/apps/native/src/app/(root)/(protected)/tracker-app/index.tsx +0 -1
- package/recipes/voice-bot/apps/native/src/app/(root)/(protected)/voice-bot/index.tsx +0 -27
- package/recipes/voice-bot/packages/backend/convex/router.ts +0 -81
- /package/recipes/{chatbot/apps/native/src/app/(root)/(protected) → chatbot-supabase/apps/native/src/app}/chatbot/index.tsx +0 -0
- /package/recipes/{image-generator/apps/native/src/app/(root)/(protected) → image-generator-supabase/apps/native/src/app}/image-generator/gallery.tsx +0 -0
- /package/recipes/{image-generator/apps/native/src/app/(root)/(protected) → image-generator-supabase/apps/native/src/app}/image-generator/index.tsx +0 -0
|
@@ -1,325 +0,0 @@
|
|
|
1
|
-
import { getAuthUserId } from '@convex-dev/auth/server';
|
|
2
|
-
import { v } from 'convex/values';
|
|
3
|
-
|
|
4
|
-
import { internal } from './_generated/api';
|
|
5
|
-
import type { Id } from './_generated/dataModel';
|
|
6
|
-
import {
|
|
7
|
-
action,
|
|
8
|
-
internalAction,
|
|
9
|
-
internalMutation,
|
|
10
|
-
internalQuery,
|
|
11
|
-
query,
|
|
12
|
-
} from './_generated/server';
|
|
13
|
-
import { generateImageAnalysis } from './lib/ai/imageAnalysisAdapter';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Public action to start image analysis
|
|
17
|
-
*/
|
|
18
|
-
export const analyzeImages = action({
|
|
19
|
-
args: {
|
|
20
|
-
imagesAsBase64: v.array(v.string()),
|
|
21
|
-
preferences: v.object({
|
|
22
|
-
goal: v.string(),
|
|
23
|
-
feedbackStyle: v.string(),
|
|
24
|
-
}),
|
|
25
|
-
analysisConfigId: v.optional(v.string()), // Optional for backward compatibility
|
|
26
|
-
},
|
|
27
|
-
handler: async (ctx, args): Promise<Id<'imageAnalyses'>> => {
|
|
28
|
-
const userId = await getAuthUserId(ctx);
|
|
29
|
-
if (!userId) {
|
|
30
|
-
throw new Error('Not authenticated');
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Use default config if not provided (for backward compatibility)
|
|
34
|
-
const configId = args.analysisConfigId || 'face_analysis';
|
|
35
|
-
|
|
36
|
-
// Store images in Convex storage
|
|
37
|
-
const imageStorageIds: Id<'_storage'>[] = [];
|
|
38
|
-
for (const base64Image of args.imagesAsBase64) {
|
|
39
|
-
const uint8Array = new Uint8Array(Buffer.from(base64Image, 'base64'));
|
|
40
|
-
const blob = new Blob([uint8Array], { type: 'image/jpeg' });
|
|
41
|
-
const storageId = await ctx.storage.store(blob);
|
|
42
|
-
imageStorageIds.push(storageId);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Create analysis record
|
|
46
|
-
const analysisId = await ctx.runMutation(
|
|
47
|
-
internal.imageAnalysisFunctions.createAnalysisRecord,
|
|
48
|
-
{
|
|
49
|
-
userId,
|
|
50
|
-
imageStorageIds,
|
|
51
|
-
analysisConfigId: configId,
|
|
52
|
-
preferences: args.preferences,
|
|
53
|
-
},
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
// Schedule background AI analysis
|
|
57
|
-
await ctx.scheduler.runAfter(
|
|
58
|
-
0,
|
|
59
|
-
internal.imageAnalysisFunctions.performAiAnalysis,
|
|
60
|
-
{
|
|
61
|
-
analysisId,
|
|
62
|
-
},
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
return analysisId;
|
|
66
|
-
},
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Internal mutation to create analysis record
|
|
71
|
-
*/
|
|
72
|
-
export const createAnalysisRecord = internalMutation({
|
|
73
|
-
args: {
|
|
74
|
-
userId: v.id('users'),
|
|
75
|
-
imageStorageIds: v.array(v.id('_storage')),
|
|
76
|
-
analysisConfigId: v.optional(v.string()),
|
|
77
|
-
preferences: v.object({
|
|
78
|
-
goal: v.string(),
|
|
79
|
-
feedbackStyle: v.string(),
|
|
80
|
-
}),
|
|
81
|
-
},
|
|
82
|
-
handler: async (ctx, args) => {
|
|
83
|
-
return await ctx.db.insert('imageAnalyses', {
|
|
84
|
-
userId: args.userId,
|
|
85
|
-
status: 'processing',
|
|
86
|
-
imageStorageIds: args.imageStorageIds,
|
|
87
|
-
analysisConfigId: args.analysisConfigId,
|
|
88
|
-
preferences: args.preferences,
|
|
89
|
-
});
|
|
90
|
-
},
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Internal action to perform AI analysis
|
|
95
|
-
*/
|
|
96
|
-
export const performAiAnalysis = internalAction({
|
|
97
|
-
args: {
|
|
98
|
-
analysisId: v.id('imageAnalyses'),
|
|
99
|
-
},
|
|
100
|
-
handler: async (ctx, args) => {
|
|
101
|
-
try {
|
|
102
|
-
// Get analysis record
|
|
103
|
-
const analysis = await ctx.runQuery(
|
|
104
|
-
internal.imageAnalysisFunctions.getAnalysisRecord,
|
|
105
|
-
{
|
|
106
|
-
analysisId: args.analysisId,
|
|
107
|
-
},
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
if (!analysis) {
|
|
111
|
-
throw new Error('Analysis record not found');
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Get analysis configuration (use default if not specified for backward compatibility)
|
|
115
|
-
const configId = analysis.analysisConfigId || 'face_analysis';
|
|
116
|
-
|
|
117
|
-
// Get image URLs from storage - parallelize for better performance
|
|
118
|
-
const imageUrls = await Promise.all(
|
|
119
|
-
analysis.imageStorageIds.map(async (storageId) => {
|
|
120
|
-
const url = await ctx.storage.getUrl(storageId);
|
|
121
|
-
return url;
|
|
122
|
-
}),
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
// Convert image URLs to base64 for AI analysis - parallelize with timeout
|
|
126
|
-
const FETCH_TIMEOUT_MS = 30000; // 30 seconds per image
|
|
127
|
-
const imagesAsBase64 = await Promise.all(
|
|
128
|
-
imageUrls
|
|
129
|
-
.filter((url): url is string => url !== null)
|
|
130
|
-
.map(async (url) => {
|
|
131
|
-
const controller = new AbortController();
|
|
132
|
-
const timeoutId = setTimeout(
|
|
133
|
-
() => controller.abort(),
|
|
134
|
-
FETCH_TIMEOUT_MS,
|
|
135
|
-
);
|
|
136
|
-
try {
|
|
137
|
-
const response = await fetch(url, { signal: controller.signal });
|
|
138
|
-
clearTimeout(timeoutId);
|
|
139
|
-
if (!response.ok) {
|
|
140
|
-
throw new Error(
|
|
141
|
-
`Failed to fetch image: ${response.statusText}`,
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
145
|
-
return Buffer.from(arrayBuffer).toString('base64');
|
|
146
|
-
} catch (error) {
|
|
147
|
-
clearTimeout(timeoutId);
|
|
148
|
-
if (error instanceof Error && error.name === 'AbortError') {
|
|
149
|
-
throw new Error(
|
|
150
|
-
`Image fetch timed out after ${FETCH_TIMEOUT_MS}ms`,
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
throw error;
|
|
154
|
-
}
|
|
155
|
-
}),
|
|
156
|
-
);
|
|
157
|
-
|
|
158
|
-
// Validate we have at least one image
|
|
159
|
-
if (imagesAsBase64.length === 0) {
|
|
160
|
-
throw new Error('No valid images found for analysis');
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Normalize preferences for backward compatibility
|
|
164
|
-
const normalizedPreferences = {
|
|
165
|
-
goal:
|
|
166
|
-
'goal' in analysis.preferences
|
|
167
|
-
? analysis.preferences.goal
|
|
168
|
-
: (analysis.preferences as any).analysisGoal,
|
|
169
|
-
feedbackStyle: analysis.preferences.feedbackStyle,
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
// Perform AI analysis
|
|
173
|
-
const results = await generateImageAnalysis(
|
|
174
|
-
imagesAsBase64,
|
|
175
|
-
normalizedPreferences,
|
|
176
|
-
configId,
|
|
177
|
-
);
|
|
178
|
-
|
|
179
|
-
// Update analysis record with results
|
|
180
|
-
await ctx.runMutation(
|
|
181
|
-
internal.imageAnalysisFunctions.updateAnalysisResults,
|
|
182
|
-
{
|
|
183
|
-
analysisId: args.analysisId,
|
|
184
|
-
results,
|
|
185
|
-
status: 'completed',
|
|
186
|
-
},
|
|
187
|
-
);
|
|
188
|
-
} catch (error) {
|
|
189
|
-
console.error('AI analysis failed:', error);
|
|
190
|
-
|
|
191
|
-
// Update analysis record with error
|
|
192
|
-
await ctx.runMutation(
|
|
193
|
-
internal.imageAnalysisFunctions.updateAnalysisResults,
|
|
194
|
-
{
|
|
195
|
-
analysisId: args.analysisId,
|
|
196
|
-
status: 'failed',
|
|
197
|
-
errorMessage:
|
|
198
|
-
error instanceof Error ? error.message : 'Unknown error',
|
|
199
|
-
},
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
},
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Internal query to get analysis record
|
|
207
|
-
*/
|
|
208
|
-
export const getAnalysisRecord = internalQuery({
|
|
209
|
-
args: {
|
|
210
|
-
analysisId: v.id('imageAnalyses'),
|
|
211
|
-
},
|
|
212
|
-
handler: async (ctx, args) => {
|
|
213
|
-
return await ctx.db.get(args.analysisId);
|
|
214
|
-
},
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Internal mutation to update analysis results
|
|
219
|
-
*/
|
|
220
|
-
export const updateAnalysisResults = internalMutation({
|
|
221
|
-
args: {
|
|
222
|
-
analysisId: v.id('imageAnalyses'),
|
|
223
|
-
results: v.optional(v.any()),
|
|
224
|
-
status: v.union(v.literal('completed'), v.literal('failed')),
|
|
225
|
-
errorMessage: v.optional(v.string()),
|
|
226
|
-
},
|
|
227
|
-
handler: async (ctx, args) => {
|
|
228
|
-
await ctx.db.patch(args.analysisId, {
|
|
229
|
-
status: args.status,
|
|
230
|
-
results: args.results,
|
|
231
|
-
errorMessage: args.errorMessage,
|
|
232
|
-
});
|
|
233
|
-
},
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Public query to get analysis by ID
|
|
238
|
-
*/
|
|
239
|
-
export const getAnalysisById = query({
|
|
240
|
-
args: {
|
|
241
|
-
analysisId: v.id('imageAnalyses'),
|
|
242
|
-
},
|
|
243
|
-
handler: async (ctx, args) => {
|
|
244
|
-
const userId = await getAuthUserId(ctx);
|
|
245
|
-
if (!userId) {
|
|
246
|
-
throw new Error('Not authenticated');
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
const analysis = await ctx.db.get(args.analysisId);
|
|
250
|
-
if (!analysis) {
|
|
251
|
-
return null;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// Ensure user owns this analysis
|
|
255
|
-
if (analysis.userId !== userId) {
|
|
256
|
-
throw new Error('Unauthorized');
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
return analysis;
|
|
260
|
-
},
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Public query to get full analysis with image URLs
|
|
265
|
-
*/
|
|
266
|
-
export const getImageAnalysis = query({
|
|
267
|
-
args: {
|
|
268
|
-
analysisId: v.id('imageAnalyses'),
|
|
269
|
-
},
|
|
270
|
-
handler: async (ctx, args) => {
|
|
271
|
-
const userId = await getAuthUserId(ctx);
|
|
272
|
-
if (!userId) {
|
|
273
|
-
throw new Error('Not authenticated');
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
const analysis = await ctx.db.get(args.analysisId);
|
|
277
|
-
if (!analysis) {
|
|
278
|
-
return null;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Ensure user owns this analysis
|
|
282
|
-
if (analysis.userId !== userId) {
|
|
283
|
-
throw new Error('Unauthorized');
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// Get image URLs - parallelize for better performance
|
|
287
|
-
const imageUrls = await Promise.all(
|
|
288
|
-
analysis.imageStorageIds.map(async (storageId) => {
|
|
289
|
-
const url = await ctx.storage.getUrl(storageId);
|
|
290
|
-
return url;
|
|
291
|
-
}),
|
|
292
|
-
);
|
|
293
|
-
|
|
294
|
-
return {
|
|
295
|
-
...analysis,
|
|
296
|
-
imageUrls: imageUrls.filter((url): url is string => url !== null),
|
|
297
|
-
};
|
|
298
|
-
},
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* Public query to get user's analysis history with pagination
|
|
303
|
-
*/
|
|
304
|
-
export const getUserImageAnalyses = query({
|
|
305
|
-
args: {
|
|
306
|
-
paginationOpts: v.optional(
|
|
307
|
-
v.object({
|
|
308
|
-
numItems: v.number(),
|
|
309
|
-
cursor: v.union(v.string(), v.null()),
|
|
310
|
-
}),
|
|
311
|
-
),
|
|
312
|
-
},
|
|
313
|
-
handler: async (ctx, args) => {
|
|
314
|
-
const userId = await getAuthUserId(ctx);
|
|
315
|
-
if (!userId) {
|
|
316
|
-
throw new Error('Not authenticated');
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
return await ctx.db
|
|
320
|
-
.query('imageAnalyses')
|
|
321
|
-
.withIndex('by_userId', (q) => q.eq('userId', userId))
|
|
322
|
-
.order('desc')
|
|
323
|
-
.paginate(args.paginationOpts ?? { numItems: 50, cursor: null });
|
|
324
|
-
},
|
|
325
|
-
});
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
'use node';
|
|
2
|
-
|
|
3
|
-
import { createGoogleGenerativeAI } from '@ai-sdk/google';
|
|
4
|
-
import { createOpenAI } from '@ai-sdk/openai';
|
|
5
|
-
import { generateObject } from 'ai';
|
|
6
|
-
|
|
7
|
-
import { getAnalysisConfig } from './analysisConfigs';
|
|
8
|
-
import { type AIProvider, getAIConfig } from './config';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Generic analysis result type
|
|
12
|
-
*/
|
|
13
|
-
export type GenericAnalysisResult = {
|
|
14
|
-
overallScore: number;
|
|
15
|
-
overallMessage: string;
|
|
16
|
-
confidence: number;
|
|
17
|
-
traitScores: Record<
|
|
18
|
-
string,
|
|
19
|
-
{
|
|
20
|
-
score: number;
|
|
21
|
-
feedback: string;
|
|
22
|
-
strengths: string[];
|
|
23
|
-
improvements: string[];
|
|
24
|
-
}
|
|
25
|
-
>;
|
|
26
|
-
recommendations: string[];
|
|
27
|
-
analysisType: string;
|
|
28
|
-
processingTime: number;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Truncate an oversized base64 string while preserving a valid encoding.
|
|
33
|
-
*
|
|
34
|
-
* Base64 strings must have a length that is a multiple of 4 and must end with
|
|
35
|
-
* optional padding characters ("="). A naive substring can break the encoding
|
|
36
|
-
* and lead to downstream "unsupported image" errors from the OpenAI API. This
|
|
37
|
-
* helper trims the string **only** when it exceeds the chosen limit and always
|
|
38
|
-
* rounds the length down to the nearest multiple of 4 to keep the data valid.
|
|
39
|
-
*
|
|
40
|
-
* NOTE: The client already compresses/resize images before upload, so the limit
|
|
41
|
-
* here is intentionally generous (4 MB of base64 text ≈ 3 MB binary). This
|
|
42
|
-
* should fit within OpenAI's 30 MB image cap while safeguarding Convex memory.
|
|
43
|
-
*/
|
|
44
|
-
function optimizeImageForMemory(base64: string): string {
|
|
45
|
-
const MAX_CHARS = 4 * 1024 * 1024; // 4 MB worth of base64 characters
|
|
46
|
-
|
|
47
|
-
if (base64.length <= MAX_CHARS) {
|
|
48
|
-
return base64;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
console.log(
|
|
52
|
-
`[Memory Optimization] Truncating image from ${base64.length} to ${MAX_CHARS} chars (safe multiple-of-4)`,
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
// Round down to nearest multiple of 4 to preserve base64 integrity
|
|
56
|
-
const safeLength = MAX_CHARS - (MAX_CHARS % 4);
|
|
57
|
-
const truncated = base64.substring(0, safeLength);
|
|
58
|
-
|
|
59
|
-
// Ensure proper padding – if the truncated string ends with partial padding
|
|
60
|
-
// remove it and re-append correct padding.
|
|
61
|
-
const withoutPartialPad = truncated.replace(/=+$/, '');
|
|
62
|
-
const padNeeded = (4 - (withoutPartialPad.length % 4)) % 4;
|
|
63
|
-
return withoutPartialPad + '='.repeat(padNeeded);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Generate structured analysis using AI SDK (generic for any analysis type)
|
|
68
|
-
*/
|
|
69
|
-
export async function generateImageAnalysis(
|
|
70
|
-
imagesAsBase64: string[],
|
|
71
|
-
preferences: {
|
|
72
|
-
goal: string;
|
|
73
|
-
feedbackStyle: string;
|
|
74
|
-
},
|
|
75
|
-
analysisConfigId = 'face_analysis',
|
|
76
|
-
): Promise<GenericAnalysisResult> {
|
|
77
|
-
const config = getAIConfig();
|
|
78
|
-
const analysisConfig = getAnalysisConfig(analysisConfigId);
|
|
79
|
-
const startTime = Date.now();
|
|
80
|
-
|
|
81
|
-
// Memory optimization: Limit to max 3 images and optimize them
|
|
82
|
-
const maxImages = 3;
|
|
83
|
-
const optimizedImages = imagesAsBase64
|
|
84
|
-
.slice(0, maxImages)
|
|
85
|
-
.map(optimizeImageForMemory);
|
|
86
|
-
|
|
87
|
-
console.log(
|
|
88
|
-
`[Memory Optimization] Processing ${optimizedImages.length} images (max ${maxImages})`,
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
// Create the appropriate AI provider
|
|
92
|
-
const provider = createAIProvider(config.provider);
|
|
93
|
-
const model = provider(config.model);
|
|
94
|
-
|
|
95
|
-
// Build a simpler, shorter prompt to reduce memory usage
|
|
96
|
-
const prompt = buildOptimizedPrompt(
|
|
97
|
-
analysisConfig,
|
|
98
|
-
preferences,
|
|
99
|
-
optimizedImages.length,
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
// Convert base64 images to the format expected by AI SDK
|
|
103
|
-
const imageMessages = optimizedImages.map((base64) => ({
|
|
104
|
-
type: 'image' as const,
|
|
105
|
-
image: `data:image/jpeg;base64,${base64}`,
|
|
106
|
-
}));
|
|
107
|
-
|
|
108
|
-
try {
|
|
109
|
-
// Generate structured output using AI SDK with reduced complexity
|
|
110
|
-
const { object } = await generateObject({
|
|
111
|
-
model,
|
|
112
|
-
schema: analysisConfig.schema,
|
|
113
|
-
messages: [
|
|
114
|
-
{
|
|
115
|
-
role: 'user',
|
|
116
|
-
content: [{ type: 'text', text: prompt }, ...imageMessages],
|
|
117
|
-
},
|
|
118
|
-
],
|
|
119
|
-
temperature: config.temperature,
|
|
120
|
-
maxTokens: Math.min(config.maxTokens, 2000), // Reduce max tokens to save memory
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
// Add processing metadata
|
|
124
|
-
const processingTime = Date.now() - startTime;
|
|
125
|
-
return {
|
|
126
|
-
...object,
|
|
127
|
-
processingTime,
|
|
128
|
-
analysisType: `${analysisConfigId}_${preferences.goal}_${preferences.feedbackStyle}`,
|
|
129
|
-
} as GenericAnalysisResult;
|
|
130
|
-
} catch (error) {
|
|
131
|
-
console.error('Error generating analysis:', error);
|
|
132
|
-
throw new Error(
|
|
133
|
-
`Failed to generate analysis: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Build optimized, shorter prompt to reduce memory usage
|
|
140
|
-
*/
|
|
141
|
-
function buildOptimizedPrompt(
|
|
142
|
-
analysisConfig: any,
|
|
143
|
-
preferences: { goal: string; feedbackStyle: string },
|
|
144
|
-
imageCount: number,
|
|
145
|
-
): string {
|
|
146
|
-
const traitNames = Object.keys(analysisConfig.traits).slice(0, 6); // Limit traits to reduce complexity
|
|
147
|
-
|
|
148
|
-
return `Analyze the ${imageCount} image(s) for ${analysisConfig.name.toLowerCase()}.
|
|
149
|
-
|
|
150
|
-
Goal: ${preferences.goal}
|
|
151
|
-
Style: ${preferences.feedbackStyle}
|
|
152
|
-
|
|
153
|
-
Provide scores (0-100) and feedback for: ${traitNames.join(', ')}.
|
|
154
|
-
|
|
155
|
-
Include:
|
|
156
|
-
- Overall score and message
|
|
157
|
-
- Individual trait scores with feedback
|
|
158
|
-
- 2-3 recommendations
|
|
159
|
-
- Confidence level (0-1)
|
|
160
|
-
|
|
161
|
-
Be concise and ${preferences.feedbackStyle}.`;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Legacy function for backward compatibility (face analysis)
|
|
166
|
-
* @deprecated Use generateImageAnalysis instead
|
|
167
|
-
*/
|
|
168
|
-
export async function generateFacialAnalysis(
|
|
169
|
-
imagesAsBase64: string[],
|
|
170
|
-
preferences: {
|
|
171
|
-
goal: string;
|
|
172
|
-
feedbackStyle: string;
|
|
173
|
-
},
|
|
174
|
-
): Promise<GenericAnalysisResult> {
|
|
175
|
-
return generateImageAnalysis(imagesAsBase64, preferences, 'face_analysis');
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Create AI provider based on configuration
|
|
180
|
-
*/
|
|
181
|
-
function createAIProvider(provider: AIProvider) {
|
|
182
|
-
switch (provider) {
|
|
183
|
-
case 'openai': {
|
|
184
|
-
const apiKey = process.env.OPENAI_API_KEY;
|
|
185
|
-
if (!apiKey) {
|
|
186
|
-
throw new Error('OPENAI_API_KEY environment variable is required');
|
|
187
|
-
}
|
|
188
|
-
return createOpenAI({ apiKey });
|
|
189
|
-
}
|
|
190
|
-
case 'gemini': {
|
|
191
|
-
const apiKey = process.env.GEMINI_API_KEY;
|
|
192
|
-
if (!apiKey) {
|
|
193
|
-
throw new Error('GEMINI_API_KEY environment variable is required');
|
|
194
|
-
}
|
|
195
|
-
return createGoogleGenerativeAI({ apiKey });
|
|
196
|
-
}
|
|
197
|
-
default:
|
|
198
|
-
throw new Error(`Unsupported AI provider: ${provider}`);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { router, Stack } from 'expo-router';
|
|
2
|
-
import React, { useMemo } from 'react';
|
|
3
|
-
import { ScrollView, View } from 'react-native';
|
|
4
|
-
|
|
5
|
-
import { FeatureButton, FocusAwareStatusBar } from '@/components/ui';
|
|
6
|
-
import { translate } from '@/lib';
|
|
7
|
-
import { useThemeConfig } from '@/lib/use-theme-config';
|
|
8
|
-
|
|
9
|
-
export default function PaywallSelection() {
|
|
10
|
-
const theme = useThemeConfig();
|
|
11
|
-
const optionsTitle = translate('paywall.options_title');
|
|
12
|
-
const screenOptions = useMemo(
|
|
13
|
-
() => ({
|
|
14
|
-
title: optionsTitle,
|
|
15
|
-
headerShown: true,
|
|
16
|
-
headerBackButtonDisplayMode: 'generic' as const,
|
|
17
|
-
}),
|
|
18
|
-
[optionsTitle],
|
|
19
|
-
);
|
|
20
|
-
|
|
21
|
-
const paywallOptions = [
|
|
22
|
-
{
|
|
23
|
-
id: 'remote',
|
|
24
|
-
title: translate('paywall.remote_title'),
|
|
25
|
-
icon: '💳',
|
|
26
|
-
color: '#FBBF24', // Amber
|
|
27
|
-
description: translate('paywall.remote_description'),
|
|
28
|
-
route: '/paywall/remote',
|
|
29
|
-
testID: 'remote-paywall-option',
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
id: 'local',
|
|
33
|
-
title: translate('paywall.local_title'),
|
|
34
|
-
icon: '🛒',
|
|
35
|
-
color: '#10B981', // Emerald
|
|
36
|
-
description: translate('paywall.local_description'),
|
|
37
|
-
route: '/paywall/local',
|
|
38
|
-
testID: 'local-paywall-option',
|
|
39
|
-
},
|
|
40
|
-
];
|
|
41
|
-
|
|
42
|
-
const handleOptionPress = (route: string) => {
|
|
43
|
-
router.push(route as any);
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
return (
|
|
47
|
-
<>
|
|
48
|
-
<FocusAwareStatusBar />
|
|
49
|
-
<Stack.Screen options={screenOptions} />
|
|
50
|
-
<ScrollView
|
|
51
|
-
style={{ backgroundColor: theme.colors.background }}
|
|
52
|
-
className="flex-1"
|
|
53
|
-
showsVerticalScrollIndicator={false}
|
|
54
|
-
>
|
|
55
|
-
<View className="px-6 py-8">
|
|
56
|
-
<View className="-mx-2 flex-row flex-wrap">
|
|
57
|
-
{paywallOptions.map((option) => (
|
|
58
|
-
<View key={option.id} className="w-1/2 px-2 pb-4">
|
|
59
|
-
<FeatureButton
|
|
60
|
-
title={option.title}
|
|
61
|
-
icon={option.icon}
|
|
62
|
-
color={option.color}
|
|
63
|
-
description={option.description}
|
|
64
|
-
testID={option.testID}
|
|
65
|
-
onPress={() => handleOptionPress(option.route)}
|
|
66
|
-
/>
|
|
67
|
-
</View>
|
|
68
|
-
))}
|
|
69
|
-
</View>
|
|
70
|
-
</View>
|
|
71
|
-
</ScrollView>
|
|
72
|
-
</>
|
|
73
|
-
);
|
|
74
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { Stack } from 'expo-router';
|
|
2
|
-
import React, { useMemo } from 'react';
|
|
3
|
-
|
|
4
|
-
import LocalPaywall from '@/features/payments/app/local-paywall';
|
|
5
|
-
import { RevenueCatAdapter } from '@/features/payments/services/revenuecat-adapter';
|
|
6
|
-
import { translate } from '@/lib';
|
|
7
|
-
|
|
8
|
-
export default function LocalPaywallScreen() {
|
|
9
|
-
const paymentService = useMemo(() => new RevenueCatAdapter(), []);
|
|
10
|
-
const localTitle = translate('paywall.local_title');
|
|
11
|
-
const screenOptions = useMemo(
|
|
12
|
-
() => ({
|
|
13
|
-
title: localTitle,
|
|
14
|
-
headerShown: false,
|
|
15
|
-
}),
|
|
16
|
-
[localTitle],
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
return (
|
|
20
|
-
<>
|
|
21
|
-
<Stack.Screen options={screenOptions} />
|
|
22
|
-
<LocalPaywall paymentService={paymentService} />
|
|
23
|
-
</>
|
|
24
|
-
);
|
|
25
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { Stack } from 'expo-router';
|
|
2
|
-
import React, { useMemo } from 'react';
|
|
3
|
-
|
|
4
|
-
import RemotePaywall from '@/features/payments/app/remote-paywall';
|
|
5
|
-
import { translate } from '@/lib';
|
|
6
|
-
|
|
7
|
-
export default function RemotePaywallScreen() {
|
|
8
|
-
const remoteTitle = translate('paywall.remote_title');
|
|
9
|
-
const screenOptions = useMemo(
|
|
10
|
-
() => ({
|
|
11
|
-
title: remoteTitle,
|
|
12
|
-
headerShown: true,
|
|
13
|
-
}),
|
|
14
|
-
[remoteTitle],
|
|
15
|
-
);
|
|
16
|
-
|
|
17
|
-
return (
|
|
18
|
-
<>
|
|
19
|
-
<Stack.Screen options={screenOptions} />
|
|
20
|
-
<RemotePaywall />
|
|
21
|
-
</>
|
|
22
|
-
);
|
|
23
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import Quiz from '@/features/quiz';
|
|
2
|
-
import { QuestionMode } from '@/features/quiz/config';
|
|
3
|
-
|
|
4
|
-
const questions = [
|
|
5
|
-
{
|
|
6
|
-
text: 'What is your favorite color?',
|
|
7
|
-
mode: QuestionMode.AutoAdvance,
|
|
8
|
-
options: [
|
|
9
|
-
{ id: 'red', text: 'Red', note: 'Red is the color of passion.' },
|
|
10
|
-
{ id: 'blue', text: 'Blue', note: 'Blue is the color of calm.' },
|
|
11
|
-
{ id: 'green', text: 'Green', note: 'Green is the color of nature.' },
|
|
12
|
-
],
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
text: 'What is your favorite animal?',
|
|
16
|
-
mode: QuestionMode.ManualAdvance,
|
|
17
|
-
options: [
|
|
18
|
-
{ id: 'dog', text: 'Dog', note: 'Dogs are loyal.' },
|
|
19
|
-
{ id: 'cat', text: 'Cat', note: 'Cats are independent.' },
|
|
20
|
-
{ id: 'bird', text: 'Bird', note: 'Birds can fly.' },
|
|
21
|
-
],
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
text: 'What is your favorite animal?',
|
|
25
|
-
mode: QuestionMode.ManualAdvance,
|
|
26
|
-
options: [
|
|
27
|
-
{ id: 'dog', text: 'Dog', note: 'Dogs are loyal.' },
|
|
28
|
-
{ id: 'cat', text: 'Cat', note: 'Cats are independent.' },
|
|
29
|
-
{ id: 'bird', text: 'Bird', note: 'Birds can fly.' },
|
|
30
|
-
],
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
text: 'What is your favorite animal?',
|
|
34
|
-
mode: QuestionMode.ManualAdvance,
|
|
35
|
-
options: [
|
|
36
|
-
{ id: 'dog', text: 'Dog', note: 'Dogs are loyal.' },
|
|
37
|
-
{ id: 'cat', text: 'Cat', note: 'Cats are independent.' },
|
|
38
|
-
{ id: 'bird', text: 'Bird', note: 'Birds can fly.' },
|
|
39
|
-
],
|
|
40
|
-
},
|
|
41
|
-
];
|
|
42
|
-
|
|
43
|
-
const QuizScreen = () => {
|
|
44
|
-
return <Quiz questions={questions} />;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
export default QuizScreen;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from '@/features/tracker-app/app';
|