Skip to content

Commit dde4f91

Browse files
committed
modified: README.md
modified: css/styles.css modified: index.html
1 parent 99ef1ec commit dde4f91

File tree

3 files changed

+397
-154
lines changed

3 files changed

+397
-154
lines changed

README.md

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# ✦ ClearVoice — AI Professional Writing Wizard
1+
# ✦ ClearVoice — AI Professional Writing Coach
22

3-
**A zero-backend AI writing coach** that rewrites your professional content across three styles, provides tone heatmaps, and explains every change with coaching insights.
3+
**A zero-backend AI writing coach** that rewrites your professional content across three styles, surfaces tone issues with a live heatmap, and coaches you with actionable before/after insights — all running directly in the browser.
44

55
**Live demo:** https://sushegaad.github.io/Professional-Writing-Wizard/
66

@@ -12,14 +12,30 @@
1212

1313
## Features
1414

15+
### Writing Intelligence
1516
- **Style Matrix** — Configure Persona, Audience, Goal, Content Type, and a Succinct ↔ Narrative vibe slider before every analysis
16-
- **Three Variations** — The Negotiator (empathetic), The Visionary (bold), The Minimalist (direct)
17-
- **Tone Heatmap** — Colour-coded overlay on your original text flagging aggressive, passive, waffle, jargon, vague, and strong phrases with hover tooltips
18-
- **Coaching Panel** — Animated score rings (Clarity, Confidence, Professionalism, Impact) plus before/after insight cards explaining every change
19-
- **Dual Provider** — Works with both Claude (Anthropic) and GPT-4 (OpenAI)
20-
- **Resizable Panes** — Drag the centre divider to adjust the editor/output split; double-click to reset to 50/50
21-
- **Light / Dark Mode** — Light theme by default with a toggle; preference saved locally
22-
- **Accept / Copy / Export** — Accept a variation back into the editor, copy to clipboard, or export a full text report
17+
- **Three Variations** — The Negotiator (persuasive & empathetic), The Visionary (bold & inspiring), The Minimalist (direct & efficient)
18+
- **Tone Heatmap** — Colour-coded overlay on your original draft flagging aggressive, too-passive, waffle/hedge, jargon, vague, and strong phrases with hover tooltips. Enabled automatically after every analysis
19+
- **Coaching Insights** — Animated score rings (Clarity, Confidence, Professionalism, Impact) plus before/after insight cards. Accept a suggestion to apply it directly into your draft, or dismiss it with one click
20+
21+
### Privacy & Safety
22+
- **PII Redaction** — Optional "Redact PII before sending" checkbox powered by a custom Semantic Privacy Guard engine. Strips emails, phone numbers, SSNs, credit cards, IBANs, IP addresses, passport numbers, medical record IDs, and more before the text leaves the browser
23+
24+
### Layout & UX
25+
- **Split Workspace** — Input (Your Draft) pane stacked above the output area. Drag the horizontal divider to adjust the top/bottom split; double-click to reset to 50/50
26+
- **Side-by-Side Output** — Enhanced Writing and Coaching Insights panes sit horizontally within the output area. Drag the vertical divider to adjust their relative widths; double-click to reset
27+
- **Collapsible Coaching Panel** — Click ◀ in the Coaching Insights header to collapse it and give more room to the Enhanced Writing pane; click ▶ to expand again
28+
- **Mobile Responsive** — On tablets the output area stacks vertically; on phones the layout fully reorganises with horizontally-scrollable controls, compact touch targets, and collapsible panels
29+
- **Scrollbars** — Styled 8px scrollbars with rounded thumbs throughout; Firefox `scrollbar-width: thin` support
30+
- **Light / Dark Mode** — Light theme by default with a one-click toggle; preference persists in `localStorage`
31+
32+
### Provider & Key Management
33+
- **Dual Provider** — Works with Claude (Anthropic) and GPT-4 (OpenAI); select inside ⚙ Settings
34+
- **Shared Key Gating** — When deployed with a GitHub Actions-injected key, a non-invasive usage counter (✦ N/50 uses) appears in the header. After 50 runs on the shared key, users are prompted to bring their own key. Counter turns amber as the limit approaches
35+
- **Accept / Copy / Export** — Accept a variation back into the editor, copy to clipboard, or export a full coaching report as a text file
36+
37+
### Tests
38+
A browser-based unit test suite lives in `tests/`. Open `tests/index.html` in any browser to run all tests — no build step required.
2339

