Customization
How to customize pricing, AI settings, theme, locales, and app pages.
Modify Products and Pricing
Edit constants/billing.ts.
The starter currently ships with a lifetime one-time pack enabled in oneTimePacks. Subscription plan keys are already typed, but subscriptionPlans is empty until you add real plans and Creem product IDs.
export const oneTimePacks = {
lifetime: {
key: "lifetime",
kind: "one_time",
priceCents: 9900,
originalPriceCents: 15900,
currency: "usd",
credits: 0,
planKeyToGrant: "lifetime",
creemPriceId: "prod_xxx",
},
};When enabling subscriptions, add entries to subscriptionPlans and keep the key aligned with the PlanKey union in the same file. Annual plans can use grantSchedule for installment credit grants.
Modify Credit Costs
Generation costs are centralized in modules/ai/costs.ts:
| Feature | Constant | Current cost |
|---|---|---|
| Chat | CHAT_CREDIT_COST | 10 |
| Image generation | IMAGE_GENERATION_CREDIT_COST | 20 |
| Video generation | VIDEO_GENERATION_CREDIT_COST | 50 |
The server workflows in features/demo/server/* import these constants, deduct credits through modules/credits, and use refund compensation when provider work fails.
Replace AI Provider
The current provider adapter lives in extensions/ai/volcano-engine/.
To switch from Volcano Engine to OpenAI, Anthropic, or another provider:
- Add a new adapter under
extensions/ai/your-provider/ - Match the small surface used by
features/demo/server/chat.ts,features/demo/server/image-generation.ts,features/demo/server/video-generation.ts, andfeatures/demo/server/video-status.ts - Update the imports in those server workflows
- Update environment variables in
.env.exampleand deployment settings - Preserve the
createCreditCompensation(...)refund pattern after credit deduction
Add a New Language
- Copy
messages/en.jsontomessages/XX.json - Copy
messages/seo.en.jsontomessages/seo.XX.json - Translate both files
- Add the locale to
i18n.config.ts
export const locales = ['en', 'zh', 'XX'] as const;- Add matching docs MDX files in
content/docs, such asquickstart.XX.mdx - Update docs locale labels in
features/docs/localization.tsand Fumadocs i18n if the docs site should expose the new locale
proxy.ts and lib/i18n.ts read from i18n.config.ts, so most locale routing follows that shared config.
Customize Theme
Colors are defined in app/globals.css using CSS custom properties:
:root {
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--primary: 0 0% 9%;
/* ... */
}Dark mode variables are in the .dark class.
Fumadocs CSS is handled separately. scripts/sync-fumadocs-style.mjs syncs the generated stylesheet to public/fumadocs-style.css, and features/docs/styles.tsx loads it only for docs routes.
Add New Protected Pages
- Create a page under
app/[locale]/(protected)/your-page/page.tsx - Compose the page from
features/user-console/pagesor create a new feature-owned page component - Put server queries or mutations in the matching
features/*/servermodule - Add navigation keys in
features/navigation/config.ts - Add user-facing labels in both
messages/en.jsonandmessages/zh.json
The protected layout calls getActiveSessionUser() from modules/auth, so pages in this route group require an authenticated user.