Skip to content

Releases

1.13.0

Choose a tag to compare

@github-actions github-actions released this 14 Nov 15:13

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.json says 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.json lists every style, palette, genre, font, format and mockup, and /api/openapi.json describes the API as OpenAPI 3.1.
  • The MCP server shows what it drew: the hosted server (/mcp) answers create_cover and create_mockup with 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); createPrintCover holds 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.js is 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 take lang too. The app names it under the title ("Set as Turkish text, from the link"), with a way to clear it.
  • download=1 on any image of the HTTP API answers it as a file named after the title (download=my-cover names it, my-cover.png too; 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, as createPrintCover checks them. The app's print panel reads it too.
  • Many covers saves JPEGs too, as KDP takes eBook covers.
  • createCover's palette takes your own colours as a link writes them (custom-0b132b-f8f9fa-ff6b6b), so the CLI's --palette and 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, as createPrintCover checks 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-After after a 429, and a download's name, and the OpenAPI description of /mcp says 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 texture and svg as 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 texture written as text ("true", "1") as links do, and takes a blank genre in list_styles as 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 ' is s).
  • 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".
  • registerPalette refuses 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 kind or PNG scale in the HTTP API is the default, as a link's other blank choices are, instead of a 400.
  • fitText keeps 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).
  • fitText refuses a maxLines that 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 constructor or toString as any other family.
  • The edge cache keys an answer by the values the API reads: title=A&title=Dune is 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-reader is the e-reader mockup and premiumcolor the premium colour paper. titleCase takes spaces around it.
  • An empty --palette beside your own colours is no conflict in the CLI, as in links and MCP (one rule for all three).
  • robots.txt keeps 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 auto or random beside 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 ...
Read more

1.12.0

Choose a tag to compare

@github-actions github-actions released this 13 Nov 07:32

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.png and /api/mockup.png draw what the .svg endpoints 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 (scale to 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 a 422 (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

Choose a tag to compare

@github-actions github-actions released this 13 Nov 05:46

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. fitText and fitStack take a lang for their upper.
  • 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

Choose a tag to compare

@github-actions github-actions released this 13 Nov 00:56

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 and svgToPdf (in ./browser) the PDF; spineWidth, trimSizes, paperTypes and printLimits give 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: --height alone keeps the paperback shape, as --width alone does.

1.9.2

Choose a tag to compare

@github-actions github-actions released this 12 Nov 22:22

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: seed defaults to the title and author (as Cover.seed shows), not a hash of them; texture is on for any truthy value; TitleBlockOptions.maxSize may be exceeded by a short one-word title; FitOptions.maxHeight counts accents above the first line; textGroupAttrs takes px and writes attrs as given; svgToBlob's type names its charset.

1.9.1

Choose a tag to compare

@github-actions github-actions released this 12 Nov 21:55

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, BookCover is 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

Choose a tag to compare

@github-actions github-actions released this 12 Nov 21:50

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 --mcp or 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 named constructor.
  • 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's palette is its own id; a style's subtitle holds the credit when there is no subtitle. isHex is no longer typed as a type guard.

1.8.0

Choose a tag to compare

@github-actions github-actions released this 12 Nov 19:51

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, or import '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 --mcp speaks the Model Context Protocol over stdio (claude mcp add book-covers -- npx -y book-cover-generator --mcp), and the site at https://covers.otsirh.dev/mcp over 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 --format takes a shape too (--format 10x16 --width 1600), and --height with 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

Choose a tag to compare

@github-actions github-actions released this 12 Nov 18:09

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 (genres in listStyles(), and the genres list), and createCover({ 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 --list shows each style's genres. A book list can give each book its genre, in a genre column: 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.svg a 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 --batch make 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. FONTS entries have a name, and styleIdFor(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 -o makes the folder it writes into (as --out-dir does).
  • 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

Choose a tag to compare

@otsirh otsirh released this 12 Nov 13:43

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 a title column, 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, book turned with its spine showing, ereader) or on a social card (card 1200 × 630, square 1080 × 1080, story 1080 × 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. createCover also returns the cover's colors, title and author.
  • 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, and noteText and recordText for styles that write their own). In the app it sits with the series and credit; in the CLI, --badge.