DownCV is a modern, interactive, open-source web platform for creating, editing, analyzing, and exporting resumes (CVs) that are fully optimized for Applicant Tracking Systems (ATS) using Markdown syntax.
JavaScript
11
5 commits
updated Oct 12, 2026
The Local-First, Privacy-Focused Markdown CV Builder & ATS Optimizer
DownCV is a high-performance, open-source web application designed to help job seekers create, format, and optimize professional resumes using Markdown.
Unlike web resume builders that process sensitive personal data on third-party servers, DownCV runs 100% client-side in your browser. It includes an isolated Applicant Tracking System (ATS) evaluation engine that generates real vector PDFs, reads back the text layer using pdf.js (exactly as real ATS parsers do), scores the resume across 12 critical dimensions, and provides one-click automatic Markdown fixes.
Edit raw Markdown or select text on the A4 page preview to format elements directly without breaking document syntax.
Scores your generated PDF against realistic ATS rules (text layer integrity, keyword density, action verbs, contact information, bullet quality, etc.). Also analyses a PDF you already have, and exports the report. The score is the sum of the rules that pass, out of 100. Both lexicons are measured together, so a bilingual CV is never punished for the language of its headings or of its interface.
| Rule | Points | What it fails on |
|---|---|---|
text | 10 | The PDF has almost no selectable text (a scan or an image) |
pages | 8 | More than two pages |
length | 3 | Fewer than 200 or more than 900 words |
contact | 9 | Fewer than two of email, phone and profile link |
sections | 9 | Fewer than three standard sections found as headings |
headings | 4 | A source heading that never reaches the sheet as a heading (a title too long, wrapped into loose lines) |
residue | 6 | Markdown or HTML syntax visible in the text layer |
emoji | 3 | An emoji or an image/table placeholder a parser cannot read |
verbs | 10 | Fewer than four action verbs |
metrics | 8 | Fewer than two numbers behind the achievements |
phrasing | 5 | Fillers such as "responsible for" or "hard-working", with the verb that says it better |
duplicates | 3 | The same bullet copied into two jobs |
keywords | 5 | Fewer than four relevant terms, including the ones the detected role expects |
bullets | 4 | More than 30% of the experience bullets with no verb and no number |
language | 2 | Headings in one language and bullets in another |
dates | 4 | A date in the future, two date styles mixed, or two jobs claiming the same months |
filename | 2 | A file name without your name, with spaces, or marked "final" / "v2" |
density | 3 | A single page over 750 words (the densest page, not the average) |
stuffing | 2 | A term repeated four times in one line, or eating 6% of the document |
The panel also groups them into five dimensions (structure, contact, verbs, metrics, keywords), lists what is missing sorted by points lost, and can download the whole report as HTML.
Scans your Markdown for every email, phone and link and makes you approve each one explicitly β flagging missing domain extensions, numbers without a country code, links written without https://, and URLs hidden behind link text that the ATS parser never reads. Export stays blocked until every item is verified or dismissed.
Keep one tailored CV per position instead of overwriting a single document. Name each version after the offer (Acme Β· Frontend Engineer), then open, rename, duplicate or delete them independently β each one shows the keywords it adds or removes versus the CV on screen, and can be linked to a real .md file on disk.
Select any paragraph or bullet in the preview and let your own Ollama model rewrite it for ATS parsing β same meaning, stronger action verbs, quantified impact. Point the endpoint to http://localhost:11434/v1/chat/completions, pick a model and test the connection: no data ever leaves your machine.
Automatically drafts a tailored, professional cover letter derived from your resume's key achievements and contact information.
Export to native vector PDF (via jsPDF), Microsoft Word (.docx via OOXML), or Rich Text Format (.rtf).
Import existing resumes from .pdf, .docx, .txt, .md, or raw text into clean, structured Markdown.
| Layer | Technology | Purpose |
|---|---|---|
| Frontend Framework | React 18 & Vite 6 | Fast SPA rendering & modern build pipeline |
| Styling & UI | Tailwind CSS 4 + Lucide Icons | Responsive layout, dark mode, CSS custom properties |
| Markdown Processing | Marked.js (Custom Renderer) | AST block tagging & bi-directional source mapping |
| PDF Generation | jsPDF | Native vector text streams (WinAnsi fallback, standard A4 layout) |
| ATS Text Extraction | PDF.js (pdfjs-dist) | Worker-based text layer parsing mimicking Workday/Taleo |
| Word / RTF Export | docx (OOXML) | Native .docx block generation and .rtf document creation |
| Testing Suite | Vitest 5 + React Testing Library | Unit & integration tests for parser, builders, and UI |
| Deployment / Container | Docker + Nginx Alpine | Lightweight containerized production distribution |
flowchart TD
subgraph Editor ["Markdown Workspace"]
MD[Markdown Source] --> MP[marked Parser + Offset Mapping]
MP --> PREVIEW[A4 Interactive Preview]
PREVIEW -. Selection Sync .-> MD
end
subgraph PDFEngine ["PDF & Export Layer"]
MD --> PM[pdfModel.js - Styled Blocks]
PM --> PB[pdfBuilder.js - jsPDF Vector Stream]
PB --> PA[pdfArtifact.js - Content-Keyed Cache]
PA --> EXPORT[Export: PDF / DOCX / RTF]
end
subgraph ATSPipeline ["ATS Analyzer Engine (Local)"]
PA --> PT[pdfText.js - pdf.js Text Layer Extractor]
PT --> EVAL[atsPdfEvaluation.js - 19 Scoring Rules]
EVAL --> SCORE[ATS Score % + Gaps + Report]
SCORE --> FIXES[markdownFixes.js - 1-Click Fixes]
FIXES -. Apply Fix .-> MD
end
pdfModel.js converts Markdown into structured style blocks. pdfBuilder.js renders real vector text via jsPDF.pdfArtifact.js caches generated binary blobs based on a hash of the Markdown text and layout settings, ensuring scores and downloads never drift.pdfText.js invokes pdf.js to extract plain text per page, replicating how Enterprise ATS software (Workday, Taleo, Greenhouse) ingests resumes.atsPdfEvaluation.js checks 19 rules worth 100 points (see the table above): text layer existence, length, contact info, section structure, headings the parser cannot read, Markdown residue, exotic characters, action verbs, numbers, weak phrasing, duplicated bullets, keywords, bullet formatting, document language, dates and chronology, file name, page density and keyword stuffing. It can also score any PDF you already have (evaluatePdfFile).The AI assistant is entirely optional and entirely local. DownCV never proxies your CV to anyone: it only issues one POST request to the endpoint you configure, and it talks to Ollama's OpenAI-compatible API, so any local runtime exposing /v1/chat/completions works.
Ctrl+K β Open AI assistant) and turn on Enable AI in the preview.http://localhost:11434/v1/chat/completions), the model name and, if your runtime requires it, an API key. Press Test connection to confirm the model answers.enhanceBulletPoint() (weak-verb replacement, metric detection), then Generate with Ollama asks your model to rewrite the fragment keeping the exact same meaning but with stronger action verbs and quantified impact.Ctrl+Z.The system prompt (aiEnhancer.js) is set to "You are an expert ATS resume writer and career coach", and the request is stripped of quotes so the result can be pasted straight back into the document.
Clone the repository:
git clone https://github.com/AcoranGonzalezMoray/DownCV.git
cd DownCV
Install dependencies:
npm install
Start the development server:
npm run dev
Open your browser at http://localhost:3000.
Run tests:
npm run test:run
Lint and format code:
npm run lint
npm run format
The ATS rewriter needs a local model runtime. With Ollama:
ollama serve # start the server (default port 11434)
ollama pull llama3 # or any other chat model you prefer
Then in DownCV open the AI Assistant dialog, enable it, and press Test connection. Any runtime that speaks the OpenAI /v1/chat/completions schema (Ollama, LM Studio, llama.cpp server, vLLMβ¦) works by changing the endpoint.
DownCV can be easily containerized and served using Nginx.
docker compose up -d --build
Access DownCV in your browser at http://localhost:8080.
docker build -t down-cv .
docker run -d -p 8080:80 down-cv
DownCV/
βββ public/ # Static assets, app icons & PWA manifest
βββ src/
β βββ components/ # React components
β β βββ ATSAnalyzer.jsx # ATS score display, recommendations, 1-click fixes
β β βββ AIAssistantModal.jsx # Ollama endpoint / model settings & connection test
β β βββ AiRewritePopover.jsx # AI rewrite suggestions popover
β β βββ ContactWarnings.jsx # Unverified email/phone/link banner & validator
β β βββ CoverLetterModal.jsx # Cover letter generator & PDF exporter
β β βββ CVPreview.jsx # A4 paper preview with direct formatting tools
β β βββ ExportMenu.jsx # Dropdown menu for PDF, DOCX, RTF, PNG, JSON, HTML
β β βββ ImportModal.jsx # Import PDF, DOCX, TXT, or MD into Markdown
β β βββ MarkdownEditor.jsx # Caret shortcuts, sample CVs, search, draft history
β β βββ StyleControls.jsx # Font, color palette, margin, line height & layout presets
β β βββ ThemeToggle.jsx # Dark / light mode toggle
β β βββ VariantsPanel.jsx # Named CV versions per job offer & local .md file
β βββ data/ # Static seed data & i18n strings
β β βββ sampleCVs.js # Ready-to-edit sample CVs per language
β β βββ snippets.js # Reusable section snippets
β β βββ templates.js # Layout template presets
β β βββ translations.js # English & Spanish UI strings
β βββ hooks/ # Custom React hooks
β β βββ useContactVerification.js # Verification logic for email/phone/URLs
β β βββ useKeyboardShortcuts.js # Global hotkeys (Ctrl+S, Ctrl+Z, etc.)
β β βββ useLocalFile.js # File System Access API .md linking
β β βββ useLocalStorage.js # Synced localStorage state
β β βββ useMarkdownHistory.js # Pure undo/redo state stack
β βββ utils/ # Pure business logic & formatting modules
β β βββ aiEnhancer.js # Local bullet enhancer + Ollama OpenAI-compatible client
β β βββ atsDimensions.js # Rule groups behind the radar chart
β β βββ atsPdfEvaluation.js # Main ATS evaluation orchestration (19 rules / 100 pts)
β β βββ atsReport.js # Standalone HTML score report
β β βββ atsScorer.js # Action verbs, keywords & weak phrase lexicons
β β βββ contactScan.js # Contact parser & regex scanner
β β βββ coverLetter.js # Template logic for cover letter creation
β β βββ cvImport.js # Converter from arbitrary text to clean MD
β β βββ cvVariants.js # CV version creation & keyword diffing
β β βββ docxGenerator.js # OOXML (.docx) and RTF document exporter
β β βββ jobMatcher.js # Job description keyword match score
β β βββ jsonResume.js # JSON Resume (jsonresume.org) import/export
β β βββ markdownFixes.js # Pure 1-click Markdown issue resolution
β β βββ markdownParser.js # marked AST parser with source line offsets
β β βββ pdfBuilder.js # Vector PDF rendering engine using jsPDF
β β βββ pdfText.js # pdf.js text layer reader worker
β βββ App.jsx # Main application layout & state coordinator
β βββ main.jsx # Application entry point
βββ Dockerfile # Multi-stage production build
βββ docker-compose.yml # Local Docker configuration
βββ vite.config.js # Vite build configuration
We have an active product roadmap! Check out our planned features and technical improvements:
Contributions are warmly welcomed! Whether you want to fix a bug, add a new ATS scoring rule, translate to a new language, or suggest a feature:
git checkout -b feature/AmazingFeature).git commit -m 'Add some AmazingFeature').npm run lint && npm run test:run).git push origin feature/AmazingFeature).Distributed under the MIT License. See LICENSE for more information.
Crafted with β€οΈ for privacy and career success.
DownCV is a modern, interactive, open-source web platform for creating, editing, analyzing, and exporting resumes (CVs) that are fully optimized for Applicant Tracking Systems (ATS) using Markdown syntax.
JavaScript
11
5 commits
updated Oct 12, 2026
The Local-First, Privacy-Focused Markdown CV Builder & ATS Optimizer
DownCV is a high-performance, open-source web application designed to help job seekers create, format, and optimize professional resumes using Markdown.
Unlike web resume builders that process sensitive personal data on third-party servers, DownCV runs 100% client-side in your browser. It includes an isolated Applicant Tracking System (ATS) evaluation engine that generates real vector PDFs, reads back the text layer using pdf.js (exactly as real ATS parsers do), scores the resume across 12 critical dimensions, and provides one-click automatic Markdown fixes.
Edit raw Markdown or select text on the A4 page preview to format elements directly without breaking document syntax.
Scores your generated PDF against realistic ATS rules (text layer integrity, keyword density, action verbs, contact information, bullet quality, etc.). Also analyses a PDF you already have, and exports the report. The score is the sum of the rules that pass, out of 100. Both lexicons are measured together, so a bilingual CV is never punished for the language of its headings or of its interface.
| Rule | Points | What it fails on |
|---|---|---|
text | 10 | The PDF has almost no selectable text (a scan or an image) |
pages | 8 | More than two pages |
length | 3 | Fewer than 200 or more than 900 words |
contact | 9 | Fewer than two of email, phone and profile link |
sections | 9 | Fewer than three standard sections found as headings |
headings | 4 | A source heading that never reaches the sheet as a heading (a title too long, wrapped into loose lines) |
residue | 6 | Markdown or HTML syntax visible in the text layer |
emoji | 3 | An emoji or an image/table placeholder a parser cannot read |
verbs | 10 | Fewer than four action verbs |
metrics | 8 | Fewer than two numbers behind the achievements |
phrasing | 5 | Fillers such as "responsible for" or "hard-working", with the verb that says it better |
duplicates | 3 | The same bullet copied into two jobs |
keywords | 5 | Fewer than four relevant terms, including the ones the detected role expects |
bullets | 4 | More than 30% of the experience bullets with no verb and no number |
language | 2 | Headings in one language and bullets in another |
dates | 4 | A date in the future, two date styles mixed, or two jobs claiming the same months |
filename | 2 | A file name without your name, with spaces, or marked "final" / "v2" |
density | 3 | A single page over 750 words (the densest page, not the average) |
stuffing | 2 | A term repeated four times in one line, or eating 6% of the document |
The panel also groups them into five dimensions (structure, contact, verbs, metrics, keywords), lists what is missing sorted by points lost, and can download the whole report as HTML.
Scans your Markdown for every email, phone and link and makes you approve each one explicitly β flagging missing domain extensions, numbers without a country code, links written without https://, and URLs hidden behind link text that the ATS parser never reads. Export stays blocked until every item is verified or dismissed.
Keep one tailored CV per position instead of overwriting a single document. Name each version after the offer (Acme Β· Frontend Engineer), then open, rename, duplicate or delete them independently β each one shows the keywords it adds or removes versus the CV on screen, and can be linked to a real .md file on disk.
Select any paragraph or bullet in the preview and let your own Ollama model rewrite it for ATS parsing β same meaning, stronger action verbs, quantified impact. Point the endpoint to http://localhost:11434/v1/chat/completions, pick a model and test the connection: no data ever leaves your machine.
Automatically drafts a tailored, professional cover letter derived from your resume's key achievements and contact information.
Export to native vector PDF (via jsPDF), Microsoft Word (.docx via OOXML), or Rich Text Format (.rtf).
Import existing resumes from .pdf, .docx, .txt, .md, or raw text into clean, structured Markdown.
| Layer | Technology | Purpose |
|---|---|---|
| Frontend Framework | React 18 & Vite 6 | Fast SPA rendering & modern build pipeline |
| Styling & UI | Tailwind CSS 4 + Lucide Icons | Responsive layout, dark mode, CSS custom properties |
| Markdown Processing | Marked.js (Custom Renderer) | AST block tagging & bi-directional source mapping |
| PDF Generation | jsPDF | Native vector text streams (WinAnsi fallback, standard A4 layout) |
| ATS Text Extraction | PDF.js (pdfjs-dist) | Worker-based text layer parsing mimicking Workday/Taleo |
| Word / RTF Export | docx (OOXML) | Native .docx block generation and .rtf document creation |
| Testing Suite | Vitest 5 + React Testing Library | Unit & integration tests for parser, builders, and UI |
| Deployment / Container | Docker + Nginx Alpine | Lightweight containerized production distribution |
flowchart TD
subgraph Editor ["Markdown Workspace"]
MD[Markdown Source] --> MP[marked Parser + Offset Mapping]
MP --> PREVIEW[A4 Interactive Preview]
PREVIEW -. Selection Sync .-> MD
end
subgraph PDFEngine ["PDF & Export Layer"]
MD --> PM[pdfModel.js - Styled Blocks]
PM --> PB[pdfBuilder.js - jsPDF Vector Stream]
PB --> PA[pdfArtifact.js - Content-Keyed Cache]
PA --> EXPORT[Export: PDF / DOCX / RTF]
end
subgraph ATSPipeline ["ATS Analyzer Engine (Local)"]
PA --> PT[pdfText.js - pdf.js Text Layer Extractor]
PT --> EVAL[atsPdfEvaluation.js - 19 Scoring Rules]
EVAL --> SCORE[ATS Score % + Gaps + Report]
SCORE --> FIXES[markdownFixes.js - 1-Click Fixes]
FIXES -. Apply Fix .-> MD
end
pdfModel.js converts Markdown into structured style blocks. pdfBuilder.js renders real vector text via jsPDF.pdfArtifact.js caches generated binary blobs based on a hash of the Markdown text and layout settings, ensuring scores and downloads never drift.pdfText.js invokes pdf.js to extract plain text per page, replicating how Enterprise ATS software (Workday, Taleo, Greenhouse) ingests resumes.atsPdfEvaluation.js checks 19 rules worth 100 points (see the table above): text layer existence, length, contact info, section structure, headings the parser cannot read, Markdown residue, exotic characters, action verbs, numbers, weak phrasing, duplicated bullets, keywords, bullet formatting, document language, dates and chronology, file name, page density and keyword stuffing. It can also score any PDF you already have (evaluatePdfFile).The AI assistant is entirely optional and entirely local. DownCV never proxies your CV to anyone: it only issues one POST request to the endpoint you configure, and it talks to Ollama's OpenAI-compatible API, so any local runtime exposing /v1/chat/completions works.
Ctrl+K β Open AI assistant) and turn on Enable AI in the preview.http://localhost:11434/v1/chat/completions), the model name and, if your runtime requires it, an API key. Press Test connection to confirm the model answers.enhanceBulletPoint() (weak-verb replacement, metric detection), then Generate with Ollama asks your model to rewrite the fragment keeping the exact same meaning but with stronger action verbs and quantified impact.Ctrl+Z.The system prompt (aiEnhancer.js) is set to "You are an expert ATS resume writer and career coach", and the request is stripped of quotes so the result can be pasted straight back into the document.
Clone the repository:
git clone https://github.com/AcoranGonzalezMoray/DownCV.git
cd DownCV
Install dependencies:
npm install
Start the development server:
npm run dev
Open your browser at http://localhost:3000.
Run tests:
npm run test:run
Lint and format code:
npm run lint
npm run format
The ATS rewriter needs a local model runtime. With Ollama:
ollama serve # start the server (default port 11434)
ollama pull llama3 # or any other chat model you prefer
Then in DownCV open the AI Assistant dialog, enable it, and press Test connection. Any runtime that speaks the OpenAI /v1/chat/completions schema (Ollama, LM Studio, llama.cpp server, vLLMβ¦) works by changing the endpoint.
DownCV can be easily containerized and served using Nginx.
docker compose up -d --build
Access DownCV in your browser at http://localhost:8080.
docker build -t down-cv .
docker run -d -p 8080:80 down-cv
DownCV/
βββ public/ # Static assets, app icons & PWA manifest
βββ src/
β βββ components/ # React components
β β βββ ATSAnalyzer.jsx # ATS score display, recommendations, 1-click fixes
β β βββ AIAssistantModal.jsx # Ollama endpoint / model settings & connection test
β β βββ AiRewritePopover.jsx # AI rewrite suggestions popover
β β βββ ContactWarnings.jsx # Unverified email/phone/link banner & validator
β β βββ CoverLetterModal.jsx # Cover letter generator & PDF exporter
β β βββ CVPreview.jsx # A4 paper preview with direct formatting tools
β β βββ ExportMenu.jsx # Dropdown menu for PDF, DOCX, RTF, PNG, JSON, HTML
β β βββ ImportModal.jsx # Import PDF, DOCX, TXT, or MD into Markdown
β β βββ MarkdownEditor.jsx # Caret shortcuts, sample CVs, search, draft history
β β βββ StyleControls.jsx # Font, color palette, margin, line height & layout presets
β β βββ ThemeToggle.jsx # Dark / light mode toggle
β β βββ VariantsPanel.jsx # Named CV versions per job offer & local .md file
β βββ data/ # Static seed data & i18n strings
β β βββ sampleCVs.js # Ready-to-edit sample CVs per language
β β βββ snippets.js # Reusable section snippets
β β βββ templates.js # Layout template presets
β β βββ translations.js # English & Spanish UI strings
β βββ hooks/ # Custom React hooks
β β βββ useContactVerification.js # Verification logic for email/phone/URLs
β β βββ useKeyboardShortcuts.js # Global hotkeys (Ctrl+S, Ctrl+Z, etc.)
β β βββ useLocalFile.js # File System Access API .md linking
β β βββ useLocalStorage.js # Synced localStorage state
β β βββ useMarkdownHistory.js # Pure undo/redo state stack
β βββ utils/ # Pure business logic & formatting modules
β β βββ aiEnhancer.js # Local bullet enhancer + Ollama OpenAI-compatible client
β β βββ atsDimensions.js # Rule groups behind the radar chart
β β βββ atsPdfEvaluation.js # Main ATS evaluation orchestration (19 rules / 100 pts)
β β βββ atsReport.js # Standalone HTML score report
β β βββ atsScorer.js # Action verbs, keywords & weak phrase lexicons
β β βββ contactScan.js # Contact parser & regex scanner
β β βββ coverLetter.js # Template logic for cover letter creation
β β βββ cvImport.js # Converter from arbitrary text to clean MD
β β βββ cvVariants.js # CV version creation & keyword diffing
β β βββ docxGenerator.js # OOXML (.docx) and RTF document exporter
β β βββ jobMatcher.js # Job description keyword match score
β β βββ jsonResume.js # JSON Resume (jsonresume.org) import/export
β β βββ markdownFixes.js # Pure 1-click Markdown issue resolution
β β βββ markdownParser.js # marked AST parser with source line offsets
β β βββ pdfBuilder.js # Vector PDF rendering engine using jsPDF
β β βββ pdfText.js # pdf.js text layer reader worker
β βββ App.jsx # Main application layout & state coordinator
β βββ main.jsx # Application entry point
βββ Dockerfile # Multi-stage production build
βββ docker-compose.yml # Local Docker configuration
βββ vite.config.js # Vite build configuration
We have an active product roadmap! Check out our planned features and technical improvements:
Contributions are warmly welcomed! Whether you want to fix a bug, add a new ATS scoring rule, translate to a new language, or suggest a feature:
git checkout -b feature/AmazingFeature).git commit -m 'Add some AmazingFeature').npm run lint && npm run test:run).git push origin feature/AmazingFeature).Distributed under the MIT License. See LICENSE for more information.
Crafted with β€οΈ for privacy and career success.