Skip to content

feat(ui)!: export from the app - #95

Merged
wow merged 19 commits into
mainfrom
feat/export-screen
Nov 5, 2026
Merged

wow merged 19 commits into
mainfrom
feat/export-screen

Conversation

@wow

@wow wow commented Nov 5, 2026 •

Copy link
Copy Markdown
Owner

Summary

The app can now export. The rail gets an Export section and button; the button first shows what the engine plans for each file, with nothing written until it is confirmed; rows walk through their stages and end with what was verified, the grid check and Revert; the footer links the batch's rekordbox XML and report. The pipeline machine gains export states, with one job at a time and the analysis queue as it was.

Exports go to a folder by default; in place with a backup is an explicit choice. Exporting again to a folder replaces a copy of this same file that SoundCheck wrote there, when its sidecar proves it, and one folder has one rekordbox XML and report that describe it. What the user confirms is what gets written: settings that changed refuse the job, and a file that changed fails its row before anything is written.

User-visible behaviour

  • Rail, Export section: Prepare | Library; To folder (default, with a remembered folder) | In place with backup; depth source / 16 / 24; Grid only; Write TBPM (per mode); Write rekordbox XML. A file with Serato data makes the batch Library with a notice; choosing Prepare is remembered per folder.
  • Export button (also in the top bar of a narrow window): disabled with its reason during a job, a revert or recovery, or when tracks were analysed under another BPM range ("3 tracks need Analyse for this BPM range"). With a folder all that is missing, "Choose a folder" opens the picker, and so does the top bar's Export.
  • Confirm step: what is being typed reaches the engine first. Title and button say what is written ("Write 10 files"). 12 files: 9 gain, 3 cut, 2 skipped; backups in ~/Music/SoundCheck Backups, the target and ceiling, a warning when a cut shifts positions, "3 files replace your earlier copies", "1 file in the folder isn't SoundCheck's, skipped", "1 file skipped: a copy of another track already has its name", and what the folder's XML keeps. An XML or report there that starts like SoundCheck's but cannot be read whole refuses the export, naming the file.
  • Rows exported with other settings say so ("Exported at -11.0"), and the confirm step says "18 exported at -11.0 stay as they are" with an unticked "Export them again at -8.0". A skipped row makes no work: a skip about the file itself stays, and any other is planned again and written only if the plan now differs.
  • Rows: Queued, Processing, Writing, Checking (already verified), then Done, verified / Skipped / Failed. Action: Gain -2.3 dB, Cut 0.21 s with a positions-shift badge, Grid only, Grid: XML only (MP3). A row exported fine whose grid still asks for a look says Done, verified with the reason under it and counts in Needs review only, so the tabs add up.
  • After export: "Verified: 27/27 carried, 3 added", the grid-check chip (an advisory is neutral, never a warning), Revert with a spinner on its row. Selecting a row shows the whole story under the table.
  • Footer: progress and Cancel; links that show the XML, report and folder in the Finder; a "rekordbox XML" button that lists every analysed row, an exported copy where it is; an XML listing nothing says so plainly.
  • Reload during an export: the export stops, the rows in flight keep their stage while the page waits, the links and summary come back, and a file written in place whose analysis did not finish is analysed again. After a minute the app says to restart.
  • A file changed after it was analysed or confirmed says so on its row and to press Analyse, which is lit for it.
  • sc-cli process requires --out <dir> or --in-place, refusing in three lines with exit 2 otherwise.
  • The rows of a running batch cannot be reverted or opened until it ends. Rejected commands keep their typed error instead of [object Object].

Tests run

PASS  file-size               11s
PASS  test-files               0s
PASS  cargo-fmt                1s
PASS  cargo-clippy             2s
PASS  cargo-test             288s
PASS  fe-typecheck             4s
PASS  fe-lint                  4s
PASS  fe-test                 11s
RESULT PASS

