v1.23.2
gildas-lormeau/SingleFilev1.23.2Aug 31, 2026by gildas-lormeau
AI Summary
This release focuses on fixing critical bugs in font handling, stylesheet preservation, and self-extracting archive extraction. It also improves error reporting for extension requests and resolves memory leaks related to shadow roots.
Key Highlights
- Stylesheet attributes (id, class, data-*) are now preserved when replacing stylesheets, preventing data loss in plain saves.
- Self-extracting archives now correctly decode legacy entry names and prevent duplicate stylesheets from causing missing images.
- Inline SVG is no longer removed when images are blocked, and tainted canvas elements are now reported instead of dropped silently.
- Chromium extensions now receive a reason when their capture request is refused, replacing the previous silent timeout.
- Filename handling now preserves every character, preventing text like `C++` from being corrupted into `C+`.
Full Release Notes
### Fixes Fonts and stylesheets - Fonts declared inside a frame whose content cannot be read are no longer all removed, which left the text of those frames in a system font - Replacing a stylesheet element no longer drops the attributes that identify it: `id`, `class`, `data-*` and `title` are kept. Plain saves lost them on every page with an external stylesheet - A `font-family` built with `var()`, a multi-word family name, or a custom property naming another custom property is now resolved when removing unused fonts - A `font` shorthand the parser cannot read, such as `font: bold var(--font)`, no longer counts the fonts it names as unused - A font value that cannot be resolved no longer switches off font removal for the whole document - Vendor-prefixed values the browser supports are kept - Font faces already declared with the same sources are no longer embedded twice - Circular `@import` rules no longer make the capture loop until it times out Page content - Inline SVG is no longer removed when images are blocked, which deleted charts, icons, gradients and filters - The constructed stylesheets of a closed shadow root are no longer lost - The canvas image of a page that was already saved is kept - A canvas tainted by a cross-origin resource is now reported instead of being dropped silently - Video posters are stored in a lossy format, and autoplay is dropped from media elements left without a source - A listener added for adopted stylesheets was never removed and accumulated one per shadow root per save Filenames - Every character is kept when replacing the characters a filesystem rejects; a run of them was collapsed, so `C++` was saved as `C+` - The ellipsis counts toward the filename length limit Self-extracting archives - Self-extracting pages can read the names of the entries they contain. They declare windows-1252, and a non-ASCII character in the inlined ZIP library made every legacy entry name decode to garbage - Shared copies of duplicate stylesheets no longer keep the addresses of the original page, which left the images they reference missing from the archive - A stylesheet shared by duplicate `<style>` elements is stored once instead of twice - Saving an archive no longer asks the captured site for a ZIP worker three times - Script resources are named with a `.js` extension, so a module script saved from a `.ts` URL is no longer refused when the archive is opened - The compression of a resource is decided from its content type when there is one - The SingleFile comment and the canonical link are withheld from a password-protected archive; both carried the page URL in the clear - The `%PDF-` header stays within the range PDF readers scan whatever the length of the page doctype - Extracting the ZIP data from the document is hardened: the data is addressed by an identifier instead of its position, the candidate nodes are checked against the payload, and a failed download falls back to the page data - An embedded image or PDF the wrapper ladder cannot hide is left out of the file instead of being written unhidden - Editing a self-extracting archive and saving it again keeps the options it was created with, read from the file itself Extension API - An extension whose capture request is refused is now told why on Chromium. It previously received no answer at all and could only time out