2440
---
2541

@@ -31,7 +47,7 @@ cd Professional-Writing-Wizard
3147
```
3248

3349
Open `index.html` in your browser — or push to GitHub and enable Pages (see below).
34-
When the app loads, click **⚙ Settings** to enter your API key.
50+
When the app loads, click **⚙ Settings** to enter your API key. It is stored only in `localStorage`.
3551

3652
---
3753

@@ -42,7 +58,7 @@ When the app loads, click **⚙ Settings** to enter your API key.
4258
1. Go to your repo → **Settings → Pages**
4359
2. Source: **Deploy from a branch**`main``/ (root)`**Save**
4460
3. Visit `https://sushegaad.github.io/Professional-Writing-Wizard/`
45-
4. Click ** Settings** in the app and enter your API key. It is stored only in your browser's `localStorage`.
61+
4. Click **** in the app header and enter your API key
4662

4763
---
4864

@@ -81,6 +97,32 @@ Check progress under your repo's **Actions** tab.
8197

8298
---
8399

100+
## File Structure
101+
102+
```
103+
Professional-Writing-Wizard/
104+
├── index.html # Main application shell
105+
├── css/
106+
│ └── styles.css # Full stylesheet (light + dark, responsive)
107+
├── js/
108+
│ ├── config.js # Injected API config (overwritten at CI build time)
109+
│ ├── privacy.js # PII redaction engine (Semantic Privacy Guard port)
110+
│ ├── api.js # AI provider calls (Claude + OpenAI), prompt builder
111+
│ ├── ui.js # Render functions (variations, heatmap, score rings, insights)
112+
│ └── app.js # State, event handlers, settings, usage gating
113+
├── tests/
114+
│ ├── index.html # Browser test runner (auto-runs on load)
115+
│ ├── test-privacy.js # 19 PII redaction unit tests
116+
│ └── test-core.js # Core function tests (parseAIResponse, buildHeatmap, xEsc)
117+
├── architecture.html # Interactive system design diagram
118+
├── .github/
119+
│ ├── workflows/deploy.yml # GitHub Actions Pages deployment
120+
│ └── scripts/inject_config.py # Safely injects API key into config.js at build time
121+
└── README.md
122+
```
123+
124+
---
125+
84126
## Architecture
85127

