Repository navigation
Conversation
The title and Copy Snapshot lived inside the scrolling pane, so they slid away as soon as the cover wall moved.
A lossy encoder is free to reconstruct a waveform louder than the one it was handed, and every one of them does: a master peaking at -0.18 dBFS comes back out of AAC at +1.45. The iPod's output stage is fixed point, so those samples saturate and land as short bright clicks. Nothing about the source predicts it — this master's true peak is -0.05 dBTP — so measure the encoded file instead and re-encode it attenuated when it decodes back above full scale. Lossless targets are exact and skip the pass entirely. convert_check grows --aac/--mp3 because the lossless default it had could not exercise the new path at all.
gpod_remove_track frees the database record and nothing else, so every removal left its file on the device for good. A 74 GB iPod had 2 GB of audio no record pointed at. Queue the device path at removal and sweep it once itdb_write returns. The ordering is the whole point: deleting first and then failing to write leaves the on-disk database pointing at audio that is gone, which is worse than the leak. A path a surviving record still claims is skipped — libgpod does not check either.
Picking a cover in the bulk panel wrote it to every selected track immediately, out of step with the other fields and with no way back from a mis-click. It is now a draft like any other field: the panel shows the staged file name, offers Clear, and the single Apply stamps fields and artwork together. Claude-Session: https://claude.ai/code/session_01TdJdhEhGTJHScRWETLqEYA
A drop while Convert is visible used to fall through to the library import path, which needs an iPod and starts writing immediately. Convert now takes the paths and only lists them: format, bitrate and destination are still unset at that moment, so nothing runs until Convert Files is pressed. Claude-Session: https://claude.ai/code/session_016BSACqVKky8go5gyYk41xk
Every heading is now a button: click sorts by that column, click again reverses. This replaces the Sort By radio group in the View menu, which offered four of the ten orders the list can be in and put them two menus deep from the columns they describe. The sort is stored per grouping rather than once for the list. Album order is the only order that makes sense under an album header and a poor one for a flat list, so sharing one value meant every switch between grouped and flat silently re-sorted the view being left. Flat view opens on artist A-Z, the grouped views on album order. Around that: - An Added column, so Recently Added has a heading that asks for it. - Album absorbs the row's slack instead of Title. Title took the whole width of a wide window for a three-word string while the album beside it truncated. - Flat rows carry their own cover art. The grouped views already show one per album header. - The heading clips and mirrors the list's scrollLeft. A heading too wide for the window used to widen the document, and a sideways gesture slid the entire UI; html/body now hide overflow too. - Library's inspector and Convert's settings column share one width constant, so the edge stops jumping when tabs are switched.
A cover the webview cannot decode is not a missing cover as far as the <img> is concerned: WebKit swaps in its own broken-image icon, which on macOS is the system question mark tile — foreign to this UI and readable as neither "no cover" nor "bug". The thumb now reports the failure to the artwork cache, so the key falls back to the music-note placeholder for every later mount rather than only the one that happened to paint it. The mark is an ordinary cache entry, so invalidateArtwork drops it and a replaced cover is fetched again.
Half the cores left the UI responsive but not the audio: with eight decode+resample chains running, CoreAudio's render thread loses the race often enough that music playing on the machine stutters for as long as a batch lasts. Two workers still halve the wall clock against a plain queue, which is where most of the win was.
The DMG is handed out by link, so without a check on launch a user stays on whatever build they downloaded once. The HTTP request is made in Rust rather than the webview: the CSP allows no origin but 'self', and widening connect-src to reach GitHub would open that door for the whole UI. A declined version is remembered by version, not as a flag, so saying no to 0.2.0 still leaves 0.3.0 audible. Claude-Session: https://claude.ai/code/session_01NTrpqWnfR4k3BNVhAkeJe1
A successful job used to leave a log holding a header, a footer and
whatever ffmpeg happened to complain about — no record of the tracks
themselves. Each one now writes a start line and a result line, and the
result carries what the file turned out to be:
✓ FKJ - Blessed.flac: converted in 0.7s · ALAC 16-bit 44.1 kHz stereo
· 28.5 MB · 856 kbps
Those facts are read back off the finished file rather than derived from
the request: a passthrough that skipped the encode, or a VBR pass that
landed nowhere near its nominal rate, would otherwise be reported as
whatever was asked for. The extra ffprobe is gated on the observer
wanting it, so the drag-and-drop import path does not pay for it.
Three things the per-track lines exposed:
- ffmpeg's own lines carried the destination name while ours carried the
source, so one track appeared in the log under two names.
- swscale's "deprecated pixel format" fires once per track of an album
with art, from cover normalization this app asks for itself.
- the failure line repeated ffmpeg's whole stderr tail, which had already
been logged line by line just above it.
Colour follows who is speaking rather than one hue per level: the app
narrating its own progress stays muted, ffmpeg keeps amber, and the only
two saturated colours are the states a reader scans for — landed and
failed. Both are the queue's existing tones, so a track that went green
in the list is green in the log too.
The archive is packed by bundle-dylibs.sh, not by tauri's own createUpdaterArtifacts: that one is produced inside `tauri build`, before a single dylib has been copied, so it would carry an app that still links ~/.local and /opt/homebrew — it installs cleanly and then dies in dyld on the user's machine. latest.json is written on `release: published` instead of alongside the DMG because it has to carry the release notes, and those are typed into the draft long after the tag build finished. A missing .sig fails the release outright rather than drafting one the updater could never use. Claude-Session: https://claude.ai/code/session_01NTrpqWnfR4k3BNVhAkeJe1
Replaces gray/dark/mono with orange/green/purple/black, all regenerated from new 1024px sources, and adds the Icon Composer document the artwork is authored in. The old three were value variations of one image and read as the same icon dimmed; the colourways are told apart at Dock size.
The log export's three sentences of explanation were the bulk of the panel, which read as Settings being mostly about logging. Five icon tiles need 464px, so the dialog goes 420 -> 500.
xattr -dr on /Applications needs sudo.
An unfiltered pull_request trigger burned a macos-14 runner on every push to every feature branch, while the dev -> main pull request re-runs the same suite on the same commits before anything ships. Claude-Session: https://claude.ai/code/session_019JSNhxAtjFP8is9uJ7nQWX
fix: clipped audio and leaked files on the device
feat(icons): ship four dock colourways
fix(ui): stage bulk cover art until apply
feat(ui): stage dropped files on the Convert tab
feat(ui): sort the track list by column heading StatsView conflicted with the pinned header from #2: both branches replaced the pane's `flex-1` root because the tab wrapper is a plain block. The header split already roots at `flex h-full flex-col` and moved the scroller inside, so that structure wins and only the reasoning from this branch's comment is kept.
feat(convert): per-track log, and our own art placeholder
feat(updater): ship in-app updates from GitHub Releases CLAUDE.md conflicted only because this branch and the branch policy notes both opened a new section at the same anchor. Both are kept.
None of these branches was ever fmt-checked: ci.yml only runs for pull requests based on main, and every one of them targeted dev. Claude-Session: https://claude.ai/code/session_019JSNhxAtjFP8is9uJ7nQWX
Nothing here deploys — the artifact is a DMG someone downloads, so a pull request has no environment a red tick protects, while every job needs a macos-14 runner to compile against libgpod. main is now the only branch with a verdict, and the promotion merge is the commit it lands on. The dmg job's `if: github.event_name == 'push'` and the concurrency group's pull_request check are both dead with the trigger gone. Claude-Session: https://claude.ai/code/session_019JSNhxAtjFP8is9uJ7nQWX
.gitignore already listed `.claude`, but the rule never applied: gitignore is only consulted for untracked paths, and CLAUDE.md had been in the index since before the entry existed. The stale comment above `.claude/settings.local.json` claimed the opposite and is gone with it. The file stays on disk; only the index entry is dropped. Claude-Session: https://claude.ai/code/session_019JSNhxAtjFP8is9uJ7nQWX
release.yml refuses a tag whose name disagrees with tauri.conf.json or package.json, so all of them move together. Cargo.toml and both lock files follow to keep the crate and the DMG's name in step. Claude-Session: https://claude.ai/code/session_019JSNhxAtjFP8is9uJ7nQWX
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Promotes everything accumulated on
devsince v0.1.0 and cuts 0.2.0. Eightfeature branches, the CI trigger change, and the version bump. This is the
first build to carry the in-app updater, so it is also the last one existing
users have to install by hand.
Changes
tauri-plugin-updater, verified against a minisign key built into the app.bundle-dylibs.shpacks and signs the updater archive itself, afterre-signing, because Tauri's own artifact is produced before any dylib is
bundled.
remembered per grouping.
default; Settings collapses its advanced section.
file instead of one per run, and broken artwork falls back to our own tile.
re-encoded under 0 dBFS, and removing a track now deletes its audio, but
only once
itdb_writehas returned.both wait for an explicit apply rather than acting on the gesture.
needs a macos-14 runner to compile against libgpod.
mainis the only branchwith a verdict now; this merge is the commit it lands on.
.claude/untracked — it was in.gitignorebut had been in the index sincebefore the entry existed.
both lock files, which
release.ymlrefuses to build without.Test plan
CI does not run on pull requests, so the full suite was run locally on the
merged tree at 38ae3db:
npm ci && npm run typecheck && npm test && npm run build— 157 tests pass,production bundle builds.
cargo fmt --manifest-path tauri-src/Cargo.toml --check— clean. Every oneof the eight branches failed this on arrival, since nothing had ever run it
on them; fixed in 4dd933f.
cargo clippy --manifest-path tauri-src/Cargo.toml --all-targets -- -D warnings— clean.
cargo test --manifest-path tauri-src/Cargo.toml— 50 unit tests and the 13library_roundtriptests that drive the real iTunesDB write path.release.yml's verify-version gate simulated for tag v0.2.0 — tag,tauri.conf.json and package.json agree.
Merging this triggers CI on
main, which builds a DMG. The release itselfcomes from tagging v0.2.0 afterwards.