Repository navigation
Releases
1.13.0
The HTTP API and the MCP server describe themselves, the MCP server is in the official registry, and print covers come in every KDP trim. Covers are drawn as in 1.12.0, except where text was cut short, overflowed or overlapped, which now reads right.
Added
- The HTTP API describes itself:
/api/cover.jsonsays what a query draws (the style, palette, colours, seed, size and pins it came to, its link with every choice written out, and its SVG, PNG, edit and share URLs),/api/catalog.jsonlists every style, palette, genre, font, format and mockup, and/api/openapi.jsondescribes the API as OpenAPI 3.1. - The MCP server shows what it drew: the hosted server (
/mcp) answerscreate_coverandcreate_mockupwith the picture (a PNG up to 480 px wide), so the assistant can see the cover and your client can show it; every server adds a link to the PNG (pngUrl). - All of KDP's paperback trims:
trimSizes(and the app's print panel) has the sixteen, from 5 × 8 to 8.5 × 11 with 5.06 × 7.81, 6.69 × 9.61, 7.44 × 9.69, 7.5 × 9.25, 8 × 10, 8.25 × 6, 8.25 × 8.25, 8.5 × 8.5 and A4 (8.27 × 11.69);createPrintCoverholds the largest to the fewer pages KDP prints at them (8.5 × 11 at most 590 on white paper), and says when a paper isn't offered at a size. - An about page (
/about/, "Using your covers" in the footer): you may use the covers you make for anything, books you sell included, with no credit needed; what stays private; which fonts covers use and why a cover looks different elsewhere; and how to upload ebook and paperback covers to Amazon KDP. - One file for
<book-cover>:https://covers.otsirh.dev/element.jsis the element and the library as one minified module (48 KB compressed), for pages that would rather load one file than the two dozen of/src/element.js. - The MCP registry: the server is listed in the official MCP registry as
io.github.otsirh/book-cover-generator(npm and the hosted/mcp), published by each release. - A design's language in its link (
lang=tr): the app, the HTTP API, kept designs and the gallery draw it with that language's capitals and letterforms, and the MCP tools takelangtoo. The app names it under the title ("Set as Turkish text, from the link"), with a way to clear it. download=1on any image of the HTTP API answers it as a file named after the title (download=my-covernames it,my-cover.pngtoo;download=0, or empty, doesn't).- Keep is a star on the preview: it shows whether the design on show is kept, keeps it (or press K), and pressed again removes it. Removing a kept design, here or in the tray, offers Undo.
- Undo and Redo with Ctrl or Cmd+Z too (Shift for Redo, or Ctrl+Y), outside text fields, and with single-key shortcuts off. On a phone, Undo and Keep are in the bar along the bottom, and the previous and next style beside the pinned preview.
pageRange(trim, paper): the page counts a paperback can have at a trim on a paper ([24, 590]at 8.5 × 11 in on white), or null where the paper isn't printed at that trim, ascreatePrintCoverchecks them. The app's print panel reads it too.- Many covers saves JPEGs too, as KDP takes eBook covers.
createCover'spalettetakes your own colours as a link writes them (custom-0b132b-f8f9fa-ff6b6b), so the CLI's--paletteand a book list's palette column take a value copied from a link.printLimits.trimPages: the most pages at the named trims that take fewer (8.5 × 11 in: 590 on white paper), by trim and paper, ascreatePrintCoverchecks them. The app's page-count field keeps to them. (KDP states no such limits for a trim of your own.)
Fixed
- A page on another site can read the HTTP API's and the MCP endpoint's
Retry-Afterafter a 429, and a download's name, and the OpenAPI description of/mcpsays it takes a batch and answers 400 and 405. - A seed cut to its 40 characters just after a space is trimmed again, so a design and the API's link of it are the same design (a kept design showed as not kept).
- Keeping or removing a design writes the tray out once, not again for each way of fitting a full storage.
- A pin given with spaces (
forest) is drawn and written in the cover's link alike, so the MCP server's links draw the cover it describes. - The MCP server takes a title given as a number (1984), reads
textureandsvgas links read on and off (true or false, 1 or 0), and refuses a seed, kind or line of another type instead of setting it aside. - A design's link page is checked again on every visit, as the app's page and scripts are, so a page kept from before a deploy never meets newer scripts.
- A page count past what the trim takes names the trim's limit first time (8.5 × 11 in: 900 pages says "at most 590", not "up to 828"), and "An 8.5 × 11 in book" reads right.
- The MCP server refuses an object or a list given as a text argument instead of drawing "[object Object]", reads
texturewritten as text ("true", "1") as links do, and takes a blank genre inlist_stylesas none. - A pin of a registered style named in capitals survives a link (it's read back in any case), and pin values and language tags are trimmed, as links read them.
- A seed is trimmed, as a link reads it, so a cover's link draws that very cover (
seed: ' s 'iss). - A link leaves out a registered style's pin it can't hold (a name or value other than letters and digits) instead of becoming unreadable.
- Print settings kept with a paper the trim isn't printed on open on white paper, not on an error.
- A word too long for its line is broken so that no line starts or ends with a space, and, inside a word already broken, a closing mark (
?,») stays with its word; a word that fits whole is never split for it, and a web address breaks after its dots and slashes. - Print settings kept from an older version with a page count the trim no longer takes open on the default count, not on an error. The page field is marked wrong only when the error is about pages.
- An export scale on an iPhone is the largest that fits its canvas (it could come out a step smaller).
- A link preview's fallback picture (the redirect to the site's own image) carries the API's usual headers, so a page on another site that fetches it follows it.
- The app's contrast note shows the ratio rounded down, so a colour just under the 3:1 limit never reads "3.0:1, too low".
registerPaletterefuses an id a link reads as your own colours (custom-rrggbb-…), which no link could choose.- A print cover's imprint past its 80 characters ends in "…", as the hook and the blurb do.
- The local MCP server recognises an SVG whose comments or metadata run past its first 16 KB, and replaces it as asked.
- The app's print panel says why the spine is blank from the library's own page limit.
- Typewriter's rubber stamp never lands on the typed text: on a square or wide cover with a long block it goes below it, or is left off when there's no room.
- The book mockup of a wide cover keeps its floor shadow on the picture (it was cut at the right edge).
- A blank mockup kind is the book everywhere: in the library, MCP and the HTTP API.
- The HTTP API's edge cache keeps one answer for a query with blank parameters and the same query without them.
- A blank mockup
kindor PNGscalein the HTTP API is the default, as a link's other blank choices are, instead of a 400. fitTextkeeps every line within the width even in a box far narrower than its largest size (a single letter could overflow it): it sets the size first, then breaks the lines, so text that fits isn't cut.- A plain book list's line of more than three parts keeps the rest in the subtitle instead of dropping it.
- A mockup's line is cleaned as the cover's text is: a control character is a space, a no-break space stays.
- A very long blurb or hook is cut without first splitting the whole of it into characters.
- A blank style, font, palette, genre or language in a link (
style=%20) is left to the seed by the HTTP API too, as the app reads it, instead of a 400. - The square and story cards keep a long author and line inside their margins (they could run off the bottom).
fitTextrefuses amaxLinesthat isn't a whole number from 1 (0 meant no limit).- A book list in JSON lines says which line has an object or a list for a title, author or other cell, instead of drawing "[object Object]".
- A style that prefers dark (or light) palettes, with none of that kind registered, picks from all of them instead of failing.
- The PNG fonts measure a family named
constructorortoStringas any other family. - The edge cache keys an answer by the values the API reads:
title=A&title=Duneis Dune's answer, drawn once. - Text cut short ends in a single ellipsis: a word that already ended in "…", a comma or a hyphen no longer shows "……", ",…" or "-…" (titles, card lines, the spine, the imprint and the back's blurb).
- A blurb whose first paragraph filled its 3,000 characters no longer ends on a paragraph of "…" alone.
- Mockups and paper are named as other choices are:
E-readeris the e-reader mockup andpremiumcolorthe premium colour paper.titleCasetakes spaces around it. - An empty
--palettebeside your own colours is no conflict in the CLI, as in links and MCP (one rule for all three). robots.txtkeeps crawlers that take the first matching rule out of the API too.- A title typed on more lines than a place takes has its last lines joined instead of shrunk and cut off (the card, square and story mockups, and their link-preview images, cut a fifth line).
- A blurb or hook too long for its limit ends in an ellipsis, not silently mid-word.
--palette autoorrandombeside your own colours in the CLI is no longer refused as a conflict, as in links and MCP.- A style, palette, genre or font typed without its spaces (
BigLetter,retrosun) is found everywhere. - The app's PDF export draws a big paperback in one go on desktops, instead of ...
1.12.0
PNG covers from the site, and link previews of a design. Covers are drawn as in 1.11.0.
Added
- PNG from the HTTP API:
/api/cover.pngand/api/mockup.pngdraw what the.svgendpoints do as PNG, for email, chat and social posts: a cover up to 600,000 pixels (600 × 900), a mockup up to 2.1 million (a 1080 × 1920 story; a flat or e-reader shot 1.3 million), larger ones drawn smaller to fit (scaleto choose). The site's Worker draws them with resvg and open fonts (Noto, Liberation, EB Garamond, Roboto Condensed, Anton, TeX Gyre Adventor), laying the text out in their own widths; text in a script they lack gets a422(the SVG draws it). - Link previews of a design: Copy link gives a link that holds the design in its query,
https://covers.otsirh.dev/?title=…, and a link preview of it (on X, Facebook, Slack, Discord, iMessage…) shows that design, as a social card. The app opens it as before.
Fixed
createPrintCover's trim no longer makes a slow pattern of a long run of spaces (a CodeQL finding): the text is trimmed and capped first.
1.11.0
Capitals in the cover's language. Covers without a language are drawn as in 1.10.0; Turkish and Azeri covers, and Big Letter for an initial with an accent on its I, change.
Changed
- Capitals follow the cover's language (
lang): a Turkish or Azeri title's i is İ in every style that sets capitals, on its badge and its mockups, as on the print cover's spine.fitTextandfitStacktake alangfor theirupper. - Big Letter sets two letters for an initial İ, Í, Ì, Î or Ï, as it does for I: one alone reads as a slab.
- A cover's SVG ids include its language, so two covers that differ only in it can share a page (Japanese and Korean covers, whose language is inferred, get new ids).
1.10.0
A paperback for print: the back, the spine and the front as the PDF Amazon KDP takes. Covers are drawn as in 1.9.2, but for Typewriter's square covers with a series.
Added
- A paperback for print: the whole cover of a printed book, the back, the spine and the front, as the PDF Amazon KDP takes. In the app, Paperback for print sizes it from the trim, the page count and the paper (an eighth of an inch of bleed, a spine as wide as the pages are thick, blank under 80 pages as KDP requires), sets your headline, back cover text and imprint on the back with the barcode's box left clear, carries the front's art into the bleed, and downloads a PDF at 300 dpi with its trim marked. In the library,
createPrintCover(cover, print)makes it as SVG andsvgToPdf(in./browser) the PDF;spineWidth,trimSizes,paperTypesandprintLimitsgive the numbers. On the command line,--print 6x9 --pages 320.
Fixed
- On phones, Many covers sat above the preview at the top of the page; it now follows the export buttons, with Kept designs and the print panel. On wider screens, the preview stays in view beside the controls however many designs are kept, and never covers what's under it.
- On tablets, the controls (kept in view as the page scrolls) no longer lie over the styles below them.
- On phones, a field reached with the keyboard is scrolled clear of the pinned preview, in Safari too (it stayed under it). Focus on a variation chip stays on one when the style changes, rather than falling back to the page.
- Typewriter keeps a tall title block below its typed header on square covers (a series line could overprint it).
- Kept designs: one kept again with a new drawing shows the new one in the tray; importing a file with no designs in it says so (not "kept already").
- The Title field is named by its label alone (its hint describes it, and was read twice); disabled export sizes look disabled.
- CLI:
--heightalone keeps the paperback shape, as--widthalone does.
1.9.2
The whole API documented, on JSR and in your editor. Covers are drawn as in 1.9.1.
Changed
- The whole API is documented: every function, type and option, with module docs and examples for each entry point (shown on JSR and in your editor). CI keeps it so (
deno doc --lint).
Fixed
- Docs:
seeddefaults to the title and author (asCover.seedshows), not a hash of them;textureis on for any truthy value;TitleBlockOptions.maxSizemay be exceeded by a short one-word title;FitOptions.maxHeightcounts accents above the first line;textGroupAttrstakes px and writesattrsas given;svgToBlob's type names its charset.
1.9.1
The library on JSR, as 1.9.0 meant it to be. Covers are drawn as in 1.9.0.
Fixed
- JSR: the package publishes there (1.9.0 didn't reach it: its
<book-cover>types declared the tag globally, which JSR doesn't allow). From JSR,BookCoveris typed as a class; from npm,document.createElement('book-cover')is still typed as one too. CI now checks the package as JSR's server does.
1.9.0
A gallery of every style, the library on JSR, <book-cover loading="lazy">, and a sturdier MCP server. Covers are drawn as in 1.8.0 (some of their SVG ids are named differently).
Added
- A gallery of every style at covers.otsirh.dev/gallery/, each drawn for a book of the genre it suits (by
<book-cover>, in your browser) and opening the generator in it. It works offline, as the app does, even when it is the first page you open. - JSR:
import { createCover } from 'jsr:@otsirh/book-cover-generator'in Deno. Every module names its types for Deno. <book-cover loading="lazy">draws once it comes near the viewport, as an image does (the gallery's covers do). Until then it holds the cover's shape and is named for screen readers, both following its attributes.- Going to the gallery from the app keeps your design, whichever link takes you there (or opens it in a new tab): a cover there opens your book in its style, with your pins if it is your own style, and the header's links take you back to your design.
- The app's developer section shows every way to use the covers: an image link,
<book-cover>, an AI assistant (MCP) and the library.
Fixed
- On the narrowest phones (320 px), the app no longer scrolls sideways: the variation thumbnails shrink a little to fit.
- MCP: a request whose id JSON-RPC doesn't allow (an object, an array, however deeply nested) is answered as an invalid request instead of stopping
book-cover --mcpor failing at/mcp; a message that isn't a request is answered with an error even without an id, and a response sent to the server needs no answer. A cover's links no longer carry a pin namedconstructor. - Two covers that differ only in their font and their series (say, a series named "serif") no longer share ids, and a mockup's ids are its own, so a cover and its mockups can share a page.
- Opening the app at a place on the page (
#usage, or from the gallery) moves keyboard focus there too. - Docs:
<book-cover loading="lazy">in the README and types; a leading "A" or "An" stays with the title (only longer articles get a line of their own); a custom palette'spaletteis its ownid; a style'ssubtitleholds the credit when there is no subtitle.isHexis no longer typed as a type guard.
1.8.0
Covers wherever you work: an MCP server for AI assistants, a <book-cover> element for any page, Deno and Bun, and signed releases. Covers are drawn as in 1.7.0.
Added
<book-cover>, a custom element for any page: one script (https://covers.otsirh.dev/src/element.js, orimport 'book-cover-generator/element'), then<book-cover cover-title="Dune" author="Frank Herbert" cover-style="retro">. Its attributes are the app's link parameters; it draws again when one changes.- MCP server, so AI assistants can make covers:
book-cover --mcpspeaks the Model Context Protocol over stdio (claude mcp add book-covers -- npx -y book-cover-generator --mcp), and the site athttps://covers.otsirh.dev/mcpover HTTP. Its tools draw a cover or a mockup and answer with links to the image and to the app, and list the styles and palettes. - Deno and Bun: the library runs in both, and CI checks that Node, Deno and Bun draw the same covers byte for byte.
- Each GitHub release carries the npm package and its build provenance, signed with Sigstore: check a download with
gh attestation verify book-cover-generator-X.Y.Z.tgz --repo otsirh/book-cover-generator. (The package on npm has had provenance since 1.7.0.)
Changed
- The CLI's
--formattakes a shape too (--format 10x16 --width 1600), and--heightwith a format, as the HTTP API reads them: the shape at that width or height.
Fixed
- In Many covers, a book whose list gives it a larger size no longer fails to export on an iPhone or iPad: its scale keeps within the device's canvas, as the export's does.
1.7.0
Genres and five styles made for them, an HTTP API that draws covers from a URL, and text that fits in Node as it does in the browser. Covers made without a style may come out in a new one, and text made in Node may be set a little differently (see Changed); covers drawn in the browser with a style are as in 1.6.0.
Added
- Five genre styles: Hollow (horror: a dead tree across a full or blood moon, fog, and worn lettering), Ribbon (romance: ribbons turning over under an italic title, and falling petals; dark palettes make it a dark romance), Crest (fantasy: a heraldic shield before laurels, swords or rays, framed in gold, with its field, emblem and what's behind it to pin), Storybook (children's: hills with trees and a house, a sun or a rainbow, clouds, and a bold outlined title) and Big Idea (non-fiction: a big plain title, a prominent subtitle, and bars, a trend line or a pie). There are 39 styles now.
- Genres. Every style says which genres it suits (
genresinlistStyles(), and thegenreslist), andcreateCover({ genre })picks a random style that suits one. In the app, the style rail's Genre menu shows only those styles (and is remembered), and Randomize, the dice and the arrow keys stay in it. In the CLI,--genre, and--listshows each style's genres. A book list can give each book its genre, in agenrecolumn: a book whose genre the chosen style doesn't suit gets a style that does. - HTTP API.
https://covers.otsirh.dev/api/cover.svg?title=…&author=…draws a cover, and/api/mockup.svga mockup, for an<img>on any page. The query takes the library's option names and the app's link parameters, so an app link's query draws the same design. In the app, Copy image link copies that URL for the design on screen. optionsForBook(book, defaults): a listed book's cover options over the options every book shares, as the app's Many covers and the CLI's--batchmake them.- Names in any case. Styles, palettes, genres and fonts may be given by id or by name, in any case (
'Art Deco','MINT','Non-fiction'), in the library, the CLI, the HTTP API, the app's links and book lists, and fonts by the names the app's menu gives them ("Old style", "Heavy", "Typewriter"); pinned variants too.FONTSentries have aname, andstyleIdFor(name)says which style a name means.
Changed
- Text fits in Node as it does in the browser. Where there is no canvas (Node, the CLI, the HTTP API), the built-in fonts are now measured from their characters' widths as macOS draws them, not estimated: text was up to 15% too wide or too narrow, and a title made in Node could run past its box in a browser. Each weight and its italic are measured on their own, as Chrome picks a different face for 600 or 800. Covers made in Node may set their text at a slightly different size than before.
- With
style: 'random'(the default), a seed can now pick one of the new styles, so covers made without a style may come out in a different one. Covers with a style keep it.
Fixed
- A plain book list line ("Title | Author") is split at the first kind of separator it uses, so a dash in a title stays in it ("Harry Potter – The Philosopher’s Stone | J. K. Rowling"). A size in a list that isn't a number is reported as written.
- The CLI's
-omakes the folder it writes into (as--out-dirdoes). - On an iPhone or iPad, the export sizes the device can't draw (over its canvas's 16.7 megapixels) are unavailable, instead of failing.
- With a random style, a pinned variant that another style names alike (Aurora's and Stripes'
layout) no longer throws when the seed picks the other one: a style picked at random takes only the pins it can. A value no style offers throws whichever style the seed picks, and a style you name still says when it lacks a value.
1.6.0
Badges, mockups and many covers at once. Covers that use none of these are drawn exactly as in 1.5.0.
Added
- Many covers at once.
parseBookList(text)reads a list of books (CSV with commas, semicolons or tabs and a header row naming atitlecolumn, JSON lines, or "Title | Author | Subtitle" lines) into cover options. In the app, "Many covers" previews the list in the current design and downloads them all as PNG or SVG in one ZIP, with "A different palette for each" for a set in several colours. In the CLI,--batch <file|->writes a cover per book into--out-dir, with the other options for every book the list doesn't set (a book that can't be made is reported and the rest are written). - Mockups.
createMockup(cover, { kind, background, text })shows a cover as a product shot (flat,bookturned with its spine showing,ereader) or on a social card (card1200 × 630,square1080 × 1080,story1080 × 1920) with its title, author and a line such as "Out now". In the app, the Mockup row under the export buttons previews and downloads them as PNG.createCoveralso returns the cover'scolors,titleandauthor. - Badges.
badge("Winner of the Hugo Award", "10th Anniversary Edition") is set as a star-edged seal in the first corner (then side) of the cover clear of its text, smaller if it must be. The text styles draw is recorded for this (drawText,drawStack,titleBlock, andnoteTextandrecordTextfor styles that write their own). In the app it sits with the series and credit; in the CLI,--badge.