New UI tests cover the rail, the machine, the store for every event, all Action kinds and chip states, the footer, the actions, what the Export button works on, the typed target landing before planning, and a whole export through <App /> on the stand-in. New Rust tests: src-tauri/src/shell/tests/preview.rs, crates/sc-io/src/txn/copy_at/tests.rs, the XML and report round trips and merges in crates/sc-engine/src/export/artefacts/tests.rs, copies replaced and refused in crates/sc-engine/tests/process.rs, and sc-cli process refusing and replacing. Every guard was broken on purpose (72 UI and 46 Rust breakages) and a test failed for each.

A browser pass on the stand-in found overflowReport() empty at 720x560 in idle, confirm, during and done.

Output change

  • No: the same input still yields the same numbers, grids and bytes.
  • Yes: goldens regenerated and reviewed; the commit carries Output-Change: yes; this ships in a minor release. No golden changed. A copy to a folder can now replace an earlier one, and a batch's rekordbox XML and grid report now merge with what SoundCheck wrote in that folder, sorted by location.

Docs and dependencies

  • docs/PRODUCT.md / docs/ARCHITECTURE.md updated if behaviour or crate boundaries changed.
  • New dependencies, with licence: none.

Output-Change: yes
BREAKING CHANGE: sc-cli process requires --out <dir> or --in-place.

wow added 12 commits November 5, 2026 06:50
The confirm step needs real numbers, so plan_export plans every file with
the same plan_export_snapped path as the job, on the blocking pool, refused
during recovery and while a file is busy. Rows carry the plan's wording
(skip reason, XML-only reason, notices, withheld grid) so the UI shows the
same words as the report.

The snapshot now names the jobs still winding down and keeps the latest
batch's artefacts: a window reloaded during an export would otherwise never
learn when the job ended or where its files went.

Signed-off-by: Ilham Deneri <ilhamdeneri@gmail.com>
The rail gets the Export section and button; a confirm step shows what the
engine plans for each file before anything is written; rows walk Processing,
Writing, Checking and Done, verified, with the Action column, grid-check chip
and Revert; the footer links the batch's files. The machine gains export
states, one job at a time. A window reloaded during an export waits for the
job through restore_session and finds the batch's links afterwards.

Rejected commands keep their typed error instead of [object Object].

Signed-off-by: Ilham Deneri <ilhamdeneri@gmail.com>
Also hardens the status pill and the footer for narrow widths, and gives the
Export button a reason when nothing is analysed yet.

Signed-off-by: Ilham Deneri <ilhamdeneri@gmail.com>
Product, architecture and README now say what the app does at export. Also
keeps a saved grid on a row outside the running batch, and raises the Action
column's second line to 12 px, as its numbers need.

Signed-off-by: Ilham Deneri <ilhamdeneri@gmail.com>
Changing the originals is now an explicit choice. ExportSettings::new and the
rail default to a folder, with Export disabled until one is chosen. sc-cli
process takes --out or --in-place and refuses without either, in three lines
with exit 2.

BREAKING CHANGE: sc-cli process requires --out <dir> or --in-place.

Signed-off-by: Ilham Deneri <ilhamdeneri@gmail.com>
With folders the default, exporting again to one folder is the common path, and
it failed with AlreadyExists. A copy may now be replaced when its sidecar proves
it: a copy to a folder, with the file's bytes still those it recorded. The path
stays, so a DJ app keeps what it keyed to it. Anything else of that name, the
source itself included, skips the row and is left alone. A plain apply never
replaces.

Output-Change: yes
Signed-off-by: Ilham Deneri <ilhamdeneri@gmail.com>
A second batch into a folder used to drop the first batch's tracks from its XML
and report, though their files were still there. A batch now merges with what
SoundCheck wrote there before: matched by location (the report by its file),
newest first, entries whose file is gone dropped, sorted so the result does not
depend on batch order. TrackIDs follow the sort; the playlist stays the batch's.

