Doop
Doop is an open-source multiplayer design canvas and design MCP for Claude Code, Codex, and any AI agent. Design side by side with agents — free in beta.
A focused editor for one thing: the cover letter. Pick a template, write in blocks, tune the design, and export a clean A4 PDF.
DesignTypeScript · JavaScriptGitHub @Anujrathee7OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Cover letters that look designed
Translation pending
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
A cover letter builder that does one thing.
Most tools I tried were either a word processor with no opinion about design, or a template gallery
with a hundred choices and no way to tell which ones were any good. Letterly picks a middle path:
you write in blocks, you pick from enumerated design tokens, and you can't accidentally make it
ugly. The whole thing runs in your browser. No account, no backend, no network.
It's not finished. The known limitations are at the bottom and they're real ones, not humblebrags.
Try it at getletterly.vercel.app.Read the full README ↗A cover letter builder that does one thing.
Most tools I tried were either a word processor with no opinion about design, or a template gallery with a hundred choices and no way to tell which ones were any good. Letterly picks a middle path: you write in blocks, you pick from enumerated design tokens, and you can't accidentally make it ugly. The whole thing runs in your browser. No account, no backend, no network.
It's not finished. The known limitations are at the bottom and they're real ones, not humblebrags.
Try it at getletterly.vercel.app.
Pick a template, write in blocks, drag them around, tune the design, export a PDF. The preview is a real A4 page at real size — what you see is what the PDF gives you.
The PDF is the part I care most about. It's a real vector PDF: selectable text, embedded fonts,
clickable links. There is no screenshot path anywhere in the codebase, and no html2canvas. If you
open the export in a PDF reader and hit Ctrl+F, it finds your words.
| Template | What it is |
|---|---|
minimal | A classic business letter. One typeface, wide margins, quiet. |
modern | Split letterhead, rules banding the page, results as display numbers. |
creative | Editorial. Oversized serif name, tracked role line, centred pull quote. |
brief | A card grid. Centred section headings over left-aligned bulleted cards. |
profile | Two columns with a tinted sidebar for skills and results. |
timeline | A vertical spine running down the achievements. |
Switching template keeps everything you wrote. It also keeps any design choice you actually changed, and adopts the new template's default for everything you didn't touch.
You never type a colour. You pick from palettes, font pairs and spacing steps, and the panel tells you the contrast ratio of your text against the page. That's the whole design principle: make good design difficult to mess up.
The app follows your system theme, or you can force it. The letter itself never changes — it stays white paper in both, because that's what comes out of the printer.
You need Node and npm.
npm install
npm run fonts # downloads 19 TTF faces into public/fonts
npm run dev # http://localhost:3000
One rule that will save you a debugging session: run npm run fonts, don't hand-roll it. The old
Google Fonts /css?family= endpoint returns EOT files to some user agents. fontkit rejects those
with Unknown font format and every single PDF export dies. The script uses the css2 endpoint and
checks the sfnt magic number of every file before writing it — including files already on disk, so a
poisoned cache can't survive.
Production build:
npm run build && npm start
One document schema, one style resolver, two renderers.
LetterDocument (plain JSON)
|
style resolver src/lib/render/resolve.ts
(theme + template + block -> tokens, in PDF points)
|
+--------------+--------------+
v v
DOM renderer PDF renderer
src/components/blocks src/lib/pdf
(x PT_TO_PX, inline styles) (raw pt, react-pdf)
| |
v v
the A4 preview Firstname_Lastname_Cover_Letter.pdf
Both renderers are deliberately stupid. They own structure and nothing else — every colour, size, weight and spacing comes from the resolver. Neither file contains a literal hex value or font size, and that's enforced rather than trusted:
node scripts/check-gate.mjs
The resolver emits PDF points. react-pdf treats bare numbers as points, so the PDF side uses them
raw; the DOM side multiplies by 96/72. Every length goes through round2(). That rounding is the
thing that stops the two outputs drifting apart.
A template is data, not components (src/lib/templates/). There's one component per block type,
shared by all six. if (templateId === 'creative') inside a renderer is a bug.
| Path | What's in it |
|---|---|
src/lib/schema/ | LetterDocument, the block union, defaults, and 27 rules that repair anything read from storage |
src/lib/render/ | The resolver. Pure — no React, no DOM, no react-pdf |
src/lib/templates/ | The six templates, as data |
src/lib/pdf/ | PDF renderer, the pre-export checks, font registration |
src/components/blocks/ | DOM renderer. Store-free, pure props |
src/components/editor/ | Shell, canvas, block rail, inspector |
src/lib/store/ | zustand store, history, selectors |
src/lib/storage/ | localStorage repository |
Two token systems that never mix: the app chrome is Tailwind CSS variables, the document theme is
data inside document.theme applied as inline styles. A cover letter never inherits app chrome —
the page root carries .letter-canvas to enforce it, which is also why the letter stays white paper
in dark mode.
The suites are Playwright scripts. Start a production build, then:
node scripts/acceptance.mjs # the whole journey, in a real browser
node scripts/test-dnd-design.mjs # drag-and-drop and every design control
node scripts/qa-structure.mjs # block cap, deletes, rapid-fire storms
node scripts/qa-state.mjs # corrupted localStorage, quota, undo bounds
node scripts/qa-ui.mjs # breakpoints, overflow, focus rings, dead buttons
node scripts/qa-content.mjs # 15k-char pastes, unicode, XSS as literal text
node scripts/qa-theme.mjs # the landing theme stays on the landing page
node scripts/verify-parity.mjs # every word in the editor reaches the PDF
These need no server at all — they drive the module graph directly:
node scripts/check-gate.mjs # renderer purity
node scripts/verify-resolver.mjs # the resolved-style contract
node scripts/render-pdf.mjs brief out.pdf # render any template, no browser
node scripts/verify-pdf.mjs out.pdf # prove it's vector, not a picture
node scripts/pdf-geometry.mjs out.pdf --rects # where everything actually landed
pdf-geometry.mjs exists for a specific reason. verify-parity.mjs compares text, not geometry,
so the two renderers can drift several points apart per item with every word still present in both.
That happened. It took measuring filled rectangles out of the PDF content stream to catch it.
Real ones.
/dev/pdf-smoke ships in the build. It's inert and only reachable by typing the URL, but it
should come out before this is ever public.The bundled typefaces come from Google Fonts under the SIL Open Font License: Inter, DM Sans, Source Sans 3, Playfair Display and Space Grotesk.