⚙ System Architecture

How ClearVoice Works

A technical deep-dive into the architecture, data flow, module design, and deployment pipeline of the ClearVoice AI Communication Coach.

Architecture at a Glance

ClearVoice is a fully static, zero-backend web application. All AI inference is performed via direct browser-to-API calls. No server, no database, no telemetry.

USER INTERFACE LAYER — Browser / GitHub Pages
📄
index.html
DOM structure, layout, all UI markup
🎨
css/styles.css
Light/dark themes via CSS variables
js/config.js
Runtime config, API key injection point
user action triggers
APPLICATION LOGIC LAYER — Vanilla JS modules
🧠
js/app.js
State, settings, theme, matrix, orchestration
🔌
js/api.js
Prompt builder, Claude & OpenAI callers
🖼
js/ui.js
DOM rendering: scores, insights, heatmap
HTTPS fetch() — direct browser API call
AI PROVIDER LAYER — External APIs
🟠
Anthropic Claude
api.anthropic.com/v1/messages
Sonnet 4.5 / Opus 4.5 / Haiku 4.5
🟢
OpenAI GPT-4
api.openai.com/v1/chat/completions
GPT-4o / GPT-4 Turbo / GPT-3.5

Repository Layout

Clean separation of concerns: HTML for structure, one CSS file for all theming, three focused JS modules, and a GitHub Actions workflow for CI/CD.

Professional-Writing-Wizard/
├── index.html← Main application shell; all UI markup
├── architecture.html← This page; system design documentation
├── README.md← Setup, deployment, and usage guide
├── css/
│ └── styles.css← Full stylesheet; :root = light, body.dark = dark
├── js/
│ ├── config.js← window.CV_CONFIG; replaced by GitHub Actions
│ ├── api.js← buildSystemPrompt(), callClaude(), callOpenAI(), parseAIResponse()
│ ├── ui.js← renderResults(), renderScores(), buildHeatmap(), toast(), setLoading()
│ └── app.js← AppState, init(), theme, settings, matrix, runAnalysis()
└── .github/workflows/
└── deploy.yml← GitHub Actions: inject secret → upload to Pages
📄
index.html
Pure HTML structure with no inline scripts or styles. Loads CSS and the 4 JS files in dependency order.
🎨
styles.css
All CSS variables in :root for light mode. body.dark overrides each variable.
config.js
Sets window.CV_CONFIG. Safe placeholder in Git; GitHub Actions regenerates it at build time with the real secret.
🤖
deploy.yml
Uses Python's json.dumps() for safe key injection, then deploys via the official GitHub Pages action.

End-to-End Request Lifecycle

From a user clicking "Analyze & Enhance" to rendered results — here is every step in the pipeline.