Output-Change: yes
Signed-off-by: Ilham Deneri <ilhamdeneri@gmail.com>
plan_export now returns each file's identity and the settings revision, and the
request sends them back: a file changed since it was confirmed fails its row
with fileChanged before anything is written, and changed settings refuse the job.
The preview also notices a file that changed since its analysis.

Rows record what they were exported with. The session keeps exportStale, set only
when the saved grid differs, and the latest batch's summary, so a reloaded window
reads both. A job that panics now always ends: a guard in spawn_job removes it,
releases its files and sends Finished. The rekordbox XML command lists an exported
copy where it is.

Output-Change: yes
Signed-off-by: Ilham Deneri <ilhamdeneri@gmail.com>
Export commits what is being typed and waits for it to reach the engine before
planning, and the step shows the target and sends back each file's identity and
the settings revision. Its title and button say what is written.

Rows record what they were exported with, say so when the settings moved on, and
stay as they are unless the step's unticked box is ticked. Skipped rows alone make
no work, Revert is offered only where it exists, and rows analysed under another
BPM range block Export. A row exported fine that needs review reads Done, verified
with the reason under it, and counts in one tab only. Export sits in the narrow top
bar. A reload analyses what it wrote in place, reads the last batch from the
session, and gives up waiting after a minute. Engine text reads as sentences.

Signed-off-by: Ilham Deneri <ilhamdeneri@gmail.com>
Also tests sc-cli process replacing its copy and leaving another file alone, and
keeps the docs on what a copy may replace.

Signed-off-by: Ilham Deneri <ilhamdeneri@gmail.com>
A check before the hash added nothing for a written file, and one after it covers
files that are not hashed as well.

Signed-off-by: Ilham Deneri <ilhamdeneri@gmail.com>
Flags for where files go move into their own struct.

Signed-off-by: Ilham Deneri <ilhamdeneri@gmail.com>
@wow wow changed the title feat(ui): export from the app feat(ui)!: export from the app Nov 5, 2026
wow added 7 commits November 5, 2026 10:04
Work towards a second review round: a copy is replaced only when it was made from this file, an artefact of ours that does not parse is refused instead of written over, and identity is read before an analysis. Session events move to their own file.

Output-Change: yes
Signed-off-by: Ilham Deneri <ilhamdeneri@gmail.com>
Window checks and a crash matrix for replacing a copy, links to their own copy, a report or XML that does not read whole, what cures a skip, and identity read before an analysis.

Signed-off-by: Ilham Deneri <ilhamdeneri@gmail.com>
Skips map to what could cure them: reasons about the file stay along, and the rest are planned again and become work only if the plan differs. Rows the job or the plan found changed light Analyse. The step plans exactly what would go out. Copies of another track count apart. Choosing a folder is a button, and the narrow top bar's Export asks for one. Footer counts come from the job's own events, each row keeps its analysed range across a reload, and a row the XML lists says XML only.

Signed-off-by: Ilham Deneri <ilhamdeneri@gmail.com>
The lock window now shows nothing was journaled, an empty track and a cut last field each have a case of their own. Docs say what a copy may replace and when an artefact is refused.

Signed-off-by: Ilham Deneri <ilhamdeneri@gmail.com>
Planning a file with what is at its copy's path moves into its own function.

Signed-off-by: Ilham Deneri <ilhamdeneri@gmail.com>
Its settings move into their own function.

Signed-off-by: Ilham Deneri <ilhamdeneri@gmail.com>
Names now compare the way the folder's volume does, so Track.wav and
TRACK.wav only collide where the volume folds case. The test assumed
it always does and failed on Linux. It now checks both outcomes.

Signed-off-by: Ilham Deneri <ilhamdeneri@gmail.com>
@wow
wow merged commit 689c9b8 into main Nov 5, 2026
8 checks passed
@wow
wow deleted the feat/export-screen branch November 5, 2026 10:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant