Skip to content

v0.24.0

Choose a tag to compare

@SysAdminDoc SysAdminDoc released this 30 Jul 21:45
· 165 commits to main since this release

OpenShop v0.24.0

This release closes the last of the audit findings. Most of it comes from finally running things that had only ever been read — the AI pipelines, the WASM filter backend, and the exported PDF and PSD files — and several of them turned out not to work at all.

Every AI feature was broken

Background Removal, Segment Select, Depth Map and Object Detection each handed the model a data: URL. Transformers.js reads a URL with fetch(), and connect-src does not list data: — so the request was blocked the instant the model finished downloading. The model loaded, then nothing happened.

They now receive canvas pixels directly, which also skips a full-image PNG encode and decode. Verified end to end against the pinned MODNet weights: 3.3 s cold including the 25.9 MB download, 0.8 s warm.

Relatedly, the Offline & Install dialog and the backend report both claimed no model was loaded while background removal was holding one — its handles live outside the pipeline map.

Photon's filters are not this app's filters

The optional WASM backend was measured against the JavaScript worker on a fixed 16x16 input. Its grayscale differs by up to 58 levels — it does not use the Rec.601 weights the rest of the app and the histogram use — its sepia by 192, its threshold flips pixels outright, and its sharpen and emboss convolutions zero the alpha of the outermost pixel ring, leaving a transparent one-pixel frame.

Which result you got depended on whether an optional download happened to succeed. Only invert agrees, so only invert is accelerated now. The measurements are a test, so a Photon upgrade that fixes them shows up as a failure rather than going unnoticed.

Exports were the wrong physical size

  • PDF: jsPDF's px unit produced an 800x533pt page for a 600x400 document — an 11.1 inch wide page at roughly 54 DPI. Pages are now sized in points from CSS pixels (450x300pt, 6.25x4.17in at 96 DPI) with the raster still at full resolution, and the file carries a title, creator and language.
  • PSD: no resolution resource was written at all, so the document had no DPI and its physical size was whatever the reader defaulted to. It now declares 96 PPI.

Both were verified by reading the written bytes with tools that share no code with the exporters — an independent PSD parser and veraPDF. Photoshop and Acrobat were not available, so how Adobe renders these files is still unverified; the details are in RESEARCH.md.

Security

  • script-src no longer names a CDN. Fabric, ag-psd and jsPDF were <script src> tags on jsDelivr, and CSP does not require SRI on scripts it permits by host — so an allowance covering three pinned tags also let any HTML-injection sink load an arbitrary npm package. All three are now fetched, SHA-384 verified in page, and executed from blob: URLs, the same path the eight lazy assets already took. Substituted bytes stop the editor with a visible message instead of quietly becoming the engine.
  • Exported SVGs are judged on every href regardless of namespace, against an allowlist. The old [xlink\:href] selector matched nothing — attribute selectors match the local name in the null namespace, and xlink:href is exactly what fabric emits for images — so that branch was dead, and the plain-href checks were case-sensitive.

Correctness

  • Autosave no longer captures a document mid-load. Dimensions were assigned before the canvas finished loading, so a capture landing in that window persisted the old document's content at the new document's size.
  • Recovery ownership stops flapping when another tab owns the stream. Each undo re-installed the contested document id and the next autosave renamed again, orphaning generations that were later offered as unsaved work.
  • Cyclic PSD group metadata no longer silently drops layers from an export.
  • The service worker's runtime cache is versioned with the shell, capped, and refuses opaque responses — an opaque response hides its status, so a captive-portal or CDN error page was kept and served forever.

Cross-engine truth

A capability probe now runs on all three engines and asserts the fallback for each optional platform feature. One finding is worth stating plainly: WebKit gives a file:// origin no origin-private file system, so opening the single HTML file directly in Safari has no auto-save and no crash recovery. Host it and both come back. The browser-support table says so now.

Consistency and performance

  • One treatment per component. Sliders had three thumb designs and Firefox showed the pre-redesign thumb everywhere; there is now one design on both vendor pseudo-elements, verified in Chromium and Firefox. Radii collapse to a 6/8/12 scale, menu-like rows share one hover treatment, and transition:all is replaced by named properties on three duration tokens.
  • Animation playback moves the highlight instead of rebuilding the thumbnail strip on every tick — a 20-frame timeline at 12 fps was doing roughly 240 image decodes a second to move a border.
  • The duplicated model-load path is one helper, and the PSD import stops keeping a second registry of a job the compute controller already tracks.

Tests

64 unit, 69 Chromium end-to-end (including the offline-shell contract), 28 cross-engine on Firefox and WebKit.

Install

Download openshop-v0.24.0.html and open it in a browser, or use the hosted build. Everything runs client-side.