The previous commit's sections.rs went through a PowerShell read and
write in the Windows ANSI code page, which turned …, – and ▸ into
mojibake in five album-menu labels (Create Album…, Sort Albums A–Z, …)
and two doc comments, so the labels missed their translation keys
(CodeRabbit on #3392). The file's text outside missing_count is now
byte for byte what it was before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A catalog change seen while a timed refresh of the Missing Photos count
is running cannot start its recount until the refresh ends, and the
refresh is not pending work, so settle() could return before either
finished and the row the recount adds moved the folder rows afterwards
(CodeRabbit on #3392).
The change is now counted as pending from the frame it is seen
(`owed`); the recount takes that entry over when it starts and drops it
once its count is stored. settling_waits_for_a_recount_owed_during_a_timed_refresh
adds a missing photo while a slow refresh runs and fails without this.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
While a headless run settles, its simulated clock runs several times
faster than wall time, so with every Missing Photos count pending, a
count that takes about a second started the next 5 s refresh before the
last one finished and settle() never ended (120 s timeout with a 1 s
count, 48 s with 600 ms). That would also stall lightcraft-cli snapshot
on a library on a slow share.
Only the first count and a recount after a catalog change, which add or
take away the Missing Photos row, are pending work now; the timed
refresh is not. Settling takes as long as the count (1.0 s for 1 s,
3.0 s for 3 s). The regression test also checks that settling ends
while a slow count refreshes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The sidebar's Missing Photos row appears when the background count of
missing files lands, and moves every row below it down. That worker was
not counted in fs_cached_running, so Headless::settle() did not wait for
it: on a busy machine the row appeared after settling, and a click aimed
at a folder row from the settled layout landed on the row above it
(a_folder_menu_locates_its_missing_tree_only located `other` instead of
`trip`).
- missing_count adds itself to the fs_cached_running counter before it
starts and takes itself off after the count is stored, as fs_cached does
- the worker starts with thread::Builder::spawn instead of thread::spawn,
which panics when the OS refuses a thread; on failure the last count
stays and the next refresh tries again
- settling_waits_for_the_missing_photos_count delays the worker by 1 s
(test-only field on LightcraftApp, which ui.set does not rebuild) and
fails without the fix
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UPDATE_APP (native builds; craft-update is a native-only dependency) carries our 128 px icon and accent (#f2a516, its colour in ArtCraft Launcher). The updater, the self-update
helper and craft-update's startup prompt all get them through crate::UPDATE_APP; the
prompt draws the icon, the accent and "by ArtCraft" (craft-update's default developer).
craft-update is pinned to storytold/craft-libs main at 1065653e7 (#434).
Clippy fixes for Rust 1.99 in untouched code (same logic):
- crates/engine/src/cmd/export.rs
- crates/ui-egui/src/dialogs/rules_editor/rules_move.rs
- crates/ui-egui/src/panels/detail.rs
- RawImage::develop demosaics only the default crop with AHD when there
is no OpcodeList3 (ahd_window), instead of demosaicing the margin too
and then copying the crop out of the full picture on one thread.
- Gpu::upload writes through queue.write_buffer_with and copies into
the staging buffer in parallel parts. create_buffer_init zeroed a
staging buffer and then copied over it, two single-threaded passes
over a full-size photo's ~290 MB.
- finish_and_read copies the mapped readback out in parallel parts (one
thread read the 91 MB of a 24 MP export at ~2 GB/s, ~45 ms).
Output is byte-identical (ahd_window_is_the_crop_of_the_whole,
uploads_read_back_exactly).
Export of a 24 MP ARW to full-size JPEG (examples/export_bench, 75
interleaved runs each on a shared 16-thread Ryzen):
main 802 ms median / 614 ms min -> this branch 647 / 525 (-19.3% / -14.5%).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- finish_8bit/finish_deep hand back the render itself when there is no
output sharpening and no watermark. Before, every export cloned the
image (~100 MB at 24 MP) and output_sharpen built a ~290 MB float copy
of it even for SharpenFor::None, all on one thread.
- Output sharpening's box blurs run row-parallel (par_rows); each pixel
is computed as before.
- The parallel JPEG encoder converts each band's rows to YCbCr inside the
band's task instead of first filling whole-image planes (~290 MB for
4:4:4 at 24 MP, written once and read back).
- fdct_quant rounds with plain arithmetic (round_i16) instead of
f32::round, a libm call per coefficient without SSE4.1; it equals
`x.round() as i16` for all 2^32 inputs.
Output is byte-identical (pinned by encoded_bytes_are_pinned and
sharpened_pixels_are_pinned, whose hashes come from main).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
examples/export_bench imports the files given and runs export_photo
(decode, full-size render, encode) N times, forgetting the decoded
original between runs like a batch export does, and prints the median
and minimum wall time. OPT takes export options as app.export JSON,
PHASES=1 times render and encode separately, OUT=dir writes the files
for byte comparisons between builds.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Use craft-ui's dropdown so menus grow back after shrinking
The album picker keeps its choices and search field in a fixed header and scrolls only
the list; combo_box and the print dialog's combos use craft_ui::ComboBox. egui::ComboBox
is now disallowed by clippy.
* chore(deps): craft-libs 60d60d48 (dropdown fixes)
* chore(deps): craft-libs 6af63481 (dropdown fixes)
* chore(deps): craft-libs 4d7b82f7 (dropdown fixes)
* chore(deps): craft-libs 92d392cf (#387 merged)
Reuse cached folder listings through Arc and allow tests to inject Local places.
Show browsed folders below hidden ancestors as session roots. Apply the same
hidden-entry checks to folder walks, browsing, import scanning, and Local.
Return an error when either file index is zero. Keep the resolved-path fallback for lenient comparisons. Test both unknown-identity orderings and distinguish identity lookup failures from known-original refusals.
Merge main at 662df66f.