When a hidden panel's anchor is neither shown nor hidden, craft-ui falls
back to the given anchor's tabs. The photo view is that anchor and takes no
tabs, so the panel opens beside it on its default side instead of at the
right edge. Test: Library anchored on an absent Presets opens to the left
of the photo.
When the right panel is open but minimized or behind another tab, F8 keeps
its mode instead of switching to the one it last hid, and view.rightPanel
reveals Tools. Test: F8 on a minimized floating Tools window in Crop opens
the window and stays in Crop.
Showing a panel that has no hidden entry skips craft-ui's right-edge
fallback and opens it beside the photo view on the panel's default side.
Test: Library opens to the left of the photo, Presets to the right.
The side panels are frames whose widths LightCraft tracks, so new_dock_zones
is off. Test: an edge drop of Tools leaves the layout without a Node::Dock.
craft-ui and craft-docpath are pinned to craft-libs
17744430be327d0257496a6a31c8d38fe20a6756, where a divider drag keeps the
split's sizing mode. LightCraft no longer turns dragged ratios back into
side-panel widths.
craft-ui stores a moved divider as a ratio. After each frame the split
beside a side panel takes the width the panel was drawn at, so the panel
keeps its width when the window resizes and left_width / right_width follow
the drag.
craft-ui and craft-docpath are pinned to craft-libs 16fb3d46e1eaa0d455465e4e66a3ac7b17b55bd3.
The saved workspace is craft_ui::docking::Workspace<Panel>; settings written
before it had a version load as version 1. ui.dock params parse as the shared
DockCommand and apply through Workspace::apply_all. LightCraft keeps its
panels, permissions, default layout, photo-view checks, the fallback beside
the photo view for a panel with no recorded place, and its settings file.
The photo view takes side splits (accepts_splits on PROTECTED), so panels
still open and move beside it.
Tests: a version 1 workspace file loads and reopens its hidden panel; a drag
of a group by its bar moves both of its panels.
Resolves the parity summary (regenerated), drops a duplicated #[cfg(test)], files the What's New entry
under today's date and bumps the checklist's Last updated line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main replaced "Show Clipping" with "Clipping": the PR's Beschneidung term moves to the new key.
Also takes CodeRabbit's two wording suggestions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A pass over all 2,565 interface strings and 299 formats messages of the
German catalogs (What's New lines not included). 108 messages change:
- Lightroom terms: Tönung (Tint), Beschneidung (Clipping)
- one word per thing: Bedienfeld (panel; Bereich stays for sidebar
sections), Begleitdatei (sidecar), Dia (slide), Zuschnitt (crop),
Farbmarkierung (label) where "Markierung" read as the pick/reject flag,
Verschlusszeit, Smart-Album, Perspektivkorrektur (Upright),
Aktivitätsstapel
- "Ihre Dateien/Fotos" at the start of a sentence read as the formal
"your" in a du catalog; rephrased
- double colons and "1 Fotos" in count messages, awkward phrasings
- spaced en dash instead of em dash, no space before a final ellipsis,
German quotes around {profile}
The terms are listed in docs/localization-de.md; localization-parity
records the review.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Found while testing crates/catalog/src/local.rs:62 (folder_of), used by forget_local_plan at :192 and :204
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The script runs under #!/bin/sh; on dash the array assignment was a
syntax error. Pass the auth option and its value as two plain words.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* security: bound codec allocation to what a hostile file declares
A GIF's first image descriptor could name a frame up to four times the
canvas budget and be charged that allocation before any size check saw
it; PNG's compressed text chunks were expanded through an unbounded
inflate; JPEG handed hostile frame headers to the allocating decoders
(zune for the common cases, jpeg-decoder for CMYK/scaled/lossless) and
let an APP1 flood drive quadratic metadata reassembly. Each now refuses
on the same max_pixels budget the other decoders honour, with a
regression test per gate.
🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
* Fix clippy collapsible_if in DCT-scale size check
* Check jpeg-decoder header on scaled path; cap APP1 markers in decode_codes8
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Codebuff <noreply@codebuff.com>
Co-authored-by: Dylan Taylor <dylan@craftmaker.dev>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
- Reverse slider loop order in UI to match Lightroom layout (Highlights, Lights, Darks, Shadows).
- Sanitize splits and amounts against NaN, inf, and inverted split bounds in ParametricCurve::new, at, and region_at.
- Guard param_drag against zero/negative widget dimensions.
- Update live region readout to display the current frame's dragged value immediately.
- Add regression test for non-finite and inverted split values.
Co-Authored-By: deepseek-flash <noreply@deepseek.com>
FredApps on #2689: a re-check started while the launch list is up (a
library not found looked for again, the window back to the front) never
marked its row as checking, so `apply_answers` skipped the new answer: a
library plugged back in stayed "not found" in the list. A row is re-armed
when its path is asked again about an answer it has (not after a failed
open, whose reason stays on the row). Test
a_library_found_again_comes_back_to_the_launch_list fails without it.
Fixes#2774
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JbBu4MRiaKxEbSrMTZiLj8
- Expose ParametricCurve from lightcraft-pipeline and use it in both
curve_luts and the UI curve editor, eliminating duplicate implementations
and ensuring render and graph never drift.
- Invert the parametric model while holding the other three regions fixed,
converting vertical pointer motion relative to the grabbed region's initial
amount so a horizontal drag with zero vertical travel leaves all sliders
untouched even with heavy overlapping neighbouring bumps (fixes AlexChambers
report: Darks=60, Lights=0 grabbed at x=0.51).
- Replace all raw table indexing with .get() per never-crash rules.
- Add unit and headless integration tests verifying that horizontal drag
with other regions non-zero changes nothing.
Co-Authored-By: deepseek-flash <noreply@deepseek.com>
CodeRabbit nitpick on #2701: a failing assertion skipped giving `Locked` its
permissions back, and a folder left at mode 000 can't be removed. A Drop
guard restores the mode and removes the test's folder on every exit
(found one left behind by an earlier failing run).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JbBu4MRiaKxEbSrMTZiLj8
The gate on cf9334907 failed on
tests_slideshow::playback_settings_drive_the_slideshow_and_bad_ones_are_refused
and ::settings_shows_the_slide_layout_and_a_live_preview (#1287); they passed
on the two gates before and in 15 of 15 local runs of the module with 4
threads, and nothing since touches the slideshow. Each checked its result
one frame after the click / after the preview changed, which a loaded
runner doesn't always give: they wait for it now (the checkbox state, the
green backdrop pixel). Test code only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JbBu4MRiaKxEbSrMTZiLj8
The revision field and Catalog::apply said an error changes nothing and that the revision counts applied ops; a batch whose rollback fails changes the catalog and advances it.