1
User configures Style Matrix
The user selects Persona, Audience, Goal, Content Type, and positions the Vibe slider. All values are stored in AppState in app.js and read at analysis time.
app.js — AppState object
2
runAnalysis() validates and dispatches
Guards: checks for non-empty text and a configured API key. Creates a state snapshot (merges AppState + current select values), then calls the appropriate provider function.
app.js → api.js
3
buildSystemPrompt() constructs the instruction
Maps all state values to human-readable strings and assembles a structured system prompt containing the writer's context, communication parameters, required JSON schema, and strict output rules.
system: `You are ClearVoice... CONTEXT: - Writer's Persona : Individual Contributor - Target Audience : Colleagues and Teammates - Communication Goal: Inform and update - Content Type : Professional Email (BLUF) - Tone/Vibe (0–100) : 30 — concise and direct ...EXACT REQUIRED JSON SCHEMA: { "variations": [...] }`
api.js — buildSystemPrompt()
4
Direct browser HTTPS call to AI provider
Claude uses the anthropic-dangerous-direct-browser-access: true header to allow CORS. OpenAI natively supports browser calls. Both return a JSON string in the message content.
api.js — callClaude() / callOpenAI()
5
parseAIResponse() normalises the output
Strips any accidental markdown code fences (models occasionally wrap JSON in ```json), then parses to a JavaScript object. Falls back to a regex extraction if direct parsing fails.
api.js — parseAIResponse()
6
renderResults() drives four UI updates
Variations — injects 3 full rewrites into their respective tab cards.
Scores — creates SVG rings with CSS-transition animations.
Insights — renders before/after coaching cards with category badges.
Heatmap — on toggle, wraps tone-flagged phrases with colour-coded <span> elements.
ui.js — renderResults(), renderScores(), renderInsights(), buildHeatmap()

Module Responsibilities

Four vanilla JS files with a strict load order. No bundler, no framework — pure ES5-compatible globals for maximum portability on GitHub Pages.

Configuration
js/config.js
window.CV_CONFIG Global config object. Replaced at build time by GitHub Actions to inject API key from repository secrets.
🔌
API Layer
js/api.js
buildSystemPrompt(state) Assembles multi-section system prompt from state parameters.
callClaude(text, state, key) POST to Anthropic with browser-CORS header.
callOpenAI(text, state, key) POST to OpenAI with json_object response format.
parseAIResponse(raw) JSON parse with code-fence stripping and regex fallback.
🖼
UI Rendering
js/ui.js
renderResults(result) Populates variations, triggers score/insight renders, opens coaching panel.
renderScores(scores) Builds animated SVG ring cards; uses double-RAF for CSS transition.
renderInsights(coaching) Renders before/after diff cards with category badge colouring.
buildHeatmap(text, toneAnalysis) Sorts phrases longest-first to prevent nested span replacements, then wraps each in a typed highlight span.
toast(msg, type) Self-dismissing notification with CSS slide + opacity animation.
setLoading(on) Toggles loading overlay; cycles through 5 status messages every 2.4 s.
🧠
App Logic
js/app.js
AppState Single source of truth: provider, apiKey, model, goal, type, vibe, heatOn, result, busy.
init() (IIFE) Priority merge: CV_CONFIG → localStorage → UI sync → open settings if no key.
toggleTheme() Toggles body.dark class; persists preference to localStorage.
runAnalysis() Validates, builds state snapshot, calls api.js, passes result to ui.js.
acceptVar() / copyVar() Post-analysis actions on selected variation.
exportInsights() Generates a formatted .txt report via Blob URL download.

GitHub Actions Deployment

A single workflow triggers on every push to main. It injects the API key from repository secrets and deploys the site using the official GitHub Pages action.

📦
Checkout
actions/checkout@v4
Clone the repository
🔑
Inject Secret
Python script uses json.dumps() to safely write API key into js/config.js
📄
Configure Pages
actions/configure-pages@v4
Set up the Pages environment
🗜
Upload Artifact
actions/upload-pages-artifact@v3
Package entire repo as artifact
🚀
Deploy
actions/deploy-pages@v4
Publish to github.io URL
⚠ Key Injection Detail
Python's json.dumps() is used (not shell string interpolation) to ensure the API key is safe regardless of special characters ($, ", \, etc.). The generated js/config.js is never committed to the main branch — it only exists in the deployed GitHub Pages artifact. The source repository always contains an empty placeholder.

API Key Handling

Because ClearVoice is a static site with no backend, all API communication is client-side. Here is an honest assessment of each configuration mode.

🔒 Manual Entry (Recommended)
User enters API key via the Settings dialog. It is stored in localStorage and sent only to the AI provider. The key never leaves the browser, is never logged, and is not accessible to any third party.
GitHub Actions Injection
The API key is injected at build time. It does not appear in the Git history, but it does exist in the deployed JavaScript file on GitHub Pages. Anyone who inspects the page source can read it. Use only for personal/private projects.
🌐 No Data Retention
ClearVoice has no backend, no database, no analytics, and no telemetry. Written content is sent only to your chosen AI provider (Anthropic or OpenAI) in accordance with their privacy policies.
🔐 Production Recommendation
For team or public deployments, implement a thin backend proxy (Cloudflare Workers, Netlify Functions, Vercel Edge) that holds the API key server-side and authenticates users before forwarding requests.

What It's Built With

Intentionally zero-dependency. No Node.js, no npm, no bundler. Pure web standards with one Google Fonts import.

HTML5 — Semantic markup
CSS3 — Custom properties, Grid, Flexbox, animations
Vanilla JS (ES2020) — fetch, async/await, Clipboard API
Anthropic Claude API — Direct browser access
OpenAI GPT-4 API — json_object response format
GitHub Pages — Static hosting
GitHub Actions — CI/CD with secret injection
Google Fonts (Inter) — Typography
localStorage — Theme + API key persistence