86128
See **[architecture.html](https://sushegaad.github.io/Professional-Writing-Wizard/architecture.html)** for a visual deep-dive including:
@@ -89,7 +131,7 @@ See **[architecture.html](https://sushegaad.github.io/Professional-Writing-Wizar
89131
- File responsibility breakdown
90132
- End-to-end data flow (6 annotated steps)
91133
- JS module API reference
92-
- GitHub Actions CI/CD pipeline visualization
134+
- GitHub Actions CI/CD pipeline visualisation
93135
- Security model and API key handling
94136

95137
---
@@ -98,6 +140,7 @@ See **[architecture.html](https://sushegaad.github.io/Professional-Writing-Wizar
98140

99141
- **Manual mode**: Your API key lives only in `localStorage`. It is sent only to your chosen AI provider — never to any third party.
100142
- **GitHub Actions mode**: The key is injected into the deployed JS artifact. It is not in Git history but is readable in page source. Suitable for personal use only.
143+
- **PII redaction**: When the redaction checkbox is enabled, sensitive entities are replaced with `[REDACTED_TYPE]` tokens client-side before the API call. The original text is restored locally after the response.
101144
- **No telemetry**: No backend, no tracking, no analytics.
102145
- **Production recommendation**: For shared/team deployments, front the API with a server-side proxy (Cloudflare Workers, Netlify Functions, Vercel Edge, etc.).
103146

@@ -108,4 +151,4 @@ See **[architecture.html](https://sushegaad.github.io/Professional-Writing-Wizar
108151
MIT — free to use, modify, and deploy.
109152

110153
---
111-
**Built by Hemant Naik · Powered by Claude Sonnet 4.5**
154+
**Built by Hemant Naik · Powered by Claude Sonnet 4.5**

css/styles.css

Lines changed: 190 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -260,14 +260,19 @@ input[type=range].vibe::-webkit-slider-thumb:hover {
260260
box-shadow: 0 0 0 4px var(--acc-glow), 0 1px 4px rgba(0,0,0,0.2);
261261
}
262262

263-
/* ── Workspace (vertical split — top/bottom panes) ──────── */
263+
/* ── Workspace (vertical: input on top, output-row below) ── */
264264
.workspace {
265265
display: flex; flex-direction: column; flex: 1; min-height: 0; overflow: visible;
266266
}
267267

268+
/* ── Output row (horizontal: enhanced writing + coaching) ── */
269+
.output-row {
270+
display: flex; flex-direction: row; flex: 1; min-height: 45vh; overflow: hidden;
271+
}
272+
268273
/* ── Pane ────────────────────────────────────────────────── */
269-
.pane { display: flex; flex-direction: column; flex: 1; min-height: 45vh; min-width: 0; overflow: visible; }
270-
.pane-input { border-bottom: 1px solid var(--border); }
274+
.pane { display: flex; flex-direction: column; flex: 1; min-height: 0; min-width: 0; overflow: hidden; }
275+
.pane-input { min-height: 45vh; border-bottom: 1px solid var(--border); }
271276

272277
.pane-head {
273278
display: flex; align-items: center; justify-content: space-between;
@@ -311,7 +316,7 @@ textarea.draft {
311316
}
312317
textarea.draft::placeholder { color: var(--text-3); line-height: 1.9; font-weight: 400; }
313318

314-
/* ── Pane Divider (horizontal drag-to-resize) ────────────── */
319+
/* ── Pane Divider — Y-axis (row-resize, input vs output-row) */
315320
.pane-divider {
316321
height: 6px; width: 100%; flex-shrink: 0; background: var(--border);
317322
cursor: row-resize; position: relative; z-index: 10;
@@ -329,6 +334,24 @@ textarea.draft::placeholder { color: var(--text-3); line-height: 1.9; font-weigh
329334
.pane-divider:hover .pane-divider-handle,
330335
.pane-divider.dragging .pane-divider-handle { opacity: 1; background: white; }
331336

337+
/* ── Pane Divider — X-axis (col-resize, writing vs coaching) */
338+
.pane-divider-x {
339+
width: 6px; height: 100%; flex-shrink: 0; background: var(--border);
340+
cursor: col-resize; position: relative; z-index: 10;
341+
transition: background var(--t);
342+
display: flex; align-items: center; justify-content: center;
343+
}
344+
.pane-divider-x:hover,
345+
.pane-divider-x.dragging { background: var(--accent); }
346+
347+
.pane-divider-handle-x {
348+
width: 2px; height: 40px; border-radius: 2px;
349+
background: var(--text-3); opacity: 0.5;
350+
pointer-events: none; transition: opacity var(--t);
351+
}
352+
.pane-divider-x:hover .pane-divider-handle-x,
353+
.pane-divider-x.dragging .pane-divider-handle-x { opacity: 1; background: white; }
354+
332355
/* ── Heatmap Layer ───────────────────────────────────────── */
333356
.heatmap-layer {
334357
display: none;
@@ -405,7 +428,7 @@ textarea.draft::placeholder { color: var(--text-3); line-height: 1.9; font-weigh
405428
.var-tab.active { background: var(--acc-light); color: var(--accent); }
406429

407430
/* ── Output Pane Content ─────────────────────────────────── */
408-
#outScroll { min-height: 42vh; overflow-y: auto; padding: 20px 24px; }
431+
#outScroll { overflow-y: auto; padding: 20px 24px; }
409432

410433
.var-card { display: none; }
411434
.var-card.show { display: block; }
@@ -458,32 +481,41 @@ body.dark .loading-mask { background: rgba(12,14,20,0.85); }
458481
.hl-item { display: flex; align-items: center; gap: 4px; color: var(--text-2); font-weight: 500; }
459482
.hl-dot { width: 9px; height: 9px; border-radius: 2px; }
460483

461-
/* ── Why / Coaching Panel ────────────────────────────────── */
462-
.why-panel {
463-
border-top: 1px solid var(--border); background: var(--bg-alt); flex-shrink: 0;
464-
box-shadow: 0 -1px 0 var(--border);
484+
/* ── Coaching Insights pane (side panel inside output-row) ── */
485+
.pane-coaching {
486+
flex: none; /* don't grow/shrink — output pane fills remaining space */
487+
width: 340px;
488+
min-width: 0;
489+
overflow: hidden;
490+
transition: width 0.22s cubic-bezier(.4,0,.2,1);
491+
border-left: 1px solid var(--border);
465492
}
466-
.why-head {
467-
display: flex; align-items: center; justify-content: space-between;
468-
padding: 10px 20px; cursor: pointer; user-select: none; transition: var(--t);
493+
494+
/* Collapsed: zero width, invisible */
495+
.pane-coaching.collapsed {
496+
width: 0 !important;
497+
border-left: none;
498+
}
499+
500+
/* Collapse toggle button arrow direction */
501+
.coach-collapse-btn { font-size: 11px; }
502+
503+
/* Why-panel body — always visible (no open/close toggle needed) */
504+
.why-body {
505+
flex: 1; overflow-y: auto;
506+
padding: 14px 18px 20px;
507+
display: flex; flex-direction: column; gap: 0;
469508
}
470-
.why-head:hover { background: var(--s-hover); }
471509

472-
.why-head-left { display: flex; align-items: center; gap: 10px; }
473-
.why-title { font-size: 12px; font-weight: 800; }
510+
/* why-badge inside pane-title */
474511
.why-badge {
475512
font-size: 9px; font-weight: 800; letter-spacing: .5px; text-transform: uppercase;
476513
padding: 2px 8px; border-radius: 20px;
477514
background: var(--acc-light); color: var(--accent);
478515
}
479-
.why-head-right { display: flex; align-items: center; gap: 12px; }
480-
.score-preview { display: flex; gap: 8px; font-size: 10px; }
481516

482-
.why-arrow { font-size: 10px; color: var(--text-3); transition: transform var(--t); }
483-
.why-panel.open .why-arrow { transform: rotate(180deg); }
484-
485-
.why-body { display: none; padding: 0 20px 16px; max-height: 280px; overflow-y: auto; }
486-
.why-panel.open .why-body { display: block; }
517+
/* Score preview row in coaching pane header */
518+
.score-preview { display: flex; gap: 8px; font-size: 10px; }
487519

488520
/* ── Score Cards ─────────────────────────────────────────── */
489521
.scores-row { display: flex; gap: 10px; margin-bottom: 14px; padding-top: 6px; flex-wrap: wrap; }
@@ -660,12 +692,141 @@ select.form-sel option { background: var(--surface); color: var(--text-1); }
660692
/* Firefox scrollbar */
661693
* { scrollbar-width: thin; scrollbar-color: var(--text-3) var(--s-raised); }
662694

663-
/* ── Responsive ──────────────────────────────────────────── */
664-
@media (max-width: 700px) {
665-
/* Panes are already vertical — just hide divider on very small screens */
666-
.pane-divider { display: none; }
667-
.matrix-bar { gap: 8px; }
668-
.logo-tag { display: none; }
669-
.pill-group { flex-wrap: wrap; }
695+
/* ════════════════════════════════════════════════════════════
696+
RESPONSIVE — Tablet & Mobile
697+
════════════════════════════════════════════════════════════ */
698+
699+
/* ── Tablet landscape / small desktop (≤1024px) ─────────── */
700+
@media (max-width: 1024px) {
701+
.pane-coaching { width: 280px; }
702+
.var-tab { font-size: 9px; padding: 3px 7px; }
703+
}
704+
705+
/* ── Tablet portrait (≤768px) ───────────────────────────── */
706+
@media (max-width: 768px) {
707+
/* Stack output row vertically on tablets */
708+
.output-row {
709+
flex-direction: column;
710+
min-height: 0;
711+
}
712+
713+
/* Coaching becomes a horizontal strip at bottom */
714+
.pane-coaching {
715+
width: 100% !important;
716+
height: 280px;
717+
min-height: 0;
718+
border-left: none;
719+
border-top: 1px solid var(--border);
720+
flex-shrink: 0;
721+
transition: height 0.22s cubic-bezier(.4,0,.2,1);
722+
}
723+
.pane-coaching.collapsed {
724+
width: 100% !important;
725+
height: 44px !important; /* show header only */
726+
}
727+
728+
/* Show expand icon pointing down on mobile (panel stacks below) */
729+
.coach-collapse-btn { transform: rotate(90deg); }
730+
.pane-coaching.collapsed .coach-collapse-btn { transform: rotate(-90deg); }
731+
732+
/* Hide X-axis divider (replaced by horizontal stacking) */
733+
.pane-divider-x { display: none; }
734+
735+
/* Reduce pane min-heights for smaller screens */
736+
.output-row .pane { min-height: 220px; }
737+
738+
/* Matrix bar: horizontally scrollable, no wrapping */
739+
.matrix-bar {
740+
flex-wrap: nowrap;
741+
overflow-x: auto;
742+
overflow-y: hidden;
743+
-webkit-overflow-scrolling: touch;
744+
gap: 8px;
745+
padding: 6px 14px;
746+
scrollbar-width: none;
747+
}
748+
.matrix-bar::-webkit-scrollbar { display: none; }
749+
.mx-sep { display: none; }
750+
751+
/* Slightly smaller header on tablet */
752+
.header { height: 48px; padding: 0 14px; }
753+
.logo-tag { display: none; }
754+
.api-pill { display: none; } /* hide full pill, keep settings btn */
755+
756+
/* Adjust coaching why-body height */
757+
.pane-coaching .why-body { max-height: 220px; }
758+
759+
/* Scores row scrolls horizontally */
760+
.scores-row { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 6px; scrollbar-width: none; }
761+
.scores-row::-webkit-scrollbar { display: none; }
762+
}
763+
764+
/* ── Mobile phone (≤480px) ──────────────────────────────── */
765+
@media (max-width: 480px) {
766+
/* Header: compact */
767+
.header { padding: 0 10px; }
768+
.logo-name { font-size: 14px; }
769+
.logo-mark { width: 26px; height: 26px; font-size: 12px; }
770+
.icon-btn { width: 30px; height: 30px; font-size: 12px; }
670771
.usage-counter { display: none !important; }
772+
773+
/* Matrix bar: smaller text */
774+
.matrix-label { display: none; }
775+
.mx-sublabel { font-size: 8px; }
776+
select.mx-sel { font-size: 10px; min-width: 100px; padding: 3px 22px 3px 7px; }
777+
778+
/* Pill buttons: compact */
779+
.pill-group { gap: 2px; }
780+
.pill-btn { font-size: 9px; padding: 3px 8px; }
781+
782+
/* Vibe slider: shorter */
783+
.vibe-wrap { min-width: 130px; }
784+
785+
/* Input pane: min-height reduced for phone */
786+
.pane-input { min-height: 200px; }
787+
textarea.draft { font-size: 15px; min-height: 160px; }
788+
.heatmap-layer { font-size: 15px; min-height: 160px; }
789+
790+
/* Pane head: tighter */
791+
.pane-head { padding: 8px 12px; }
792+
.pane-title { font-size: 13px; }
793+
.pane-foot { padding: 8px 12px; }
794+
795+
/* Output pane */
796+
.output-row .pane { min-height: 180px; }
797+
798+
/* Coaching pane: taller on phone for readability */
799+
.pane-coaching { height: 300px; }
800+
801+
/* Buttons: larger touch targets */
802+
.btn { font-size: 13px; padding: 9px 14px; }
803+
.btn-accent { width: 100%; justify-content: center; }
804+
805+
/* Footer: stack vertically */
806+
.pane-foot { flex-direction: column; align-items: stretch; gap: 8px; }
807+
.pane-foot > div { justify-content: stretch; }
808+
.pane-foot .btn-ghost { flex: 1; justify-content: center; }
809+
#outFoot > div { flex-wrap: wrap; }
810+
811+
/* Var tabs: scroll horizontally */
812+
.var-tabs { overflow-x: auto; scrollbar-width: none; }
813+
.var-tabs::-webkit-scrollbar { display: none; }
814+
.var-tab { font-size: 9px; white-space: nowrap; }
815+
816+
/* Output text: slightly smaller */
817+
.var-text { font-size: 15px; }
818+
819+
/* Score cards: smaller */
820+
.score-card { min-width: 76px; padding: 8px 10px; }
821+
.score-ring { width: 40px; height: 40px; }
822+
.score-num { font-size: 10px; }
823+
824+
/* Y-axis pane divider: larger touch target */
825+
.pane-divider { height: 10px; }
826+
.pane-divider-handle { width: 32px; }
827+
828+
/* Modal: full-width */
829+
.modal { padding: 20px 16px; width: 100%; border-radius: var(--r-lg); }
830+
.modal-title { font-size: 15px; }
831+
.prov-btn { font-size: 11px; padding: 8px; }
671832
}

0 commit comments

Comments
 (0)