vybekiit 0.7.28 → 0.7.32
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/dist/bin.js +17286 -17351
- package/dist/global-guidance/language.md +13 -108
- package/dist/global-skills/add-ai/SKILL.md +13 -8
- package/dist/global-skills/add-analytics/SKILL.md +10 -7
- package/dist/global-skills/add-app-clip/SKILL.md +1 -0
- package/dist/global-skills/add-blog/SKILL.md +10 -7
- package/dist/global-skills/add-crud/SKILL.md +8 -0
- package/dist/global-skills/add-files/SKILL.md +10 -7
- package/dist/global-skills/add-images/SKILL.md +10 -7
- package/dist/global-skills/add-language/SKILL.md +11 -8
- package/dist/global-skills/add-notifications/SKILL.md +10 -7
- package/dist/global-skills/add-realtime/SKILL.md +10 -7
- package/dist/global-skills/add-route/SKILL.md +6 -0
- package/dist/global-skills/add-search/SKILL.md +10 -7
- package/dist/global-skills/add-signin/SKILL.md +10 -8
- package/dist/global-skills/add-teams/SKILL.md +14 -10
- package/dist/global-skills/add-upload/SKILL.md +6 -0
- package/dist/global-skills/aws-cdk/SKILL.md +1 -0
- package/dist/global-skills/aws-cloudformation/SKILL.md +1 -0
- package/dist/global-skills/aws-serverless/SKILL.md +1 -0
- package/dist/global-skills/aws-serverless/references/compute-decision-edge-cases.md +77 -0
- package/dist/global-skills/back-up-my-code/SKILL.md +16 -5
- package/dist/global-skills/better-auth-best-practices/SKILL.md +1 -0
- package/dist/global-skills/better-auth-security-best-practices/SKILL.md +1 -0
- package/dist/global-skills/building-native-ui/SKILL.md +1 -0
- package/dist/global-skills/buy-domain/SKILL.md +10 -7
- package/dist/global-skills/check-safety/SKILL.md +11 -8
- package/dist/global-skills/claimable-postgres/SKILL.md +1 -0
- package/dist/global-skills/configure-capabilities/SKILL.md +7 -1
- package/dist/global-skills/connect-account/SKILL.md +7 -1
- package/dist/global-skills/connect-account-backend/SKILL.md +6 -0
- package/dist/global-skills/create-auth-skill/SKILL.md +1 -0
- package/dist/global-skills/design-my-data/SKILL.md +12 -5
- package/dist/global-skills/doctor/SKILL.md +17 -10
- package/dist/global-skills/eas-app-stores/SKILL.md +2 -1
- package/dist/global-skills/eas-app-stores/agents/openai.yaml +1 -1
- package/dist/global-skills/eas-hosting/SKILL.md +2 -1
- package/dist/global-skills/eas-hosting/agents/openai.yaml +1 -1
- package/dist/global-skills/eas-observe/SKILL.md +2 -1
- package/dist/global-skills/eas-observe/agents/openai.yaml +1 -1
- package/dist/global-skills/eas-simulator/SKILL.md +9 -1
- package/dist/global-skills/eas-simulator/agents/openai.yaml +1 -1
- package/dist/global-skills/eas-simulator/references/logs-and-crashes.md +101 -0
- package/dist/global-skills/eas-simulator/references/troubleshooting.md +4 -0
- package/dist/global-skills/eas-update/SKILL.md +2 -1
- package/dist/global-skills/eas-update/agents/openai.yaml +1 -1
- package/dist/global-skills/eas-update-insights/SKILL.md +2 -1
- package/dist/global-skills/eas-update-insights/agents/openai.yaml +1 -1
- package/dist/global-skills/eas-workflows/SKILL.md +2 -1
- package/dist/global-skills/eas-workflows/agents/openai.yaml +1 -1
- package/dist/global-skills/email-best-practices/SKILL.md +1 -0
- package/dist/global-skills/expo-animation/SKILL.md +2 -1
- package/dist/global-skills/expo-api-routes/SKILL.md +1 -0
- package/dist/global-skills/expo-app-clip/SKILL.md +2 -1
- package/dist/global-skills/expo-brownfield/SKILL.md +2 -1
- package/dist/global-skills/expo-cicd-workflows/SKILL.md +1 -0
- package/dist/global-skills/expo-data-fetching/SKILL.md +2 -1
- package/dist/global-skills/expo-deployment/SKILL.md +1 -0
- package/dist/global-skills/expo-design-system/SKILL.md +2 -1
- package/dist/global-skills/expo-dev-client/SKILL.md +2 -1
- package/dist/global-skills/expo-dom/SKILL.md +2 -1
- package/dist/global-skills/expo-examples/SKILL.md +2 -1
- package/dist/global-skills/expo-migrate-module/SKILL.md +2 -1
- package/dist/global-skills/expo-module/SKILL.md +2 -1
- package/dist/global-skills/expo-native-ui/SKILL.md +2 -1
- package/dist/global-skills/expo-observe/SKILL.md +1 -0
- package/dist/global-skills/expo-overview/SKILL.md +2 -1
- package/dist/global-skills/expo-project-structure/SKILL.md +2 -1
- package/dist/global-skills/expo-router/SKILL.md +2 -1
- package/dist/global-skills/expo-skill-eval/SKILL.md +1 -0
- package/dist/global-skills/expo-skill-feedback/SKILL.md +1 -0
- package/dist/global-skills/expo-tailwind-setup/SKILL.md +1 -0
- package/dist/global-skills/expo-ui/SKILL.md +2 -1
- package/dist/global-skills/expo-upgrade/SKILL.md +2 -1
- package/dist/global-skills/expo-web-to-native/SKILL.md +2 -1
- package/dist/global-skills/extension-to-functions-codebase/SKILL.md +1 -0
- package/dist/global-skills/firebase-ai-logic-basics/SKILL.md +1 -0
- package/dist/global-skills/firebase-ai-logic-basics/references/tts_generation.md +676 -0
- package/dist/global-skills/firebase-app-hosting-basics/SKILL.md +1 -0
- package/dist/global-skills/firebase-auth-basics/SKILL.md +11 -6
- package/dist/global-skills/firebase-auth-basics/references/client_sdk_android.md +61 -23
- package/dist/global-skills/firebase-basics/SKILL.md +2 -0
- package/dist/global-skills/firebase-basics/references/android_setup.md +165 -23
- package/dist/global-skills/firebase-crashlytics/SKILL.md +1 -0
- package/dist/global-skills/firebase-crashlytics/references/android_setup.md +147 -119
- package/dist/global-skills/firebase-data-connect/SKILL.md +1 -0
- package/dist/global-skills/firebase-data-connect/reference/sdk_android.md +81 -25
- package/dist/global-skills/firebase-firestore/SKILL.md +1 -0
- package/dist/global-skills/firebase-firestore/references/enterprise/android_sdk_usage.md +70 -44
- package/dist/global-skills/firebase-firestore/references/standard/android_sdk_usage.md +63 -27
- package/dist/global-skills/firebase-hosting-basics/SKILL.md +1 -0
- package/dist/global-skills/firebase-remote-config-basics/SKILL.md +1 -0
- package/dist/global-skills/firebase-remote-config-basics/references/android_setup.md +87 -95
- package/dist/global-skills/firebase-security-rules-auditor/SKILL.md +1 -0
- package/dist/global-skills/firestore-rules-creation/SKILL.md +1 -0
- package/dist/global-skills/go-live/SKILL.md +13 -7
- package/dist/global-skills/harden/SKILL.md +15 -10
- package/dist/global-skills/instrument-feature-flags/SKILL.md +1 -0
- package/dist/global-skills/instrument-product-analytics/SKILL.md +1 -0
- package/dist/global-skills/manifest.json +5 -1
- package/dist/global-skills/mongodb-atlas-stream-processing/SKILL.md +1 -0
- package/dist/global-skills/mongodb-connection/SKILL.md +1 -0
- package/dist/global-skills/mongodb-mcp-setup/SKILL.md +1 -0
- package/dist/global-skills/mongodb-natural-language-querying/SKILL.md +1 -0
- package/dist/global-skills/mongodb-qe-size-estimation/SKILL.md +224 -0
- package/dist/global-skills/mongodb-query-optimizer/SKILL.md +1 -0
- package/dist/global-skills/mongodb-schema-design/SKILL.md +1 -0
- package/dist/global-skills/mongodb-search-and-ai/SKILL.md +1 -0
- package/dist/global-skills/native-data-fetching/SKILL.md +1 -0
- package/dist/global-skills/neon/SKILL.md +18 -5
- package/dist/global-skills/neon-ai-gateway/SKILL.md +1 -0
- package/dist/global-skills/neon-auth/SKILL.md +1 -0
- package/dist/global-skills/neon-functions/SKILL.md +12 -0
- package/dist/global-skills/neon-functions/references/native-binaries.md +188 -0
- package/dist/global-skills/neon-object-storage/SKILL.md +1 -0
- package/dist/global-skills/neon-postgres/SKILL.md +1 -0
- package/dist/global-skills/neon-postgres-branches/SKILL.md +1 -0
- package/dist/global-skills/neon-postgres-egress-optimizer/SKILL.md +1 -0
- package/dist/global-skills/onboarding/SKILL.md +46 -29
- package/dist/global-skills/plan-my-idea/SKILL.md +7 -1
- package/dist/global-skills/publish-app/SKILL.md +7 -1
- package/dist/global-skills/publish-extension/SKILL.md +7 -1
- package/dist/global-skills/resend/SKILL.md +2 -1
- package/dist/global-skills/resend/references/webhooks.md +2 -0
- package/dist/global-skills/reset-password/SKILL.md +10 -6
- package/dist/global-skills/save-data/SKILL.md +51 -16
- package/dist/global-skills/sentry-sdk-setup/SKILL.md +1 -0
- package/dist/global-skills/sentry-workflow/SKILL.md +1 -0
- package/dist/global-skills/setup-email/SKILL.md +13 -8
- package/dist/global-skills/setup-payments/SKILL.md +19 -15
- package/dist/global-skills/setup-sms/SKILL.md +7 -1
- package/dist/global-skills/sign-in-with-email-link/SKILL.md +10 -6
- package/dist/global-skills/sign-in-with-google/SKILL.md +20 -13
- package/dist/global-skills/sign-in-with-phone/SKILL.md +11 -7
- package/dist/global-skills/stripe-best-practices/SKILL.md +2 -1
- package/dist/global-skills/stripe-best-practices/references/billing.md +2 -0
- package/dist/global-skills/stripe-best-practices/references/payments.md +1 -1
- package/dist/global-skills/stripe-best-practices/references/tax.md +2 -2
- package/dist/global-skills/supabase/CHANGELOG.md +19 -0
- package/dist/global-skills/supabase/SKILL.md +1 -0
- package/dist/global-skills/supabase-postgres-best-practices/SKILL.md +1 -0
- package/dist/global-skills/track-errors/SKILL.md +18 -13
- package/dist/global-skills/update-kit/SKILL.md +7 -1
- package/dist/global-skills/upgrading-expo/SKILL.md +1 -0
- package/dist/global-skills/use-dom/SKILL.md +1 -0
- package/dist/global-skills/use-railway/SKILL.md +6 -5
- package/dist/global-skills/use-railway/references/configure.md +34 -1
- package/dist/global-skills/use-railway/references/iac.md +2 -0
- package/dist/global-skills/use-railway/references/tracing.md +89 -44
- package/dist/global-skills/validate-my-idea/SKILL.md +6 -0
- package/dist/global-skills/vercel-composition-patterns/SKILL.md +1 -0
- package/dist/global-skills/vercel-react-best-practices/SKILL.md +1 -0
- package/dist/global-skills/vybekiit-feedback/SKILL.md +6 -0
- package/dist/global-skills/vybekiitReferences/backend/AGENTS.md +50 -0
- package/dist/global-skills/vybekiitReferences/backend/agent/goal-index.md +17 -0
- package/dist/global-skills/vybekiitReferences/backend/agent/goal-routes.json +229 -0
- package/dist/global-skills/vybekiitReferences/backend/agent/mcp-agent.json +12 -0
- package/dist/global-skills/vybekiitReferences/backend/agent/mcp-firebase.json +8 -0
- package/dist/global-skills/vybekiitReferences/backend/agent/mcp-mongodb.json +12 -0
- package/dist/global-skills/vybekiitReferences/backend/agent/mcp-neon.json +8 -0
- package/dist/global-skills/vybekiitReferences/backend/agent/mcp-paypal.json +8 -0
- package/dist/global-skills/vybekiitReferences/backend/agent/mcp-posthog.json +17 -0
- package/dist/global-skills/vybekiitReferences/backend/agent/mcp-railway-local.json +8 -0
- package/dist/global-skills/vybekiitReferences/backend/agent/mcp-railway-remote.json +7 -0
- package/dist/global-skills/vybekiitReferences/backend/agent/mcp-sentry.json +7 -0
- package/dist/global-skills/vybekiitReferences/backend/agent/mcp-setup.md +140 -0
- package/dist/global-skills/vybekiitReferences/backend/agent/mcp-supabase.json +7 -0
- package/dist/global-skills/vybekiitReferences/backend/agent/mcp-twilio-alpha.json +8 -0
- package/dist/global-skills/vybekiitReferences/backend/agent/mcp-twilio-docs.json +7 -0
- package/dist/global-skills/vybekiitReferences/backend/agent/session-bootstrap.md +44 -0
- package/dist/global-skills/vybekiitReferences/backend/agent/tech-references.md +28 -0
- package/dist/global-skills/vybekiitReferences/backend/language.md +345 -0
- package/dist/global-skills/vybekiitReferences/backend/platform-skills/deploy-railway-vybekiit.md +26 -0
- package/dist/global-skills/vybekiitReferences/backend/platform-skills/extend-capabilities-vybekiit.md +127 -0
- package/dist/global-skills/vybekiitReferences/backend/platform-skills/mcp-tools-vybekiit.md +87 -0
- package/dist/global-skills/vybekiitReferences/backend/platform-skills/mongodb-vybekiit.md +34 -0
- package/dist/global-skills/vybekiitReferences/backend/platform-skills/railway-postgres-vybekiit.md +24 -0
- package/dist/global-skills/vybekiitReferences/extension/AGENTS.md +88 -0
- package/dist/global-skills/vybekiitReferences/extension/agent/goal-index.md +27 -0
- package/dist/global-skills/vybekiitReferences/extension/agent/goal-routes.json +239 -0
- package/dist/global-skills/vybekiitReferences/extension/agent/mcp-agent.json +14 -0
- package/dist/global-skills/vybekiitReferences/extension/agent/mcp-posthog.json +17 -0
- package/dist/global-skills/vybekiitReferences/extension/agent/mcp-sentry.json +7 -0
- package/dist/global-skills/vybekiitReferences/extension/agent/mcp-setup.md +111 -0
- package/dist/global-skills/vybekiitReferences/extension/agent/mcp-twilio-alpha.json +13 -0
- package/dist/global-skills/vybekiitReferences/extension/agent/mcp-twilio-docs.json +7 -0
- package/dist/global-skills/vybekiitReferences/extension/agent/session-bootstrap.md +44 -0
- package/dist/global-skills/vybekiitReferences/extension/agent/tech-references.md +28 -0
- package/dist/global-skills/vybekiitReferences/extension/agent/ui-sources.md +33 -0
- package/dist/global-skills/vybekiitReferences/extension/language.md +348 -0
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/README.md +32 -0
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/browser-automation-vybekiit.md +44 -0
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/chrome-extension-vybekiit.md +28 -0
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/ci-vybekiit.md +26 -0
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/client-state-vybekiit.md +25 -0
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/code-hygiene-vybekiit.md +38 -0
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/data-model-vybekiit.md +29 -0
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/env-secrets-vybekiit.md +21 -0
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/extend-capabilities-vybekiit.md +127 -0
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/format-lint-vybekiit.md +33 -0
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/github-vybekiit.md +47 -0
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/i18n-vybekiit.md +39 -0
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/mcp-tools-vybekiit.md +87 -0
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/observability-vybekiit.md +36 -0
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/planning-vybekiit.md +59 -0
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/playwright-vybekiit.md +42 -0
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/react-patterns-vybekiit.md +45 -0
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/responsive-vybekiit.md +27 -0
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/sentry-vybekiit.md +33 -0
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/ship-via-pr-vybekiit.md +49 -0
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/testing-vybekiit.md +33 -0
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/ui-consistency-vybekiit.md +33 -0
- package/dist/global-skills/vybekiitReferences/mobile/AGENTS.md +475 -0
- package/dist/global-skills/vybekiitReferences/mobile/agent/goal-index.md +32 -0
- package/dist/global-skills/vybekiitReferences/mobile/agent/goal-routes.json +279 -0
- package/dist/global-skills/vybekiitReferences/mobile/agent/mcp-agent.json +14 -0
- package/dist/global-skills/vybekiitReferences/mobile/agent/mcp-firebase.json +8 -0
- package/dist/global-skills/vybekiitReferences/mobile/agent/mcp-neon.json +8 -0
- package/dist/global-skills/vybekiitReferences/mobile/agent/mcp-paypal.json +8 -0
- package/dist/global-skills/vybekiitReferences/mobile/agent/mcp-posthog.json +17 -0
- package/dist/global-skills/vybekiitReferences/mobile/agent/mcp-sentry.json +7 -0
- package/dist/global-skills/vybekiitReferences/mobile/agent/mcp-setup.md +112 -0
- package/dist/global-skills/vybekiitReferences/mobile/agent/mcp-stripe.json +7 -0
- package/dist/global-skills/vybekiitReferences/mobile/agent/mcp-supabase.json +7 -0
- package/dist/global-skills/vybekiitReferences/mobile/agent/mcp-twilio-alpha.json +13 -0
- package/dist/global-skills/vybekiitReferences/mobile/agent/mcp-twilio-docs.json +7 -0
- package/dist/global-skills/vybekiitReferences/mobile/agent/session-bootstrap.md +44 -0
- package/dist/global-skills/vybekiitReferences/mobile/agent/tech-references.md +28 -0
- package/dist/global-skills/vybekiitReferences/mobile/agent/ui-catalog-index.mobile.json +36294 -0
- package/dist/global-skills/vybekiitReferences/mobile/agent/ui-sources.mobile.md +36 -0
- package/dist/global-skills/vybekiitReferences/mobile/language.md +352 -0
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/README.md +43 -0
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/ci-vybekiit.md +26 -0
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/client-state-vybekiit.md +25 -0
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/code-hygiene-vybekiit.md +38 -0
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/data-model-vybekiit.md +29 -0
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/env-secrets-vybekiit.md +21 -0
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/extend-capabilities-vybekiit.md +127 -0
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/format-lint-vybekiit.md +33 -0
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/github-vybekiit.md +47 -0
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/i18n-vybekiit.md +41 -0
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/launch-store-vybekiit.md +26 -0
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/mcp-tools-vybekiit.md +87 -0
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/observability-vybekiit.md +36 -0
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/planning-vybekiit.md +59 -0
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/playwright-vybekiit.md +42 -0
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/react-patterns-vybekiit.md +45 -0
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/sentry-vybekiit.md +33 -0
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/ship-via-pr-vybekiit.md +49 -0
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/testing-vybekiit.md +33 -0
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/ui-consistency-vybekiit.md +33 -0
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/ui-port-from-web-vybekiit.md +44 -0
- package/dist/global-skills/vybekiitReferences/spa/AGENTS.md +91 -0
- package/dist/global-skills/vybekiitReferences/spa/agent/goal-index.md +30 -0
- package/dist/global-skills/vybekiitReferences/spa/agent/goal-routes.json +267 -0
- package/dist/global-skills/vybekiitReferences/spa/agent/mcp-agent.json +14 -0
- package/dist/global-skills/vybekiitReferences/spa/agent/mcp-setup.md +63 -0
- package/dist/global-skills/vybekiitReferences/spa/agent/session-bootstrap.md +44 -0
- package/dist/global-skills/vybekiitReferences/spa/agent/ui-sources.md +63 -0
- package/dist/global-skills/vybekiitReferences/spa/language.md +217 -0
- package/dist/global-skills/vybekiitReferences/spa/platform-skills/mcp-tools-vybekiit.md +87 -0
- package/dist/global-skills/vybekiitReferences/web/AGENTS.md +138 -0
- package/dist/global-skills/vybekiitReferences/web/agent/goal-index.md +54 -0
- package/dist/global-skills/vybekiitReferences/web/agent/goal-routes.json +366 -0
- package/dist/global-skills/vybekiitReferences/web/agent/mcp-agent.json +14 -0
- package/dist/global-skills/vybekiitReferences/web/agent/mcp-firebase.json +8 -0
- package/dist/global-skills/vybekiitReferences/web/agent/mcp-mongodb.json +12 -0
- package/dist/global-skills/vybekiitReferences/web/agent/mcp-neon.json +8 -0
- package/dist/global-skills/vybekiitReferences/web/agent/mcp-paypal.json +8 -0
- package/dist/global-skills/vybekiitReferences/web/agent/mcp-posthog.json +17 -0
- package/dist/global-skills/vybekiitReferences/web/agent/mcp-railway-local.json +8 -0
- package/dist/global-skills/vybekiitReferences/web/agent/mcp-railway-remote.json +7 -0
- package/dist/global-skills/vybekiitReferences/web/agent/mcp-sentry.json +7 -0
- package/dist/global-skills/vybekiitReferences/web/agent/mcp-setup.md +174 -0
- package/dist/global-skills/vybekiitReferences/web/agent/mcp-stripe.json +7 -0
- package/dist/global-skills/vybekiitReferences/web/agent/mcp-supabase.json +7 -0
- package/dist/global-skills/vybekiitReferences/web/agent/mcp-twilio-alpha.json +13 -0
- package/dist/global-skills/vybekiitReferences/web/agent/mcp-twilio-docs.json +7 -0
- package/dist/global-skills/vybekiitReferences/web/agent/session-bootstrap.md +44 -0
- package/dist/global-skills/vybekiitReferences/web/agent/tech-references.md +28 -0
- package/dist/global-skills/vybekiitReferences/web/agent/ui-catalog-index.json +36294 -0
- package/dist/global-skills/vybekiitReferences/web/agent/ui-catalog-index.mobile.json +36294 -0
- package/dist/global-skills/vybekiitReferences/web/agent/ui-sources.md +104 -0
- package/dist/global-skills/vybekiitReferences/web/language.md +348 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/README.md +66 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/ai-vybekiit.md +3 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/analytics-vybekiit.md +20 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/assets-vybekiit.md +31 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/aws-vybekiit.md +25 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/better-auth-vybekiit.md +21 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/browser-automation-vybekiit.md +48 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/ci-vybekiit.md +26 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/client-state-vybekiit.md +25 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/cloudflare-email-vybekiit.md +42 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/cms-vybekiit.md +3 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/code-hygiene-vybekiit.md +38 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/compliance-vybekiit.md +3 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/data-model-vybekiit.md +29 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/db-presets-vybekiit.md +46 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/deploy-cloudflare-vybekiit.md +22 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/deploy-github-pages-vybekiit.md +25 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/deploy-railway-vybekiit.md +26 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/deploy-vercel-vybekiit.md +23 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/env-secrets-vybekiit.md +21 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/extend-capabilities-vybekiit.md +127 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/firebase-vybekiit.md +27 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/format-lint-vybekiit.md +33 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/github-vybekiit.md +47 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/i18n-vybekiit.md +3 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/jobs-vybekiit.md +3 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/kv-vybekiit.md +3 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/lemon-squeezy-vybekiit.md +30 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/mcp-tools-vybekiit.md +87 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/mongodb-vybekiit.md +39 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/neon-vybekiit.md +33 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/nextjs-vybekiit.md +20 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/notifications-vybekiit.md +3 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/observability-vybekiit.md +36 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/paypal-vybekiit.md +25 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/planning-vybekiit.md +59 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/playwright-vybekiit.md +42 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/railway-postgres-vybekiit.md +24 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/react-patterns-vybekiit.md +45 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/realtime-vybekiit.md +3 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/registrar-vybekiit.md +36 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/resend-vybekiit.md +20 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/responsive-vybekiit.md +27 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/search-vybekiit.md +3 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/sentry-vybekiit.md +33 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/seo-vybekiit.md +29 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/ses-vybekiit.md +19 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/shadcn-vybekiit.md +25 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/ship-via-pr-vybekiit.md +49 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/stripe-vybekiit.md +26 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/supabase-vybekiit.md +31 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/tenancy-vybekiit.md +3 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/testing-vybekiit.md +33 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/ui-consistency-vybekiit.md +34 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/ui-routing-vybekiit.md +41 -0
- package/dist/global-skills/watch-my-app/SKILL.md +6 -0
- package/dist/global-skills/web-to-native/SKILL.md +1 -0
- package/dist/global-skills/wire-auth/SKILL.md +6 -0
- package/dist/global-skills/wire-database/SKILL.md +15 -1
- package/dist/global-skills/wire-email/SKILL.md +6 -0
- package/dist/global-skills/wire-payments/SKILL.md +6 -0
- package/dist/global-skills/workers-best-practices/SKILL.md +4 -3
- package/dist/global-skills/workers-best-practices/references/configuration.md +1 -1
- package/dist/global-skills/workers-best-practices/references/platform-apis.md +5 -5
- package/dist/global-skills/workers-best-practices/references/runtime-patterns.md +1 -1
- package/dist/global-skills/wrangler/SKILL.md +27 -24
- package/dist/global-skills/xcode-project-setup/SKILL.md +1 -0
- package/package.json +12 -9
|
@@ -0,0 +1,676 @@
|
|
|
1
|
+
# Client-Side Text-to-Speech (TTS) Generation with Gemini
|
|
2
|
+
|
|
3
|
+
Firebase AI Logic enables client-side Text-to-Speech (TTS) generation directly
|
|
4
|
+
from your mobile and web applications without maintaining custom backend speech
|
|
5
|
+
services. Using specialized Gemini TTS models, apps can synthesize natural,
|
|
6
|
+
expressive audio with custom voice personas, multi-speaker dialogues, and
|
|
7
|
+
in-flight audio streaming.
|
|
8
|
+
|
|
9
|
+
______________________________________________________________________
|
|
10
|
+
|
|
11
|
+
## Supported Models
|
|
12
|
+
|
|
13
|
+
Always specify a dedicated Gemini TTS model when generating audio:
|
|
14
|
+
|
|
15
|
+
| Model ID | Description |
|
|
16
|
+
| :----------------------------- | :--------------------------------------------------------------------------------- |
|
|
17
|
+
| `gemini-3.1-flash-tts-preview` | Low-latency speech synthesis (preview); supports single- and multi-speaker output. |
|
|
18
|
+
|
|
19
|
+
> [!WARNING] Always verify currently supported model availability in the
|
|
20
|
+
> [Firebase AI Logic Models documentation](https://firebase.google.com/docs/ai-logic/models.md.txt).
|
|
21
|
+
|
|
22
|
+
______________________________________________________________________
|
|
23
|
+
|
|
24
|
+
## Core Concepts & Configurations
|
|
25
|
+
|
|
26
|
+
### 1. Response Modality
|
|
27
|
+
|
|
28
|
+
To instruct Gemini to synthesize and return audio, configure
|
|
29
|
+
`responseModalities` in the generation configuration to include audio:
|
|
30
|
+
|
|
31
|
+
- **Swift**: `generationConfig = GenerationConfig(responseModalities: [.audio])`
|
|
32
|
+
- **Kotlin**:
|
|
33
|
+
`generationConfig { responseModalities = listOf(ResponseModality.AUDIO) }`
|
|
34
|
+
- **Web (JS/TS)**: `generationConfig: { responseModalities: ["AUDIO"] }`
|
|
35
|
+
|
|
36
|
+
### 2. Single-Speaker `SpeechConfig`
|
|
37
|
+
|
|
38
|
+
Configure voice persona and optional language code (`languageCode`, e.g.
|
|
39
|
+
`"en-US"`, `"es-ES"`, `"ja-JP"`) using `SpeechConfig`. Voice names map to Google
|
|
40
|
+
prebuilt neural voices (for example: `Puck`, `Charon`, `Kore`, `Fenrir`,
|
|
41
|
+
`Aoede`):
|
|
42
|
+
|
|
43
|
+
- **Swift**:
|
|
44
|
+
```swift
|
|
45
|
+
let speechConfig = SpeechConfig(
|
|
46
|
+
voiceConfig: VoiceConfig(
|
|
47
|
+
prebuiltVoiceConfig: PrebuiltVoiceConfig(voiceName: "Puck")
|
|
48
|
+
),
|
|
49
|
+
languageCode: "en-US"
|
|
50
|
+
)
|
|
51
|
+
```
|
|
52
|
+
- **Kotlin**:
|
|
53
|
+
```kotlin
|
|
54
|
+
val speechConfig = speechConfig {
|
|
55
|
+
voiceConfig = voiceConfig {
|
|
56
|
+
prebuiltVoiceConfig = prebuiltVoiceConfig {
|
|
57
|
+
voiceName = "Puck"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
languageCode = "en-US"
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
- **Web**:
|
|
64
|
+
```javascript
|
|
65
|
+
speechConfig: {
|
|
66
|
+
voiceConfig: {
|
|
67
|
+
prebuiltVoiceConfig: {
|
|
68
|
+
voiceName: "Puck"
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
languageCode: "en-US"
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 3. Multi-Speaker `MultiSpeakerVoiceConfig` (2-Speaker Dialogues)
|
|
76
|
+
|
|
77
|
+
For scripts with two distinct characters or roles (e.g., host and guest,
|
|
78
|
+
narrator and character), configure `MultiSpeakerVoiceConfig` with
|
|
79
|
+
`speakerVoiceConfigs` linking speaker names to specific voices:
|
|
80
|
+
|
|
81
|
+
- **Swift**:
|
|
82
|
+
```swift
|
|
83
|
+
let multiSpeakerConfig = SpeechConfig(
|
|
84
|
+
multiSpeakerVoiceConfig: MultiSpeakerVoiceConfig(
|
|
85
|
+
speakerVoiceConfigs: [
|
|
86
|
+
SpeakerVoiceConfig(
|
|
87
|
+
speaker: "Host",
|
|
88
|
+
voiceConfig: VoiceConfig(
|
|
89
|
+
prebuiltVoiceConfig: PrebuiltVoiceConfig(voiceName: "Puck")
|
|
90
|
+
)
|
|
91
|
+
),
|
|
92
|
+
SpeakerVoiceConfig(
|
|
93
|
+
speaker: "Guest",
|
|
94
|
+
voiceConfig: VoiceConfig(
|
|
95
|
+
prebuiltVoiceConfig: PrebuiltVoiceConfig(voiceName: "Aoede")
|
|
96
|
+
)
|
|
97
|
+
)
|
|
98
|
+
]
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
```
|
|
102
|
+
- **Kotlin**:
|
|
103
|
+
```kotlin
|
|
104
|
+
val multiSpeakerConfig = speechConfig {
|
|
105
|
+
multiSpeakerVoiceConfig = multiSpeakerVoiceConfig {
|
|
106
|
+
speakerVoiceConfigs = listOf(
|
|
107
|
+
speakerVoiceConfig {
|
|
108
|
+
speaker = "Host"
|
|
109
|
+
voiceConfig = voiceConfig {
|
|
110
|
+
prebuiltVoiceConfig = prebuiltVoiceConfig { voiceName = "Puck" }
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
speakerVoiceConfig {
|
|
114
|
+
speaker = "Guest"
|
|
115
|
+
voiceConfig = voiceConfig {
|
|
116
|
+
prebuiltVoiceConfig = prebuiltVoiceConfig { voiceName = "Aoede" }
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
- **Web**:
|
|
124
|
+
```javascript
|
|
125
|
+
speechConfig: {
|
|
126
|
+
multiSpeakerVoiceConfig: {
|
|
127
|
+
speakerVoiceConfigs: [
|
|
128
|
+
{
|
|
129
|
+
speaker: "Host",
|
|
130
|
+
voiceConfig: { prebuiltVoiceConfig: { voiceName: "Puck" } }
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
speaker: "Guest",
|
|
134
|
+
voiceConfig: { prebuiltVoiceConfig: { voiceName: "Aoede" } }
|
|
135
|
+
}
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
______________________________________________________________________
|
|
142
|
+
|
|
143
|
+
## Audio Directives and Emotional Tags
|
|
144
|
+
|
|
145
|
+
Gemini TTS models accept natural-language directives and inline tags in prompts
|
|
146
|
+
to control tone, cadence, pacing, and emotional expression.
|
|
147
|
+
|
|
148
|
+
### Directives
|
|
149
|
+
|
|
150
|
+
Include directives at the start of your prompt to set the overall tone and
|
|
151
|
+
scene:
|
|
152
|
+
|
|
153
|
+
- `[Audio Profile: Warm, conversational podcast co-host with energetic delivery]`
|
|
154
|
+
- `[Scene: Quiet bedtime story in a calm room]`
|
|
155
|
+
- `[Director's Note: Speak deliberately, pausing for reflection after major questions]`
|
|
156
|
+
|
|
157
|
+
### Emotional Tags & Delivery Cues
|
|
158
|
+
|
|
159
|
+
Embed delivery markers inline within dialogue or narration:
|
|
160
|
+
|
|
161
|
+
- `[whispers]` — Soft, confidential whisper.
|
|
162
|
+
- `[laughs]` / `[chuckles]` — Lighthearted laughter during delivery.
|
|
163
|
+
- `[sighs]` — Expressive exhale.
|
|
164
|
+
- `[slowly]` — Reduced tempo for emphasis or dramatic effect.
|
|
165
|
+
- `[excited]` — Higher energy and faster pacing.
|
|
166
|
+
- `[pause]` — Deliberate silence.
|
|
167
|
+
|
|
168
|
+
### Multi-Speaker Prompt Example
|
|
169
|
+
|
|
170
|
+
```text
|
|
171
|
+
[Scene: A lively coffee-shop tech discussion]
|
|
172
|
+
[Director's Note: Host is upbeat and curious; Guest is thoughtful and explanatory]
|
|
173
|
+
|
|
174
|
+
Host: Welcome back! [excited] Today we are diving into client-side audio.
|
|
175
|
+
Guest: [laughs] It's about time! No servers in between means virtually zero latency.
|
|
176
|
+
Host: [whispers] Tell us the secret... how does it stream?
|
|
177
|
+
Guest: [slowly] Pure linear PCM, chunk by chunk, directly to your device's audio engine.
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
______________________________________________________________________
|
|
181
|
+
|
|
182
|
+
## Audio Decoding and Playback
|
|
183
|
+
|
|
184
|
+
### Format Specifications
|
|
185
|
+
|
|
186
|
+
Gemini TTS audio format depends on whether the request is unary or streaming:
|
|
187
|
+
|
|
188
|
+
- **Unary Requests (`generateContent`)**: Returns complete **WAV (`audio/wav`)**
|
|
189
|
+
audio with a standard 44-byte RIFF header included by default (24 kHz, mono,
|
|
190
|
+
16-bit signed little-endian PCM). These bytes can be passed directly to
|
|
191
|
+
standard media players (`AVAudioPlayer`, `MediaPlayer`, `<audio>`).
|
|
192
|
+
|
|
193
|
+
- **Streaming Requests (`generateContentStream`)**: Returns headerless raw
|
|
194
|
+
**Linear PCM (`audio/l16; rate=24000; channels=1`)** chunks (24 kHz, mono,
|
|
195
|
+
16-bit signed little-endian PCM) so chunks can be streamed or concatenated
|
|
196
|
+
continuously without per-chunk container headers.
|
|
197
|
+
|
|
198
|
+
- **Audio Parameters**:
|
|
199
|
+
|
|
200
|
+
- **Sample Rate**: 24,000 Hz (24 kHz)
|
|
201
|
+
- **Channels**: 1 channel (mono)
|
|
202
|
+
- **Bit Depth**: 16-bit linear PCM (little-endian signed integer)
|
|
203
|
+
- **MIME Types**: `audio/l16` (streaming chunks) or `audio/wav` /
|
|
204
|
+
`audio/x-wav` (unary)
|
|
205
|
+
|
|
206
|
+
### Playback Approaches
|
|
207
|
+
|
|
208
|
+
1. **Direct Low-Latency PCM Streaming**:
|
|
209
|
+
- Stream raw PCM chunks directly into low-level audio renderers without
|
|
210
|
+
waiting for the full response:
|
|
211
|
+
- **iOS**: `AVAudioEngine` + `AVAudioPlayerNode` scheduling
|
|
212
|
+
`AVAudioPCMBuffer`.
|
|
213
|
+
- **Android**: `AudioTrack` configured with `ENCODING_PCM_16BIT` and
|
|
214
|
+
`CHANNEL_OUT_MONO`.
|
|
215
|
+
- **Web**: Web Audio API (`AudioContext`) scheduling PCM chunks into
|
|
216
|
+
`AudioBufferSourceNode`.
|
|
217
|
+
1. **WAV Container Format**:
|
|
218
|
+
- Standard media players (`AVAudioPlayer` on iOS, `MediaPlayer` on Android,
|
|
219
|
+
`<audio>` on Web) expect a container header (RIFF/WAV).
|
|
220
|
+
- For unary responses, the RIFF header is already present. For assembled
|
|
221
|
+
streaming PCM chunks, prepend a standard **44-byte RIFF header** to make
|
|
222
|
+
the complete audio immediately playable or saveable as `.wav`.
|
|
223
|
+
|
|
224
|
+
#### Standard 44-Byte WAV (RIFF) Header Construction
|
|
225
|
+
|
|
226
|
+
```
|
|
227
|
+
Offset Size Field Value
|
|
228
|
+
0 4 ChunkID "RIFF"
|
|
229
|
+
4 4 ChunkSize 36 + Subchunk2Size (file size - 8)
|
|
230
|
+
8 4 Format "WAVE"
|
|
231
|
+
12 4 Subchunk1ID "fmt "
|
|
232
|
+
16 4 Subchunk1Size 16 (for PCM)
|
|
233
|
+
20 2 AudioFormat 1 (PCM linear)
|
|
234
|
+
22 2 NumChannels 1 (Mono)
|
|
235
|
+
24 4 SampleRate 24000
|
|
236
|
+
28 4 ByteRate 48000 (SampleRate * NumChannels * BitsPerSample / 8)
|
|
237
|
+
32 2 BlockAlign 2 (NumChannels * BitsPerSample / 8)
|
|
238
|
+
34 2 BitsPerSample 16
|
|
239
|
+
36 4 Subchunk2ID "data"
|
|
240
|
+
40 4 Subchunk2Size Number of PCM bytes
|
|
241
|
+
44+ N Data Raw PCM bytes
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
______________________________________________________________________
|
|
245
|
+
|
|
246
|
+
## Platform Code Snippets
|
|
247
|
+
|
|
248
|
+
### Swift (iOS)
|
|
249
|
+
|
|
250
|
+
#### 1. Streaming Playback with `AVAudioEngine`
|
|
251
|
+
|
|
252
|
+
```swift
|
|
253
|
+
import AVFoundation
|
|
254
|
+
import FirebaseAILogic
|
|
255
|
+
|
|
256
|
+
@MainActor
|
|
257
|
+
final class SpeechStreamingManager {
|
|
258
|
+
private let audioEngine = AVAudioEngine()
|
|
259
|
+
private let playerNode = AVAudioPlayerNode()
|
|
260
|
+
|
|
261
|
+
// 24 kHz, 16-bit Mono PCM
|
|
262
|
+
private let audioFormat = AVAudioFormat(
|
|
263
|
+
commonFormat: .pcmFormatInt16,
|
|
264
|
+
sampleRate: 24000,
|
|
265
|
+
channels: 1,
|
|
266
|
+
interleaved: false
|
|
267
|
+
)!
|
|
268
|
+
|
|
269
|
+
init() {
|
|
270
|
+
audioEngine.attach(playerNode)
|
|
271
|
+
audioEngine.connect(playerNode, to: audioEngine.mainMixerNode, format: audioFormat)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
func streamSpeech(prompt: String) async throws {
|
|
275
|
+
let ai = FirebaseAI.firebaseAI()
|
|
276
|
+
|
|
277
|
+
let speechConfig = SpeechConfig(
|
|
278
|
+
voiceConfig: VoiceConfig(
|
|
279
|
+
prebuiltVoiceConfig: PrebuiltVoiceConfig(voiceName: "Puck")
|
|
280
|
+
)
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
let genConfig = GenerationConfig(
|
|
284
|
+
responseModalities: [.audio],
|
|
285
|
+
speechConfig: speechConfig
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
let model = ai.generativeModel(
|
|
289
|
+
modelName: "gemini-3.1-flash-tts-preview",
|
|
290
|
+
generationConfig: genConfig
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
if !audioEngine.isRunning {
|
|
294
|
+
try audioEngine.start()
|
|
295
|
+
}
|
|
296
|
+
playerNode.play()
|
|
297
|
+
|
|
298
|
+
let stream = model.generateContentStream(prompt)
|
|
299
|
+
for try await chunk in stream {
|
|
300
|
+
for part in chunk.candidates.first?.content.parts ?? [] {
|
|
301
|
+
if let inlineData = part as? InlineDataPart,
|
|
302
|
+
inlineData.mimeType.contains("audio/pcm") || inlineData.mimeType.contains("audio/l16") {
|
|
303
|
+
schedulePCMBuffer(data: inlineData.data)
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
private func schedulePCMBuffer(data: Data) {
|
|
310
|
+
let frameCount = UInt32(data.count) / audioFormat.streamDescription.pointee.mBytesPerFrame
|
|
311
|
+
guard frameCount > 0,
|
|
312
|
+
let buffer = AVAudioPCMBuffer(pcmFormat: audioFormat, frameCapacity: frameCount) else {
|
|
313
|
+
return
|
|
314
|
+
}
|
|
315
|
+
buffer.frameLength = frameCount
|
|
316
|
+
|
|
317
|
+
data.withUnsafeBytes { rawBufferPointer in
|
|
318
|
+
guard let src = rawBufferPointer.baseAddress else { return }
|
|
319
|
+
if let dest = buffer.int16ChannelData?[0] {
|
|
320
|
+
UnsafeMutableRawPointer(dest).copyMemory(from: src, byteCount: data.count)
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
playerNode.scheduleBuffer(buffer)
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
func stop() {
|
|
328
|
+
playerNode.stop()
|
|
329
|
+
audioEngine.stop()
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
#### 2. WAV Converter for `AVAudioPlayer`
|
|
335
|
+
|
|
336
|
+
```swift
|
|
337
|
+
func addWavHeader(to pcmData: Data, sampleRate: Int = 24000, channels: Int = 1, bitDepth: Int = 16) -> Data {
|
|
338
|
+
var header = Data()
|
|
339
|
+
let byteRate = sampleRate * channels * bitDepth / 8
|
|
340
|
+
let blockAlign = channels * bitDepth / 8
|
|
341
|
+
let totalDataLen = Int32(pcmData.count)
|
|
342
|
+
let totalLength = totalDataLen + 36
|
|
343
|
+
|
|
344
|
+
header.append(contentsOf: "RIFF".utf8)
|
|
345
|
+
header.append(Data(from: totalLength.littleEndian))
|
|
346
|
+
header.append(contentsOf: "WAVE".utf8)
|
|
347
|
+
header.append(contentsOf: "fmt ".utf8)
|
|
348
|
+
header.append(Data(from: Int32(16).littleEndian)) // Subchunk1Size for PCM
|
|
349
|
+
header.append(Data(from: Int16(1).littleEndian)) // AudioFormat 1 = PCM
|
|
350
|
+
header.append(Data(from: Int16(channels).littleEndian))
|
|
351
|
+
header.append(Data(from: Int32(sampleRate).littleEndian))
|
|
352
|
+
header.append(Data(from: Int32(byteRate).littleEndian))
|
|
353
|
+
header.append(Data(from: Int16(blockAlign).littleEndian))
|
|
354
|
+
header.append(Data(from: Int16(bitDepth).littleEndian))
|
|
355
|
+
header.append(contentsOf: "data".utf8)
|
|
356
|
+
header.append(Data(from: totalDataLen.littleEndian))
|
|
357
|
+
|
|
358
|
+
return header + pcmData
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
private extension Data {
|
|
362
|
+
init<T>(from value: T) {
|
|
363
|
+
var val = value
|
|
364
|
+
self = Swift.withUnsafeBytes(of: &val) { Data($0) }
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
______________________________________________________________________
|
|
370
|
+
|
|
371
|
+
### Kotlin (Android)
|
|
372
|
+
|
|
373
|
+
#### Streaming Playback with `AudioTrack`
|
|
374
|
+
|
|
375
|
+
```kotlin
|
|
376
|
+
import android.media.AudioAttributes
|
|
377
|
+
import android.media.AudioFormat
|
|
378
|
+
import android.media.AudioTrack
|
|
379
|
+
import com.google.firebase.ai.FirebaseAI
|
|
380
|
+
import com.google.firebase.ai.type.ResponseModality
|
|
381
|
+
import com.google.firebase.ai.type.generationConfig
|
|
382
|
+
import com.google.firebase.ai.type.prebuiltVoiceConfig
|
|
383
|
+
import com.google.firebase.ai.type.speechConfig
|
|
384
|
+
import com.google.firebase.ai.type.voiceConfig
|
|
385
|
+
import kotlinx.coroutines.Dispatchers
|
|
386
|
+
import kotlinx.coroutines.withContext
|
|
387
|
+
|
|
388
|
+
class SpeechStreamingManager {
|
|
389
|
+
private val sampleRate = 24000
|
|
390
|
+
private val minBufferSize = AudioTrack.getMinBufferSize(
|
|
391
|
+
sampleRate,
|
|
392
|
+
AudioFormat.CHANNEL_OUT_MONO,
|
|
393
|
+
AudioFormat.ENCODING_PCM_16BIT
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
private val audioTrack = AudioTrack.Builder()
|
|
397
|
+
.setAudioAttributes(
|
|
398
|
+
AudioAttributes.Builder()
|
|
399
|
+
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
|
|
400
|
+
.setUsage(AudioAttributes.USAGE_MEDIA)
|
|
401
|
+
.build()
|
|
402
|
+
)
|
|
403
|
+
.setAudioFormat(
|
|
404
|
+
AudioFormat.Builder()
|
|
405
|
+
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
|
|
406
|
+
.setSampleRate(sampleRate)
|
|
407
|
+
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
|
|
408
|
+
.build()
|
|
409
|
+
)
|
|
410
|
+
.setBufferSizeInBytes(minBufferSize * 2)
|
|
411
|
+
.setTransferMode(AudioTrack.MODE_STREAM)
|
|
412
|
+
.build()
|
|
413
|
+
|
|
414
|
+
suspend fun streamSpeech(prompt: String) = withContext(Dispatchers.IO) {
|
|
415
|
+
val config = generationConfig {
|
|
416
|
+
responseModalities = listOf(ResponseModality.AUDIO)
|
|
417
|
+
speechConfig = speechConfig {
|
|
418
|
+
voiceConfig = voiceConfig {
|
|
419
|
+
prebuiltVoiceConfig = prebuiltVoiceConfig {
|
|
420
|
+
voiceName = "Puck"
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
val generativeModel = FirebaseAI.getInstance().getGenerativeModel(
|
|
427
|
+
modelName = "gemini-3.1-flash-tts-preview",
|
|
428
|
+
generationConfig = config
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
audioTrack.play()
|
|
432
|
+
try {
|
|
433
|
+
val responseStream = generativeModel.generateContentStream(prompt)
|
|
434
|
+
responseStream.collect { chunk ->
|
|
435
|
+
chunk.candidates.firstOrNull()?.content?.parts?.forEach { part ->
|
|
436
|
+
if (part is com.google.firebase.ai.type.InlineDataPart) {
|
|
437
|
+
val audioBytes = part.data
|
|
438
|
+
audioTrack.write(audioBytes, 0, audioBytes.size)
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
} finally {
|
|
443
|
+
audioTrack.stop()
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
fun release() {
|
|
448
|
+
audioTrack.release()
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
#### 2. Convert PCM to WAV for `MediaPlayer`
|
|
454
|
+
|
|
455
|
+
```kotlin
|
|
456
|
+
import android.content.Context
|
|
457
|
+
import android.media.MediaPlayer
|
|
458
|
+
import java.io.File
|
|
459
|
+
import java.io.FileOutputStream
|
|
460
|
+
import java.nio.ByteBuffer
|
|
461
|
+
import java.nio.ByteOrder
|
|
462
|
+
|
|
463
|
+
fun addWavHeader(
|
|
464
|
+
pcmBytes: ByteArray,
|
|
465
|
+
sampleRate: Int = 24000,
|
|
466
|
+
channels: Short = 1,
|
|
467
|
+
bitDepth: Short = 16
|
|
468
|
+
): ByteArray {
|
|
469
|
+
val totalDataLen = pcmBytes.size
|
|
470
|
+
val totalLength = totalDataLen + 36
|
|
471
|
+
val byteRate = sampleRate * channels * bitDepth / 8
|
|
472
|
+
val blockAlign = (channels * bitDepth / 8).toShort()
|
|
473
|
+
|
|
474
|
+
val header = ByteBuffer.allocate(44).order(ByteOrder.LITTLE_ENDIAN).apply {
|
|
475
|
+
put("RIFF".toByteArray())
|
|
476
|
+
putInt(totalLength)
|
|
477
|
+
put("WAVE".toByteArray())
|
|
478
|
+
put("fmt ".toByteArray())
|
|
479
|
+
putInt(16) // Subchunk1Size
|
|
480
|
+
putShort(1) // AudioFormat 1 = PCM
|
|
481
|
+
putShort(channels)
|
|
482
|
+
putInt(sampleRate)
|
|
483
|
+
putInt(byteRate)
|
|
484
|
+
putShort(blockAlign)
|
|
485
|
+
putShort(bitDepth)
|
|
486
|
+
put("data".toByteArray())
|
|
487
|
+
putInt(totalDataLen)
|
|
488
|
+
}.array()
|
|
489
|
+
|
|
490
|
+
return header + pcmBytes
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
fun playWavAudio(context: Context, wavBytes: ByteArray) {
|
|
494
|
+
val tempFile = File.createTempFile("tts_", ".wav", context.cacheDir).apply {
|
|
495
|
+
deleteOnExit()
|
|
496
|
+
FileOutputStream(this).use { it.write(wavBytes) }
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
val mediaPlayer = MediaPlayer()
|
|
500
|
+
try {
|
|
501
|
+
mediaPlayer.setDataSource(tempFile.absolutePath)
|
|
502
|
+
mediaPlayer.prepare()
|
|
503
|
+
mediaPlayer.start()
|
|
504
|
+
mediaPlayer.setOnCompletionListener {
|
|
505
|
+
it.release()
|
|
506
|
+
tempFile.delete()
|
|
507
|
+
}
|
|
508
|
+
} catch (e: Exception) {
|
|
509
|
+
mediaPlayer.release()
|
|
510
|
+
tempFile.delete()
|
|
511
|
+
throw e
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
______________________________________________________________________
|
|
517
|
+
|
|
518
|
+
### Web (JavaScript/TypeScript)
|
|
519
|
+
|
|
520
|
+
#### 1. Streaming Playback with Web Audio API
|
|
521
|
+
|
|
522
|
+
```typescript
|
|
523
|
+
import { initializeApp } from "firebase/app";
|
|
524
|
+
import { getAI, getGenerativeModel, GoogleAIBackend } from "firebase/ai";
|
|
525
|
+
|
|
526
|
+
const firebaseConfig = {
|
|
527
|
+
// your firebase configuration
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
const app = initializeApp(firebaseConfig);
|
|
531
|
+
const ai = getAI(app, { backend: new GoogleAIBackend() });
|
|
532
|
+
|
|
533
|
+
const model = getGenerativeModel(ai, {
|
|
534
|
+
model: "gemini-3.1-flash-tts-preview",
|
|
535
|
+
generationConfig: {
|
|
536
|
+
responseModalities: ["AUDIO"],
|
|
537
|
+
speechConfig: {
|
|
538
|
+
voiceConfig: {
|
|
539
|
+
prebuiltVoiceConfig: {
|
|
540
|
+
voiceName: "Puck",
|
|
541
|
+
},
|
|
542
|
+
},
|
|
543
|
+
},
|
|
544
|
+
},
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
export class WebSpeechPlayer {
|
|
548
|
+
private audioCtx: AudioContext | null = null;
|
|
549
|
+
private nextStartTime: number = 0;
|
|
550
|
+
|
|
551
|
+
private getAudioContext(): AudioContext {
|
|
552
|
+
if (!this.audioCtx) {
|
|
553
|
+
if (typeof window === "undefined") {
|
|
554
|
+
throw new Error("AudioContext is only available in browser environments.");
|
|
555
|
+
}
|
|
556
|
+
const AudioContextClass =
|
|
557
|
+
window.AudioContext ||
|
|
558
|
+
(window as unknown as { webkitAudioContext: typeof AudioContext })
|
|
559
|
+
.webkitAudioContext;
|
|
560
|
+
this.audioCtx = new AudioContextClass({ sampleRate: 24000 });
|
|
561
|
+
}
|
|
562
|
+
return this.audioCtx;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
async playSpeechStream(prompt: string) {
|
|
566
|
+
const ctx = this.getAudioContext();
|
|
567
|
+
if (ctx.state === "suspended") {
|
|
568
|
+
await ctx.resume();
|
|
569
|
+
}
|
|
570
|
+
this.nextStartTime = ctx.currentTime;
|
|
571
|
+
|
|
572
|
+
const responseStream = await model.generateContentStream(prompt);
|
|
573
|
+
|
|
574
|
+
for await (const chunk of responseStream.stream) {
|
|
575
|
+
const candidates = chunk.candidates || [];
|
|
576
|
+
for (const candidate of candidates) {
|
|
577
|
+
for (const part of candidate.content?.parts || []) {
|
|
578
|
+
if ("inlineData" in part && part.inlineData?.data) {
|
|
579
|
+
const rawPcm = this.base64ToArrayBuffer(part.inlineData.data);
|
|
580
|
+
this.queuePcmChunk(rawPcm, ctx);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
private queuePcmChunk(pcmData: ArrayBuffer, ctx: AudioContext) {
|
|
588
|
+
const int16Array = new Int16Array(pcmData);
|
|
589
|
+
const float32Array = new Float32Array(int16Array.length);
|
|
590
|
+
|
|
591
|
+
// Convert 16-bit PCM integer samples to -1.0 .. 1.0 float samples
|
|
592
|
+
for (let i = 0; i < int16Array.length; i++) {
|
|
593
|
+
float32Array[i] = int16Array[i] / 32768.0;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const audioBuffer = ctx.createBuffer(1, float32Array.length, 24000);
|
|
597
|
+
audioBuffer.copyToChannel(float32Array, 0);
|
|
598
|
+
|
|
599
|
+
const source = ctx.createBufferSource();
|
|
600
|
+
source.buffer = audioBuffer;
|
|
601
|
+
source.connect(ctx.destination);
|
|
602
|
+
|
|
603
|
+
const startTime = Math.max(this.nextStartTime, ctx.currentTime);
|
|
604
|
+
source.start(startTime);
|
|
605
|
+
this.nextStartTime = startTime + audioBuffer.duration;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
private base64ToArrayBuffer(base64: string): ArrayBuffer {
|
|
609
|
+
const binaryString = window.atob(base64);
|
|
610
|
+
const len = binaryString.length;
|
|
611
|
+
const bytes = new Uint8Array(len);
|
|
612
|
+
for (let i = 0; i < len; i++) {
|
|
613
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
614
|
+
}
|
|
615
|
+
return bytes.buffer;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
#### 2. Convert PCM Base64 to Playable WAV Blob for `<audio>` Elements
|
|
621
|
+
|
|
622
|
+
```typescript
|
|
623
|
+
export function pcmToWavBlob(pcmBytes: Uint8Array, sampleRate = 24000): Blob {
|
|
624
|
+
const header = new ArrayBuffer(44);
|
|
625
|
+
const view = new DataView(header);
|
|
626
|
+
|
|
627
|
+
const numChannels = 1;
|
|
628
|
+
const bitsPerSample = 16;
|
|
629
|
+
const byteRate = (sampleRate * numChannels * bitsPerSample) / 8;
|
|
630
|
+
const blockAlign = (numChannels * bitsPerSample) / 8;
|
|
631
|
+
const dataSize = pcmBytes.byteLength;
|
|
632
|
+
|
|
633
|
+
// RIFF identifier
|
|
634
|
+
writeString(view, 0, "RIFF");
|
|
635
|
+
view.setUint32(4, 36 + dataSize, true);
|
|
636
|
+
writeString(view, 8, "WAVE");
|
|
637
|
+
|
|
638
|
+
// fmt subchunk
|
|
639
|
+
writeString(view, 12, "fmt ");
|
|
640
|
+
view.setUint32(16, 16, true); // Subchunk1Size
|
|
641
|
+
view.setUint16(20, 1, true); // AudioFormat (PCM = 1)
|
|
642
|
+
view.setUint16(22, numChannels, true);
|
|
643
|
+
view.setUint32(24, sampleRate, true);
|
|
644
|
+
view.setUint32(28, byteRate, true);
|
|
645
|
+
view.setUint16(32, blockAlign, true);
|
|
646
|
+
view.setUint16(34, bitsPerSample, true);
|
|
647
|
+
|
|
648
|
+
// data subchunk
|
|
649
|
+
writeString(view, 36, "data");
|
|
650
|
+
view.setUint32(40, dataSize, true);
|
|
651
|
+
|
|
652
|
+
return new Blob([header, pcmBytes], { type: "audio/wav" });
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
function writeString(view: DataView, offset: number, string: string) {
|
|
656
|
+
for (let i = 0; i < string.length; i++) {
|
|
657
|
+
view.setUint8(offset + i, string.charCodeAt(i));
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
______________________________________________________________________
|
|
663
|
+
|
|
664
|
+
## Best Practices & Troubleshooting
|
|
665
|
+
|
|
666
|
+
- **Audio Session & Permissions**: On iOS and Android, make sure the app's audio
|
|
667
|
+
session/category is configured to allow playback (e.g.,
|
|
668
|
+
`AVAudioSessionCategoryPlayback`).
|
|
669
|
+
- **Buffering & Jitter Prevention**: When streaming audio chunks via
|
|
670
|
+
`generateContentStream`, queue buffers sequentially with exact timestamps or
|
|
671
|
+
frame offsets to avoid audio stutter or gaps.
|
|
672
|
+
- **Model Compatibility**: TTS features require Gemini models with dedicated
|
|
673
|
+
audio synthesis capabilities (`gemini-3.1-flash-tts-preview`). Do not use
|
|
674
|
+
standard text-only model IDs.
|
|
675
|
+
- **App Check Protection**: Generating audio consumes quota. Protect your API
|
|
676
|
+
endpoints by enforcing App Check on every client build.
|
|
@@ -4,6 +4,7 @@ description: >-
|
|
|
4
4
|
Deploys and manages full-stack web applications (Next.js, Angular) with Server-Side Rendering (SSR) using Firebase App Hosting. Use when deploying Next.js/Angular apps, configuring apphosting.yaml or firebase.json apphosting blocks, managing secrets, setting up GitHub CI/CD, or configuring Blaze billing requirements. Don't use for classic static web hosting, Auth, Firestore, Crashlytics, or Xcode.
|
|
5
5
|
metadata:
|
|
6
6
|
category: Serverless
|
|
7
|
+
disable-model-invocation: true
|
|
7
8
|
---
|
|
8
9
|
|
|
9
10
|
# App Hosting Basics
|