vibefast-cli 1.1.3 → 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/__tests__/recipes.test.js +25 -3
- package/dist/__tests__/recipes.test.js.map +1 -1
- package/dist/commands/add.d.ts +1 -1
- package/dist/commands/add.d.ts.map +1 -1
- package/dist/commands/add.js +547 -543
- 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/{imageAnalysisFunctions.ts → imageAnalysis.ts} +5 -5
- 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/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
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
-- Chatbot Feature Migration
|
|
2
|
+
-- Creates tables for conversations and messages
|
|
3
|
+
-- Run this migration when adding the chatbot feature via CLI
|
|
4
|
+
|
|
5
|
+
-- ============================================================================
|
|
6
|
+
-- CONVERSATIONS TABLE
|
|
7
|
+
-- Stores chat conversations for each user
|
|
8
|
+
-- ============================================================================
|
|
9
|
+
CREATE TABLE IF NOT EXISTS conversations (
|
|
10
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
11
|
+
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
12
|
+
name TEXT,
|
|
13
|
+
created_at TIMESTAMPTZ DEFAULT now()
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
-- ============================================================================
|
|
17
|
+
-- MESSAGES TABLE
|
|
18
|
+
-- Stores individual messages within conversations
|
|
19
|
+
-- ============================================================================
|
|
20
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
21
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
22
|
+
conversation_id UUID NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
|
|
23
|
+
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
24
|
+
author_type TEXT NOT NULL CHECK (author_type IN ('user', 'bot')),
|
|
25
|
+
text TEXT,
|
|
26
|
+
ai_provider TEXT,
|
|
27
|
+
ai_model TEXT,
|
|
28
|
+
tool_calls JSONB,
|
|
29
|
+
metadata JSONB,
|
|
30
|
+
attachments JSONB,
|
|
31
|
+
created_at TIMESTAMPTZ DEFAULT now()
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
-- ============================================================================
|
|
35
|
+
-- INDEXES
|
|
36
|
+
-- ============================================================================
|
|
37
|
+
CREATE INDEX IF NOT EXISTS idx_conversations_user_id ON conversations(user_id);
|
|
38
|
+
CREATE INDEX IF NOT EXISTS idx_messages_conversation_id ON messages(conversation_id);
|
|
39
|
+
CREATE INDEX IF NOT EXISTS idx_messages_user_id ON messages(user_id);
|
|
40
|
+
CREATE INDEX IF NOT EXISTS idx_messages_created_at ON messages(created_at);
|
|
41
|
+
|
|
42
|
+
-- ============================================================================
|
|
43
|
+
-- RLS POLICIES - CONVERSATIONS
|
|
44
|
+
-- ============================================================================
|
|
45
|
+
ALTER TABLE conversations ENABLE ROW LEVEL SECURITY;
|
|
46
|
+
|
|
47
|
+
CREATE POLICY "conversations_select_own" ON conversations
|
|
48
|
+
FOR SELECT USING (auth.uid() = user_id);
|
|
49
|
+
|
|
50
|
+
CREATE POLICY "conversations_insert_own" ON conversations
|
|
51
|
+
FOR INSERT WITH CHECK (auth.uid() = user_id);
|
|
52
|
+
|
|
53
|
+
CREATE POLICY "conversations_update_own" ON conversations
|
|
54
|
+
FOR UPDATE USING (auth.uid() = user_id);
|
|
55
|
+
|
|
56
|
+
CREATE POLICY "conversations_delete_own" ON conversations
|
|
57
|
+
FOR DELETE USING (auth.uid() = user_id);
|
|
58
|
+
|
|
59
|
+
-- ============================================================================
|
|
60
|
+
-- RLS POLICIES - MESSAGES
|
|
61
|
+
-- ============================================================================
|
|
62
|
+
ALTER TABLE messages ENABLE ROW LEVEL SECURITY;
|
|
63
|
+
|
|
64
|
+
CREATE POLICY "messages_select_own" ON messages
|
|
65
|
+
FOR SELECT USING (auth.uid() = user_id);
|
|
66
|
+
|
|
67
|
+
CREATE POLICY "messages_insert_own" ON messages
|
|
68
|
+
FOR INSERT WITH CHECK (auth.uid() = user_id);
|
|
69
|
+
|
|
70
|
+
CREATE POLICY "messages_update_own" ON messages
|
|
71
|
+
FOR UPDATE USING (auth.uid() = user_id);
|
|
72
|
+
|
|
73
|
+
CREATE POLICY "messages_delete_own" ON messages
|
|
74
|
+
FOR DELETE USING (auth.uid() = user_id);
|
|
75
|
+
|
|
76
|
+
-- ============================================================================
|
|
77
|
+
-- STORAGE BUCKET - chat-attachments
|
|
78
|
+
-- ============================================================================
|
|
79
|
+
INSERT INTO storage.buckets (id, name, public, file_size_limit, allowed_mime_types)
|
|
80
|
+
VALUES (
|
|
81
|
+
'chat-attachments',
|
|
82
|
+
'chat-attachments',
|
|
83
|
+
false,
|
|
84
|
+
52428800, -- 50MB limit
|
|
85
|
+
ARRAY['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'application/pdf', 'text/plain', 'audio/mpeg', 'audio/wav', 'video/mp4']
|
|
86
|
+
) ON CONFLICT (id) DO NOTHING;
|
|
87
|
+
|
|
88
|
+
-- Storage RLS for chat-attachments
|
|
89
|
+
CREATE POLICY "Users can view their own chat attachments"
|
|
90
|
+
ON storage.objects FOR SELECT TO authenticated
|
|
91
|
+
USING (bucket_id = 'chat-attachments' AND name LIKE auth.uid() || '/%');
|
|
92
|
+
|
|
93
|
+
CREATE POLICY "Users can upload their own chat attachments"
|
|
94
|
+
ON storage.objects FOR INSERT TO authenticated
|
|
95
|
+
WITH CHECK (bucket_id = 'chat-attachments' AND name LIKE auth.uid() || '/%');
|
|
96
|
+
|
|
97
|
+
CREATE POLICY "Users can update their own chat attachments"
|
|
98
|
+
ON storage.objects FOR UPDATE TO authenticated
|
|
99
|
+
USING (bucket_id = 'chat-attachments' AND name LIKE auth.uid() || '/%')
|
|
100
|
+
WITH CHECK (bucket_id = 'chat-attachments' AND name LIKE auth.uid() || '/%');
|
|
101
|
+
|
|
102
|
+
CREATE POLICY "Users can delete their own chat attachments"
|
|
103
|
+
ON storage.objects FOR DELETE TO authenticated
|
|
104
|
+
USING (bucket_id = 'chat-attachments' AND name LIKE auth.uid() || '/%');
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "chatbot",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "AI-powered chat assistant with Supabase backend",
|
|
5
|
+
"copy": [
|
|
6
|
+
{
|
|
7
|
+
"from": "apps/native/src/app/chatbot",
|
|
8
|
+
"to": "apps/native/src/app/(root)/(protected)/chatbot"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"from": "apps/native/src/features/chatbot",
|
|
12
|
+
"to": "apps/native/src/features/chatbot"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"from": "apps/native/src/api-client/supabase/chatbot.ts",
|
|
16
|
+
"to": "apps/native/src/api-client/supabase/chatbot.ts"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"from": "packages/backend/supabase/functions/chat-stream",
|
|
20
|
+
"to": "packages/backend/supabase/functions/chat-stream"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"from": "packages/backend/supabase/migrations/chatbot.sql",
|
|
24
|
+
"to": "packages/backend/supabase/migrations/chatbot.sql"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"from": "packages/backend/src/services/conversations.ts",
|
|
28
|
+
"to": "packages/backend/src/services/conversations.ts"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"from": "packages/backend/src/services/messages.ts",
|
|
32
|
+
"to": "packages/backend/src/services/messages.ts"
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"nav": {
|
|
36
|
+
"href": "/(root)/(protected)/chatbot",
|
|
37
|
+
"label": "AI Chatbot",
|
|
38
|
+
"icon": "💬",
|
|
39
|
+
"color": "#F43F5E"
|
|
40
|
+
},
|
|
41
|
+
"target": "native",
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"expo": [
|
|
44
|
+
"@supabase/supabase-js",
|
|
45
|
+
"@tanstack/react-query",
|
|
46
|
+
"expo-clipboard",
|
|
47
|
+
"markdown-it",
|
|
48
|
+
"react-native-markdown-display",
|
|
49
|
+
"react-syntax-highlighter",
|
|
50
|
+
"react-native-reanimated",
|
|
51
|
+
"react-native-safe-area-context"
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
"env": [
|
|
55
|
+
{
|
|
56
|
+
"key": "OPENAI_API_KEY",
|
|
57
|
+
"description": "OpenAI API key for chat completions",
|
|
58
|
+
"example": "sk-...",
|
|
59
|
+
"link": "https://platform.openai.com/api-keys",
|
|
60
|
+
"file": "packages/backend/supabase/.env",
|
|
61
|
+
"required": true
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
"manualSteps": [
|
|
65
|
+
{
|
|
66
|
+
"title": "Apply Supabase migration",
|
|
67
|
+
"description": "Run: cd packages/backend && supabase db push",
|
|
68
|
+
"content": "supabase db push"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"title": "Deploy Edge Function",
|
|
72
|
+
"description": "Run: cd packages/backend && supabase functions deploy chat-stream",
|
|
73
|
+
"content": "supabase functions deploy chat-stream"
|
|
74
|
+
}
|
|
75
|
+
],
|
|
76
|
+
"postInstall": {
|
|
77
|
+
"message": "📦 Remember to run 'supabase db push' and 'supabase functions deploy chat-stream'"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Image Analysis Domain Aggregator
|
|
3
3
|
*
|
|
4
4
|
* Single entry point for all image analysis Convex functions.
|
|
5
|
-
* Re-exports functions from
|
|
5
|
+
* Re-exports functions from imageAnalysis.ts to decouple
|
|
6
6
|
* frontend from internal file structure.
|
|
7
7
|
*/
|
|
8
8
|
|
|
@@ -15,4 +15,4 @@ export {
|
|
|
15
15
|
getUserImageAnalyses,
|
|
16
16
|
performAiAnalysis,
|
|
17
17
|
updateAnalysisResults,
|
|
18
|
-
} from '../
|
|
18
|
+
} from '../imageAnalysis';
|
|
@@ -44,7 +44,7 @@ export const analyzeImages = action({
|
|
|
44
44
|
|
|
45
45
|
// Create analysis record
|
|
46
46
|
const analysisId = await ctx.runMutation(
|
|
47
|
-
internal.
|
|
47
|
+
internal.imageAnalysis.createAnalysisRecord,
|
|
48
48
|
{
|
|
49
49
|
userId,
|
|
50
50
|
imageStorageIds,
|
|
@@ -56,7 +56,7 @@ export const analyzeImages = action({
|
|
|
56
56
|
// Schedule background AI analysis
|
|
57
57
|
await ctx.scheduler.runAfter(
|
|
58
58
|
0,
|
|
59
|
-
internal.
|
|
59
|
+
internal.imageAnalysis.performAiAnalysis,
|
|
60
60
|
{
|
|
61
61
|
analysisId,
|
|
62
62
|
},
|
|
@@ -101,7 +101,7 @@ export const performAiAnalysis = internalAction({
|
|
|
101
101
|
try {
|
|
102
102
|
// Get analysis record
|
|
103
103
|
const analysis = await ctx.runQuery(
|
|
104
|
-
internal.
|
|
104
|
+
internal.imageAnalysis.getAnalysisRecord,
|
|
105
105
|
{
|
|
106
106
|
analysisId: args.analysisId,
|
|
107
107
|
},
|
|
@@ -178,7 +178,7 @@ export const performAiAnalysis = internalAction({
|
|
|
178
178
|
|
|
179
179
|
// Update analysis record with results
|
|
180
180
|
await ctx.runMutation(
|
|
181
|
-
internal.
|
|
181
|
+
internal.imageAnalysis.updateAnalysisResults,
|
|
182
182
|
{
|
|
183
183
|
analysisId: args.analysisId,
|
|
184
184
|
results,
|
|
@@ -190,7 +190,7 @@ export const performAiAnalysis = internalAction({
|
|
|
190
190
|
|
|
191
191
|
// Update analysis record with error
|
|
192
192
|
await ctx.runMutation(
|
|
193
|
-
internal.
|
|
193
|
+
internal.imageAnalysis.updateAnalysisResults,
|
|
194
194
|
{
|
|
195
195
|
analysisId: args.analysisId,
|
|
196
196
|
status: 'failed',
|
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "image-analysis",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "AI-powered image analysis",
|
|
5
5
|
"copy": [
|
|
6
|
-
{
|
|
7
|
-
"from": "apps/native/src/app/(root)/(protected)/analysis",
|
|
8
|
-
"to": "apps/native/src/app/(root)/(protected)/analysis"
|
|
9
|
-
},
|
|
10
6
|
{
|
|
11
7
|
"from": "apps/native/src/features/image-analyzer",
|
|
12
8
|
"to": "apps/native/src/features/image-analyzer"
|
|
@@ -15,64 +11,28 @@
|
|
|
15
11
|
"from": "apps/native/src/api-client/image-analyzer.ts",
|
|
16
12
|
"to": "apps/native/src/api-client/image-analyzer.ts"
|
|
17
13
|
},
|
|
18
|
-
{
|
|
19
|
-
"from": "apps/native/assets/features/image-analyzer",
|
|
20
|
-
"to": "apps/native/assets/features/image-analyzer"
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
"from": "packages/backend/convex/imageAnalysis",
|
|
24
|
-
"to": "packages/backend/convex/imageAnalysis"
|
|
25
|
-
},
|
|
26
14
|
{
|
|
27
15
|
"from": "packages/backend/convex/imageAnalysis.ts",
|
|
28
16
|
"to": "packages/backend/convex/imageAnalysis.ts"
|
|
29
17
|
},
|
|
30
18
|
{
|
|
31
|
-
"from": "packages/backend/convex/
|
|
32
|
-
"to": "packages/backend/convex/
|
|
19
|
+
"from": "packages/backend/convex/imageAnalysis",
|
|
20
|
+
"to": "packages/backend/convex/imageAnalysis"
|
|
33
21
|
}
|
|
34
22
|
],
|
|
23
|
+
"configuration": {
|
|
24
|
+
"apiClient": {
|
|
25
|
+
"exports": [
|
|
26
|
+
"export { type ImageAnalyzerApi, imageAnalyzerApi } from './image-analyzer';"
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
},
|
|
35
30
|
"nav": {
|
|
36
|
-
"href": "/(root)/(protected)/
|
|
37
|
-
"label": "
|
|
31
|
+
"href": "/(root)/(protected)/image-analyzer",
|
|
32
|
+
"label": "Image Analyzer",
|
|
38
33
|
"icon": "🔍",
|
|
39
|
-
"color": "#
|
|
34
|
+
"color": "#3B82F6"
|
|
40
35
|
},
|
|
41
36
|
"target": "native",
|
|
42
|
-
"
|
|
43
|
-
{
|
|
44
|
-
"key": "OPENAI_API_KEY",
|
|
45
|
-
"description": "OpenAI API key for image analysis (optional - use this OR Gemini, not both).",
|
|
46
|
-
"example": "sk-abc123def456",
|
|
47
|
-
"link": "https://platform.openai.com/account/api-keys",
|
|
48
|
-
"file": "packages/backend/.env.local"
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
"key": "GEMINI_API_KEY",
|
|
52
|
-
"description": "Google Generative AI key for image analysis (optional - use this OR OpenAI, not both).",
|
|
53
|
-
"example": "AIzaSyExampleGeminiKey",
|
|
54
|
-
"link": "https://aistudio.google.com/api-keys",
|
|
55
|
-
"file": "packages/backend/.env.local"
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
"key": "AI_PROVIDER",
|
|
59
|
-
"description": "Provider to use for the analyzer (openai or gemini). Defaults to openai if unset.",
|
|
60
|
-
"example": "openai",
|
|
61
|
-
"file": "packages/backend/.env.local",
|
|
62
|
-
"required": false
|
|
63
|
-
}
|
|
64
|
-
],
|
|
65
|
-
"manualSteps": [
|
|
66
|
-
{
|
|
67
|
-
"title": "Get an LLM API key (OpenAI or Gemini)",
|
|
68
|
-
"description": "Start with either provider (OpenAI or Gemini). You can add both later if you like.\\n\\n Option A: OpenAI\\n Visit https://platform.openai.com/account/api-keys and create an API key\\n\\n Option B: Google Gemini\\n Visit https://aistudio.google.com/api-keys and create an API key",
|
|
69
|
-
"link": "https://platform.openai.com/account/api-keys"
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
"title": "Add keys to backend .env file",
|
|
73
|
-
"description": "Open packages/backend/.env.local and add at least one provider key. You can add both. Optionally set AI_PROVIDER to choose it explicitly.",
|
|
74
|
-
"file": "packages/backend/.env.local",
|
|
75
|
-
"content": "# Add one or both:\\nOPENAI_API_KEY=sk-...\\nGEMINI_API_KEY=AIzaSy...\\n# Optional override:\\nAI_PROVIDER=openai\\n"
|
|
76
|
-
}
|
|
77
|
-
]
|
|
37
|
+
"dependencies": { "expo": ["expo-camera", "expo-image-picker"] }
|
|
78
38
|
}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { Stack, useLocalSearchParams, useRouter } from 'expo-router';
|
|
2
|
+
import { useCallback, useMemo, useState } from 'react';
|
|
3
|
+
import {
|
|
4
|
+
Alert,
|
|
5
|
+
Platform,
|
|
6
|
+
ScrollView,
|
|
7
|
+
TouchableOpacity,
|
|
8
|
+
View,
|
|
9
|
+
} from 'react-native';
|
|
10
|
+
|
|
11
|
+
import { Image, ScreenFooter, Text } from '@/components/ui';
|
|
12
|
+
import type {
|
|
13
|
+
AnalysisGoal,
|
|
14
|
+
FeedbackStyle,
|
|
15
|
+
} from '@/features/image-analyzer/config/analysis-config';
|
|
16
|
+
import type { AnalysisFlowConfig } from '@/features/image-analyzer/config/master-analysis-config';
|
|
17
|
+
import { useNavigationOptions } from '@/lib/hooks/use-navigation-options';
|
|
18
|
+
|
|
19
|
+
type DynamicAnalysisOptionsProps = {
|
|
20
|
+
config: AnalysisFlowConfig;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// Enhanced goal options with colors and icons
|
|
24
|
+
const createEnhancedGoals = (goals: readonly AnalysisGoal[]) => [
|
|
25
|
+
{
|
|
26
|
+
...goals[0],
|
|
27
|
+
color: 'bg-blue-500',
|
|
28
|
+
icon: '🔍',
|
|
29
|
+
accent:
|
|
30
|
+
'border-blue-200 bg-blue-50 dark:border-blue-700 dark:bg-blue-900/20',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
...goals[1],
|
|
34
|
+
color: 'bg-green-500',
|
|
35
|
+
icon: '⚡',
|
|
36
|
+
accent:
|
|
37
|
+
'border-green-200 bg-green-50 dark:border-green-700 dark:bg-green-900/20',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
...goals[2],
|
|
41
|
+
color: 'bg-purple-500',
|
|
42
|
+
icon: '🎯',
|
|
43
|
+
accent:
|
|
44
|
+
'border-purple-200 bg-purple-50 dark:border-purple-700 dark:bg-purple-900/20',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
...goals[3],
|
|
48
|
+
color: 'bg-orange-500',
|
|
49
|
+
icon: '⚖️',
|
|
50
|
+
accent:
|
|
51
|
+
'border-orange-200 bg-orange-50 dark:border-orange-700 dark:bg-orange-900/20',
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
// Enhanced feedback styles with colors and icons
|
|
56
|
+
const createEnhancedStyles = (styles: readonly FeedbackStyle[]) => [
|
|
57
|
+
{
|
|
58
|
+
...styles[0],
|
|
59
|
+
color: 'bg-neutral-600',
|
|
60
|
+
icon: '👔',
|
|
61
|
+
accent:
|
|
62
|
+
'border-neutral-200 bg-neutral-50 dark:border-neutral-700 dark:bg-neutral-900/20',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
...styles[1],
|
|
66
|
+
color: 'bg-cyan-500',
|
|
67
|
+
icon: '😊',
|
|
68
|
+
accent:
|
|
69
|
+
'border-cyan-200 bg-cyan-50 dark:border-cyan-700 dark:bg-cyan-900/20',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
...styles[2],
|
|
73
|
+
color: 'bg-indigo-600',
|
|
74
|
+
icon: '🔬',
|
|
75
|
+
accent:
|
|
76
|
+
'border-indigo-200 bg-indigo-50 dark:border-indigo-700 dark:bg-indigo-900/20',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
...styles[3],
|
|
80
|
+
color: 'bg-pink-500',
|
|
81
|
+
icon: '🎨',
|
|
82
|
+
accent:
|
|
83
|
+
'border-pink-200 bg-pink-50 dark:border-pink-700 dark:bg-pink-900/20',
|
|
84
|
+
},
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
export function DynamicAnalysisOptionsScreen({
|
|
88
|
+
config,
|
|
89
|
+
}: DynamicAnalysisOptionsProps) {
|
|
90
|
+
const router = useRouter();
|
|
91
|
+
const { standard } = useNavigationOptions();
|
|
92
|
+
const params = useLocalSearchParams();
|
|
93
|
+
|
|
94
|
+
const imageUris: string[] = useMemo(
|
|
95
|
+
() => (params.imageUris ? JSON.parse(params.imageUris as string) : []),
|
|
96
|
+
[params.imageUris],
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const [selectedGoal, setSelectedGoal] = useState<string>('');
|
|
100
|
+
const [selectedFeedbackStyle, setSelectedFeedbackStyle] =
|
|
101
|
+
useState<string>('');
|
|
102
|
+
|
|
103
|
+
// Create enhanced versions with emojis and colors
|
|
104
|
+
const enhancedGoals = useMemo(
|
|
105
|
+
() => createEnhancedGoals(config.availableGoals),
|
|
106
|
+
[config.availableGoals],
|
|
107
|
+
);
|
|
108
|
+
const enhancedStyles = useMemo(
|
|
109
|
+
() => createEnhancedStyles(config.availableFeedbackStyles),
|
|
110
|
+
[config.availableFeedbackStyles],
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const handleStartAnalysis = useCallback(() => {
|
|
114
|
+
if (!selectedGoal || !selectedFeedbackStyle) {
|
|
115
|
+
Alert.alert(
|
|
116
|
+
'Selection Required',
|
|
117
|
+
'Please select both an analysis goal and feedback style to continue.',
|
|
118
|
+
);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
router.push({
|
|
123
|
+
pathname: '/analysis/[type]/loading' as any,
|
|
124
|
+
params: {
|
|
125
|
+
type: config.id,
|
|
126
|
+
imageUris: JSON.stringify(imageUris),
|
|
127
|
+
analysisGoal: selectedGoal,
|
|
128
|
+
feedbackStyle: selectedFeedbackStyle,
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
}, [selectedGoal, selectedFeedbackStyle, imageUris, router, config.id]);
|
|
132
|
+
|
|
133
|
+
const handleBackToCapture = useCallback(() => {
|
|
134
|
+
router.back();
|
|
135
|
+
}, [router]);
|
|
136
|
+
|
|
137
|
+
const selectedGoalData = enhancedGoals.find(
|
|
138
|
+
(goal) => goal.id === selectedGoal,
|
|
139
|
+
);
|
|
140
|
+
const selectedStyleData = enhancedStyles.find(
|
|
141
|
+
(style) => style.id === selectedFeedbackStyle,
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<>
|
|
146
|
+
<Stack.Screen
|
|
147
|
+
options={{
|
|
148
|
+
...standard,
|
|
149
|
+
title: config.name,
|
|
150
|
+
headerBackButtonDisplayMode: 'generic',
|
|
151
|
+
}}
|
|
152
|
+
/>
|
|
153
|
+
<ScrollView
|
|
154
|
+
className="flex-1"
|
|
155
|
+
contentContainerStyle={{
|
|
156
|
+
paddingBottom: 120,
|
|
157
|
+
paddingTop: Platform.OS === 'ios' ? 78 : 0,
|
|
158
|
+
}}
|
|
159
|
+
showsVerticalScrollIndicator={false}
|
|
160
|
+
>
|
|
161
|
+
<View className="px-6 py-8">
|
|
162
|
+
{/* Image Preview Section */}
|
|
163
|
+
<View className="mb-8 rounded-2xl bg-white p-4 py-2 shadow-sm dark:bg-neutral-800">
|
|
164
|
+
<Text className="mb-4 ml-1 text-base font-medium text-neutral-800 dark:text-neutral-200">
|
|
165
|
+
📷 {imageUris.length} Images Ready
|
|
166
|
+
</Text>
|
|
167
|
+
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
|
|
168
|
+
<View className="flex-row gap-3">
|
|
169
|
+
{imageUris.map((uri, index) => (
|
|
170
|
+
<View key={index} className="items-center">
|
|
171
|
+
<View className="size-20 overflow-hidden rounded-xl bg-neutral-200 dark:bg-neutral-700">
|
|
172
|
+
<Image
|
|
173
|
+
source={{ uri }}
|
|
174
|
+
className="size-full"
|
|
175
|
+
contentFit="cover"
|
|
176
|
+
/>
|
|
177
|
+
</View>
|
|
178
|
+
<Text className="mt-1 text-xs text-neutral-500">
|
|
179
|
+
{index + 1}
|
|
180
|
+
</Text>
|
|
181
|
+
</View>
|
|
182
|
+
))}
|
|
183
|
+
</View>
|
|
184
|
+
</ScrollView>
|
|
185
|
+
</View>
|
|
186
|
+
|
|
187
|
+
{/* Analysis Goal Selection */}
|
|
188
|
+
<View className="mb-8">
|
|
189
|
+
<Text className="mb-4 text-xl font-bold text-neutral-900 dark:text-white">
|
|
190
|
+
Analysis Goal
|
|
191
|
+
</Text>
|
|
192
|
+
<View className="gap-y-3">
|
|
193
|
+
{enhancedGoals.map((goal) => (
|
|
194
|
+
<TouchableOpacity
|
|
195
|
+
key={goal.id}
|
|
196
|
+
onPress={() => setSelectedGoal(goal.id)}
|
|
197
|
+
className={`flex-row items-center rounded-2xl border-2 p-4 ${
|
|
198
|
+
selectedGoal === goal.id
|
|
199
|
+
? goal.accent
|
|
200
|
+
: 'border-neutral-200 bg-white dark:border-neutral-700 dark:bg-neutral-800'
|
|
201
|
+
}`}
|
|
202
|
+
>
|
|
203
|
+
<View
|
|
204
|
+
className={`mr-4 size-12 items-center justify-center rounded-xl ${goal.color}`}
|
|
205
|
+
>
|
|
206
|
+
<Text className="text-xl">{goal.icon}</Text>
|
|
207
|
+
</View>
|
|
208
|
+
<View className="flex-1">
|
|
209
|
+
<Text className="mb-1 text-base font-semibold text-neutral-900 dark:text-white">
|
|
210
|
+
{goal.label}
|
|
211
|
+
</Text>
|
|
212
|
+
<Text className="text-sm text-neutral-600 dark:text-neutral-400">
|
|
213
|
+
{goal.description}
|
|
214
|
+
</Text>
|
|
215
|
+
</View>
|
|
216
|
+
</TouchableOpacity>
|
|
217
|
+
))}
|
|
218
|
+
</View>
|
|
219
|
+
</View>
|
|
220
|
+
|
|
221
|
+
{/* Feedback Style Selection */}
|
|
222
|
+
<View className="mb-8">
|
|
223
|
+
<Text className="mb-4 text-xl font-bold text-neutral-900 dark:text-white">
|
|
224
|
+
Feedback Tone
|
|
225
|
+
</Text>
|
|
226
|
+
<View className="gap-y-3">
|
|
227
|
+
{enhancedStyles.map((style) => (
|
|
228
|
+
<TouchableOpacity
|
|
229
|
+
key={style.id}
|
|
230
|
+
onPress={() => setSelectedFeedbackStyle(style.id)}
|
|
231
|
+
className={`flex-row items-center rounded-2xl border-2 p-4 ${
|
|
232
|
+
selectedFeedbackStyle === style.id
|
|
233
|
+
? style.accent
|
|
234
|
+
: 'border-neutral-200 bg-white dark:border-neutral-700 dark:bg-neutral-800'
|
|
235
|
+
}`}
|
|
236
|
+
>
|
|
237
|
+
<View
|
|
238
|
+
className={`mr-4 size-12 items-center justify-center rounded-xl ${style.color}`}
|
|
239
|
+
>
|
|
240
|
+
<Text className="text-xl">{style.icon}</Text>
|
|
241
|
+
</View>
|
|
242
|
+
<View className="flex-1">
|
|
243
|
+
<Text className="mb-1 text-base font-semibold text-neutral-900 dark:text-white">
|
|
244
|
+
{style.label}
|
|
245
|
+
</Text>
|
|
246
|
+
<Text className="text-sm text-neutral-600 dark:text-neutral-400">
|
|
247
|
+
{style.description}
|
|
248
|
+
</Text>
|
|
249
|
+
</View>
|
|
250
|
+
</TouchableOpacity>
|
|
251
|
+
))}
|
|
252
|
+
</View>
|
|
253
|
+
</View>
|
|
254
|
+
|
|
255
|
+
{/* Selection Summary */}
|
|
256
|
+
<View className="mb-8 min-h-[100px]">
|
|
257
|
+
{selectedGoal || selectedFeedbackStyle ? (
|
|
258
|
+
<View className="rounded-2xl border border-border p-4 dark:bg-card">
|
|
259
|
+
<Text className="mb-2 text-lg font-semibold">
|
|
260
|
+
Your Selection
|
|
261
|
+
</Text>
|
|
262
|
+
{selectedGoalData && (
|
|
263
|
+
<Text className="mb-2 text-sm">
|
|
264
|
+
{selectedGoalData.icon} Goal: {selectedGoalData.label}
|
|
265
|
+
</Text>
|
|
266
|
+
)}
|
|
267
|
+
{selectedStyleData && (
|
|
268
|
+
<Text className="text-sm">
|
|
269
|
+
{selectedStyleData.icon} Tone: {selectedStyleData.label}
|
|
270
|
+
</Text>
|
|
271
|
+
)}
|
|
272
|
+
</View>
|
|
273
|
+
) : (
|
|
274
|
+
<View className="rounded-2xl border border-neutral-200 bg-neutral-50 p-4 opacity-50 dark:border-neutral-700 dark:bg-neutral-800">
|
|
275
|
+
<Text className="mb-2 text-lg font-semibold text-neutral-500 dark:text-neutral-400">
|
|
276
|
+
Your Selection
|
|
277
|
+
</Text>
|
|
278
|
+
<Text className="text-sm text-neutral-400 dark:text-neutral-500">
|
|
279
|
+
Please select your analysis preferences above
|
|
280
|
+
</Text>
|
|
281
|
+
</View>
|
|
282
|
+
)}
|
|
283
|
+
</View>
|
|
284
|
+
</View>
|
|
285
|
+
</ScrollView>
|
|
286
|
+
|
|
287
|
+
<ScreenFooter
|
|
288
|
+
buttons={[
|
|
289
|
+
{
|
|
290
|
+
text: 'Back to Capture',
|
|
291
|
+
onPress: handleBackToCapture,
|
|
292
|
+
variant: 'outline',
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
text: 'Start Analysis',
|
|
296
|
+
onPress: handleStartAnalysis,
|
|
297
|
+
variant: 'default',
|
|
298
|
+
disabled: !selectedGoal || !selectedFeedbackStyle,
|
|
299
|
+
},
|
|
300
|
+
]}
|
|
301
|
+
/>
|
|
302
|
+
</>
|
|
303
|
+
);
|
|
304
|
+
}
|