| name | vocalize |
| description | Build Vocalize — a multi-platform content tone converter that rewrites content for 小红书, 公众号, LinkedIn, Medium, and X/Twitter using AI. Use when the user asks to build or extend a content repurposing tool, tone converter, or multi-platform writing assistant. |
Vocalize — Multi-Platform Content Tone Converter
What it does
Takes one piece of content and rewrites it in the authentic voice of each platform:
| Platform | Style |
|---|
| 小红书 (Xiaohongshu) | Lively, emoji-rich, Gen-Z Chinese |
| 公众号 (WeChat Official Account) | Thoughtful, narrative essay-style Chinese |
| LinkedIn | Professional, credibility-building English |
| Medium | Conversational English storytelling |
| X/Twitter | Punchy, under 280 chars, bold takes |
Stack
- Frontend: React + Vite (
artifacts/tone-converter)
- Backend: Express 5 (
artifacts/api-server)
- AI: Replit OpenAI integration (
@workspace/integrations-openai-ai-server) — no API key needed
- DB: PostgreSQL + Drizzle ORM (
lib/db) — saves conversions server-side
- History: localStorage on the frontend — each user's history is private and local
- API contract: OpenAPI → Orval-generated hooks in
lib/api-client-react
Key Features
1. Tone Sliders
Two sliders (0–100): Formality and Boldness. Injected at the end of every platform prompt:
function buildToneInstruction(formality: number, boldness: number): string {
}
2. Per-Platform Refinement Chat
Each platform card has an inline chat (MessageSquarePlus icon). Sends POST /api/refine with:
{ "platform": "xiaohongshu", "currentContent": "...", "userMessage": "...", "conversationHistory": [...] }
Multi-turn: conversationHistory is an array of { role, content } pairs maintained in component state.
3. Local History (localStorage)
Hook: artifacts/tone-converter/src/hooks/use-local-history.ts
- Key:
vocalize_history, max 20 records
- Each record includes full
results for restoration (click history item → restores all tabs)
- No server read on load — each user sees only their own history
- Delete removes from localStorage, no API call needed
4. Tab-based Results
Output shown in <Tabs> — one tab per platform. Clicking a history item restores results AND active tab.
Critical Prompt Rules
Apply these to ALL platform prompts without exception:
- No markdown — never use
**, *, #, or any markdown syntax
- English-only for LinkedIn / Medium / X (Twitter) — never mix in Chinese
- Chinese-only for 小红书 / 公众号 — never mix in English
- X/Twitter: hard cap at 280 characters — enforce in prompt, check in frontend
API Endpoints
| Method | Path | Purpose |
|---|
| POST | /api/convert | Convert content. Body: { content, platforms[], formality, boldness } |
| GET | /api/history | Server-side history (not used by frontend — frontend uses localStorage) |
| DELETE | /api/history/:id | Delete a server-side record |
| POST | /api/refine | Refine one platform's output via chat |
| GET | /api/healthz | Health check |
Important Gotchas
SiLinkedin from react-icons is unavailable — use Linkedin from lucide-react instead
- API client is hand-maintained in
lib/api-client-react/src/generated/ (not auto-generated by orval for this project)
- Use
gpt-4o (or the latest available model via Replit AI Integrations) with max_completion_tokens: 8192
- Never call service ports directly; always go through the shared proxy at
localhost:80
- Use
@workspace/api-client-react package root — never deep import paths
File Map
artifacts/
tone-converter/src/
pages/Home.tsx — main page, orchestrates state
components/PlatformCard.tsx — output card + refinement chat
components/HistorySidebar.tsx — collapsible sidebar, local history
hooks/use-local-history.ts — localStorage history hook
lib/platforms.ts — platform definitions (id, name, icon, color)
api-server/src/
routes/convert/index.ts — POST /api/convert + POST /api/refine
lib/
db/src/schema/conversions.ts — Drizzle schema
api-client-react/src/generated/ — hand-maintained API client + types
api-spec/openapi.yaml — OpenAPI contract
Sharing / Multi-user
Because history lives in localStorage, each user who opens the app sees only their own conversions — no login required, no history mixing. Trade-off: clearing browser data or switching devices loses history. Add Replit Auth + per-user DB rows if persistence across devices is needed.