Online cover-art search is verified against the live services, with recorded replies in the test suite. (SLIP-0088)
ScreenScraper, TheGamesDB and libretro were each searched live. The
ScreenScraper and TheGamesDB replies are kept, with every credential
removed, so the tests now check the parser against what the services
really send.
A Settings switch turns libretro search off (SLIP-0071)
libretro needs no login, so it was contacted on every search. A new
libretro tab in Settings turns it off; it stays on by default.
You can render the back of the box (SLIP-0033)
Set View to Back to see the back cover with the spine beside it. It uses
the back cover you load, or the back half of a wraparound scan. Before
this, a loaded back cover was accepted and then not used at all.
You can tell the app an image is not a wraparound cover (SLIP-0051)
A wide front-only image could be mistaken for back + spine + front and
cut into three. Untick "Full cover detected" to use the whole image as the
front.
A subsystem map for review tooling
docs/subsystems.md and .indie-review/partition.json divide the code by what it does rather than by directory, so a review is briefed per concern.
SLIP-0086 stays open until the Ants review tooling reads the map.
CONTRIBUTING.md, with steps that have been run (SLIP-0084)
Setup, the gate, what a change should look like, the commit shape and where to report things. Every step was executed against a fresh clone rather than only written down.
The shell formatter now has a style to check against (SLIP-0085)
An .editorconfig declares the Python and shell indentation the project
already uses, so shfmt no longer skips every run for want of a config.
SECURITY.md, with a private way to report a flaw (SLIP-0083)
GitHub private vulnerability reporting is enabled, and the file states what
is in scope, what is not, and which release is supported.
The test suite runs automatically on every push (SLIP-0022)
scripts/local-ci.sh holds the one list of checks and the GitHub workflow
calls that same script, so the local push gate and CI cannot drift apart.
Regression tests for the security invariants and for every defect fixed in this release
api/ had no coverage at all, so the URL allowlist, credential scrubbing and
the download limits could each have been removed by a refactor without a test
noticing.
Search previews load a small copy of each cover instead of the full image. (SLIP-0093)
Clicking through results now fetches tens of kilobytes per cover rather
than up to a megabyte. The full-size cover is downloaded once, when a
result is chosen.
All window and dialog text is ready for translation (SLIP-0094)
The settings, search and animation dialogs, and the main window's status
messages and message boxes, now route their text through Qt's
translation system. No translations ship yet.
Settings honour XDG_CONFIG_HOME (SLIP-0050)
Settings live in $XDG_CONFIG_HOME/slipcase/ when that variable is set to
an absolute path, and in ~/.config/slipcase/ otherwise. A config already
at ~/.config is not moved: if you set the variable, copy it across.
STANDARDS.md is the one home of the security, performance and memory rules (SLIP-0092)
CLAUDE.md now points at STANDARDS.md sections 10-12 instead of keeping
a second copy that could drift.
Dragging the spine adjustment sliders is smooth (SLIP-0067)
The app no longer re-searches the whole cover for the spine on every
step; it redraws the split preview once you pause.
Spines are drawn about twice as fast, and resizing the window is smoother (SLIP-0047)
Fonts are loaded once instead of about thirty times per spine, and the
preview is re-scaled once you stop dragging rather than on every step.
Only one copy of Slipcase runs at a time (SLIP-0049)
Opening it again brings the window that is already open to the front.
Two copies used to overwrite each other's settings, which could lose login
details you had entered.
The settings file now records its version, so later releases can upgrade it safely (SLIP-0042)
Settings saved by a newer version are kept intact by an older one. A
damaged settings file that holds something other than settings no longer
stops the app from starting, and is left untouched.
The standards document now matches the code it governs (SLIP-0081)
An independent cold review of STANDARDS.md, run because the shutdown rule in
section 12 had been rewritten without one. That section turned out to be
correct; thirty-one other claims were not. Among them: the decompression-bomb
ceiling was credited to the wrong file, so following the document could have
removed the only copy that protects a test or a batch child; the download
allowlist was described as covering image downloads when it gates every
request; the test suite was said to finish in under five seconds when it takes
about twenty; and the status-bar colour rule described two themes when seven
ship. The review also surfaced two real defects in the code, filed as
SLIP-0090 and SLIP-0091.
The export width is capped at a size the app documents (SLIP-0037)
The width box accepted values that could use all available memory before anything was saved.
Renders are faster and slightly sharper (SLIP-0043)
Every render did two full image-resize passes that changed nothing. Measured at 512px: about 8% quicker, and cover art is no longer resampled twice.
Builds install exactly pinned dependencies (SLIP-0023)
requirements.lock holds the exact versions, transitive ones included, so two builds of one release bundle the same libraries. requirements.txt stays as the readable declaration of what the project depends on.
PNG exports are written atomically
An interrupted save could replace a good file with a truncated one.
The documented rendering.supersample setting is now actually read
It was shipped as a default and documented in the config schema, but every
render path hardcoded 2.
PNG compression level is configurable via rendering.compress_level
Default 6, as the code has always used; 9 is roughly 5% smaller and 2-4x
slower. The documented requirement said 9 while every call site used 6.
A refused cover-art search now says why, in the service's own words.
ScreenScraper answers bad developer credentials with a line of text
rather than data, and the search dialog showed "Expecting value: line 1
column 1 (char 0)". It now shows the reply itself, with credentials
scrubbed and long replies cut short. Found by the SLIP-0088 live run.
The left column scrolls instead of squashing the spine panel (SLIP-0095)
In an 800-pixel-tall window with a full cover loaded, "Reset to Auto"
was clipped and the split thumbnails were cut off.
Batch mode now uses your PNG compression setting (SLIP-0091)
It always saved at the default level, whatever the setting said.
Spine titles use a proper bold font on more systems (SLIP-0070)
The app looked for its fonts in a few fixed places and, on openSUSE, found
none and used a thin fallback font. It now finds installed
fonts wherever the system keeps them.
Screen readers now announce each field by name (SLIP-0046)
Fields such as Title, Serial and Angle were read out as unnamed boxes.
Most can also be reached with Alt and the underlined letter of their label.
The progress bar no longer vanishes early when one job ends while another runs (SLIP-0059)
The spine finder no longer moves the spine edge to a faint smudge (SLIP-0058)
Where the cover had no detail at the expected spine edge, any faint mark
nearby could pull the edge towards it.
When the spine finder fails, it now says so instead of hiding it (SLIP-0054)
It falls back to the size-based estimate and logs a warning; a genuine
bug is no longer silently swallowed.
The window reopens where you left it, and stays on a screen you can see (SLIP-0040)
It crept down-right by the height of the title bar on every restart, and
could reopen on a monitor that was no longer connected. It also now
remembers being maximised. After this update the window opens at its
default place once, then remembers again.
Large animations stop with a clear message instead of using up all your memory (SLIP-0036)
An animation that would need more than 1 GB is refused after its first
frame, with a note to lower the width or frame count. Bounce animations
also use half the memory they did.
Choosing a search result no longer downloads its cover a second time (SLIP-0035)
The cover fetched for the preview is reused when you pick the result.
Every case type now has moulded detail on its spine (SLIP-0034)
Jewel, Switch, DS, 3DS, PSP and Vita cases had a plain spine while the
others had grooves or fold lines.
The shadow under the box fades softly instead of ending in a hard edge (SLIP-0032)
It sat too far right and down and was cut off square at the image
edge. It now sits where intended and fades out on every side.
Two smaller search-window fixes (SLIP-0061)
The 3D Boxart button could look available for a game that has none, and a failed preview said only "No preview" while discarding the reason. Covers SLIP-0061 and SLIP-0062.
Clearer failures in the rendering helpers (SLIP-0057)
A width too small to draw at raised an unhelpful maths error, or silently produced a corrupt image. Shading now refuses an unknown direction instead of doing nothing, and the reflection helper accepts an image without transparency. Covers SLIP-0055, 0056 and 0057.
Exporting from a drive root no longer produces a blank name (SLIP-0063)
An image loaded from the top of a drive exported as " 3D Boxart.png". The name is now derived in one place, which falls back to the title and then the file name.
The app shows its own icon in the taskbar (SLIP-0048)
Neither the X11 nor the Wayland route to an icon was set, so the window showed a generic one on both.
The animation dialog shows how many frames will really be written (SLIP-0072)
Bounce replays the sweep in reverse, so asking for 120 frames writes 238. Nothing said so, and the file was about twice the size you would expect.
A damaged colour file no longer stops the app starting (SLIP-0053)
A truncated resources/case_colors.json crashed on launch. It now starts with default spine colours and says why in the status bar; a missing file is reported rather than passing silently.
Search says when there is nothing to search (SLIP-0038)
With no API credentials entered, and on a platform libretro does not carry, search reported "No results found" -- blaming your search term for a missing setup. It now says so and points at Settings.
A failed online search now ends (SLIP-0052)
An unexpected error left the progress bar spinning and the Search button disabled until the dialog was closed.
Exporting without typing .png no longer replaces a file silently (SLIP-0039)
The extension was added after the file dialog had already asked about overwriting, so typing "render" could replace an existing render.png with no warning. Both the image and animation exports are covered.
The standards document and the code agree again (SLIP-0074)
Seven statements described behaviour the code does not have -- transparency in exports, the status bar's text colour, when libretro is searched, how gradients are built, which regional cover is preferred, and two case details that are not drawn. Covers SLIP-0074, 0075, 0077, 0078, 0079 and 0080.
Two remembered settings are now declared (SLIP-0076)
The auto-filename checkbox and the last export folder were saved but listed nowhere, so nothing told you they existed. A test now fails if another setting is saved without being declared.
libretro cover art can now be found by typing a game's title (SLIP-0089)
Thumbnails are stored under the full ROM name with its region tag, so asking
for the bare title matched nothing on any system. The lookup now tries the
exact name first, then the common region tags.
The release recipe no longer claims the project is not under git (SLIP-0029)
It also points at the changelog that exists rather than asking for one to be
created.
About shows the real version (SLIP-0028)
The dialog carried its own copy of the version string, which a bump updated
everywhere else and left behind here.
Credentials are trimmed of stray whitespace before being saved
Browsing search results starts one preview download at a time
Holding Enter in the search box no longer starts overlapping searches
The Generate button and comparison captions follow a theme change
Disabled menu entries are dimmed
A corrupt or non-UTF-8 config no longer prevents the app from starting
Rate limiting is immune to system clock changes
Spine text is readable on systems without the bundled font paths
The fallback font was loaded at a fixed ~10px regardless of the size requested.
Spine text stays legible when a custom spine colour is chosen
The platform accent colour was used regardless of contrast, which could render
white text on a white spine.
A malformed API response no longer raises out of a search
ScreenScraper reports itself unconfigured unless all four credentials are set
libretro thumbnail URLs are percent-encoded
Titles containing '#' or '%' silently failed to download.
Shadow blur looks the same with and without OpenCV installed
The OpenCV path derived its blur radius from the kernel size, giving roughly a
third of the intended blur.
PC now resolves to the DVD case
It was claimed by two case types and which one won depended on declaration order.
Copy to clipboard no longer risks pasting corrupt image data
Export and settings failures are reported instead of terminating the app
A full disk or a read-only folder raised out of a Qt slot, which is fatal.
Unchecking Case Texture now applies to animated exports
The search dialog releases its cached images when a result is chosen
The cache was only cleared when the dialog was cancelled.
Rate limiting now holds across API clients instead of resetting for each search
Each background task built its own client, so the interval only ever applied
within a single task.
Search failures say what went wrong rather than reporting no results
Network, TLS, authentication and rate-limit errors were all reported as an
empty result list, which made a wrong password indistinguishable from a game
that is genuinely not in the database.
Batch failures are reported instead of being overwritten by the progress message
A batch in which every file failed previously reported success with no cause.
Starting a second render, batch or animation while one is running is refused
It could leave a stale render overwriting a newer one.
Closing the window during a render, batch or animation no longer risks a crash
The shutdown handshake could not stop these workers and then dropped the last
reference to a still-running thread. Workers now honour an interruption request
and a live worker is never discarded.
Batch processing no longer overwrites source images or same-named covers
Output was keyed on the filename stem alone, so two covers named the same in
different folders collided, and choosing the source folder as the output folder
overwrote each source with its own render.
Settings are written atomically and can no longer be lost
Saving truncated the live file before writing, and a config that failed to
load was silently replaced by defaults which the next save then wrote over the
stored credentials. The file is now written to a temp file created 0600 and
renamed into place, and a save is refused while the existing file is unreadable.
The floor reflection mirrors the bottom of the case rather than the top
Renders are no longer stretched horizontally
The final downscale used a different factor for each axis, so every render was
12-14% too wide and did not match the real-world case proportions.