All notable changes to Picvert are documented here. Version numbers follow Semantic Versioning.
- Update progress overlay — when the user confirms an update,
a centred card now shows a smooth percent bar (downloading) and
an indeterminate sweep (installing). Both phases translated in
the four UI languages. Driven by
update-progressevents thatinstall_updateemits on every chunk and on download completion.
- Windows could launch multiple Picvert instances — added
tauri-plugin-single-instance(Windows + Linux only; macOS already single-instances via LaunchServices). A second launch now wakes the existing main (or compact) window instead of spawning another process.
- PDF first-page thumbnail cache — repeated renders return the cached PNG instead of re-rendering through PyMuPDF on every list refresh.
- Retry failed file button on each failed row.
- Localised engine errors — error kinds (
unsupported/not_found/bad_request/internal/engine_died) translate to the active UI language instead of always showing the English Python message. - Click any thumbnail to open the source file in the OS default app (Preview, Adobe Reader, etc.).
- Per-extension breakdown beside the file count (
5 · PDF 4 · JPG 1). - Window state persistence — main window remembers its last position / size across launches.
- Hover spin on the capsule weather icon.
- Batch conversion runs in parallel — sidecar thread pool of 4 workers, JS dispatches up to 4 in-flight conversions. ≈3-4× faster on multi-image / multi-page-PDF batches.
- Compact window is pre-created at startup (visible: false in
tauri.conf.json) to dodge a WebView2 hang on Windows where a runtimeWebviewWindowBuilder::build()for a second webview never returned. - Folder walk hardened — defends against symlink loops and unreadable subtrees; clamps to 5000 files / drop.
- Switched to
tauri-plugin-openerfor shell.open (the previoustauri-plugin-shellopen API is deprecated). - Minimal macOS menu — Picvert / Edit / Window only (was an auto-generated File / View / Help layout with empty submenus).
APP_VERSIONnow reads from package metadata so release bumps only touchpyproject.toml.
- Windows capsule freeze — main panel blanked out and never became
the capsule because the second-webview
build()call hung. The pre-created window approach makes this transition ashow()instead. - Capsule sometimes didn't appear after toggling main ↔ compact
several times (
.leavingclass stuck on the body). - App could exit instead of going to capsule on first close — hide-main / build-compact ordering left zero visible windows briefly.
- Capsule corner halo — replaced
filter: blur(20px)on.card-glowwith radial gradients (compositor leaks past rounded clip on transparent NSWindow). - Tab outline on capsule — removed the default focus ring.
- Auto-updater wired up properly —
bundle.createUpdaterArtifacts: trueemits.app.tar.gz/.exe.sigpairs; regenerated minisign key pair after the previous one was base64-corrupted. - Sidecar consistent CSV naming + multi-sheet XLSX handling.
- Capsule position clamps to current monitor when dragged off screen.
- SVG preview in the file list — SVG files now render inline thumbnails instead of just a text badge.
- PDF first-page thumbnails — sidecar renders ~96 px PNGs on demand
through a new
previewRPC action (~50–100 ms per file, cached). - Click any thumbnail to open the source in the OS default app (Preview, Adobe Reader, etc.). Hover shows a darken-overlay + external-link icon.
- Per-extension breakdown next to the file count, e.g.
5 · PDF 4 · JPG 1(request: "old version had this"). - Hover tooltip on Max Width input — explains "leave blank = no downscale; type a number to cap every image at that width". All four languages.
- Hover spin on the capsule weather icon (3.4 s/turn).
- Minimal macOS app menu — Tauri's autogenerated File/View/Help submenus are replaced with just Picvert / Edit / Window.
- Batch conversion is now parallel — sidecar runs a 4-thread pool; JS dispatches up to 4 in-flight conversions. ~3-4× faster on multi-image / multi-page-PDF batches.
- Auto-updater wired up —
bundle.createUpdaterArtifacts: truenow emits.app.tar.gz/.exe.sigpairs, andlatest.jsoncarries the real per-platform signatures. (v1.2.0 release contains the new public key, so 1.2.0 → 1.2.1 will be the first auto-update.) - Tauri updater key pair regenerated — the old
TAURI_SIGNING_PRIVATE_ KEYsecret was base64-corrupted (unnoticed until createUpdaterArtifacts was enabled).
- Capsule sometimes didn't appear after toggling main ↔ compact
several times —
expandToMain()left a stale.leavingclass on the body, so the capsule was painted at opacity:0 on next show. Both JS (finally-remove) and Rust (eval also wipes.leaving). - App could exit instead of going to capsule on first close —
hide_main_show_compactwas hiding main BEFORE building compact, leaving zero visible windows briefly. macOS sometimes treats this as "all windows closed" and exits. Now compact is shown first. - Capsule corners showed a faint right-angle halo —
.card-glowusedfilter: blur(20px)which the compositor sometimes leaks past the parent's rounded clip on a transparent NSWindow. Replaced with radial gradients (no blur leak). - Tab on capsule painted an outline ring — removed the default focus outline; capsule is a draggable widget, not a keyboard surface.
- JPEG / WEBP quality slider (60–100, default 90). Saved across launches. Visually-lossless 90 produces ≈half the file size of the previous fixed 100.
- Max width input (px) — proportionally downscale every output. Useful for exporting phone photos at web/social sizes.
- Drop folders — dragging a folder onto the dropzone (or capsule) now recursively expands it to all supported files. Capped at 5000 files / drop.
- Progressive + optimized JPEG encoding (
optimize=True, progressive=True). - WEBP encoding now respects the quality slider with
method=6(best compression effort).
- PDF page render path also honours
qualityandmax_dim(was hard-coded to PDF's 100% quality and original render size). engine_cancelnow actually kills the sidecar — multi-page PDF conversions are interrupted immediately. The engine respawns on next request (~0.17 s onedir cold start).- File picker switched from HTML
<input type="file">to Tauri's native dialog (pick_input_files) so the engine receives real absolute paths on every platform.
- SSRF in
http_get_text: now host-allowlisted to the three weather/geo APIs and HTTPS-only. Was previously a generic Rust-side fetch tunnel. - Engine-death recovery: when the sidecar crashes, all in-flight
awaits get a clean
engine_diederror and the next call respawns rather than hanging forever. - PDF open failure no longer silently returns
written: 0— now surfaces as aninternalerror in the UI status row.
- Tk-era leftovers:
picvert/i18n.py,picvert/config.py,picvert/paths.py,picvert/logging_setup.py,picvert/single_instance.py(and their tests). The Python side is now ~370 lines smaller. - Unused i18n keys, dead
picvert:show-helplistener, redundantshell:allow-executecapability, unusedgeocoding-api.open-meteo.comCSP entry,platformdirsdependency.
- Cross-platform tray icons (macOS template / Windows / Linux variants).
- Capsule ↔ main window scale + fade animations.
- Real document conversion across DOCX ↔ XLSX ↔ CSV (six directions).
- HEIC read / write via
pillow-heif. - Compact-mode floating widget with live time + IP-based weather.
- macOS menu bar tray with multi-language menu, About + Check for Updates.
- Keyboard shortcuts: ⌘O, ⌘⏎, ⌘L, ⌘.
- Toast notifications + system notifications on batch completion.
- Help dialog with supported-format chips.
- Persistent picvert-engine sidecar in
--line-modefor ~38× faster per-call latency vs--onefile.
- Switched PyInstaller from
--onefileto--onedir: cold start 6.5 s → 0.17 s.
Initial monolithic release (Tk GUI). Retired in favour of Tauri shell + Python sidecar in 1.1.0.