experiment: Implement new raw/jpeg grouping#451
experiment: Implement new raw/jpeg grouping#451EnTeQuAk wants to merge 11 commits intoCyberTimon:mainfrom
Conversation
|
Awesome, thanks a lot for your reply. Yeah, the design is far from final. I first need to play around with it more to ensure it's actually useful the way I built it 🙈
Indeed, I'm currently investigating if it makes sense to make that a bit more explicit and more widely known in other parts of the application. Right now I'm trying to figure out how to generalize different types of files, e.g, other cameras may have a "XXX + RAW" mode, not sure if JPEG is kinda like the absolute default in the industry. As I said, it's experimental and will likely take a while to finish. Your feedback is much appreciated :) |
|
subscribing to this PR since I am very much interested in this (I also shoot in jpeg+raw). This is one thing I am missing after I moved away from Lightroom |
split('.').next() returned everything before the first dot, so
'my.photo.2024.ARW' yielded 'my' instead of 'my.photo.2024'. This could
match unrelated files that happened to share a prefix.
Switched to Path::file_stem() which correctly returns everything before
the last dot. Sidecar matching now explicitly handles the
{filename}.rrdata and {filename}.{vc_id}.rrdata naming patterns instead
of relying on the stem shortcut.
list_images_in_dir and list_images_recursive now compute is_raw (via is_raw_file) and group_id for each file. Files sharing a stem in the same directory get the same group_id. Virtual copies are excluded from the count so a file + its VC don't form a false group, but VCs still inherit the group_id of their source. Also adds group_associated_files and group_preferred_type to AppSettings (defaults: off, 'raw').
Add ImageGroup, GroupPreference types and buildImageGroups() utility in src/utils/imageGrouping.ts. Wire the grouping result into the sortedImageList pipeline in App.tsx, gated behind the groupAssociatedFiles setting. Remove the ~40 lines of RawOverNonRaw stem-matching JS that duplicated what Rust now provides via group_id. The raw status filter now uses image.is_raw directly instead of parsing extensions against supportedTypes. Settings (groupAssociatedFiles, groupPreferredType) are loaded from and persisted to AppSettings.
…p fix Phase 2 + 3 of JPEG+RAW grouping: Library: - Thumbnail shows a variant count badge (bottom-right) when the image belongs to a group with 2+ variants. - Filter dropdown: 'Prefer RAW' replaced with 'Group Variants'. Selecting it activates grouping (collapsing, badges, switcher). - Added GroupVariants to RawStatus enum; old RawOverNonRaw values are treated as grouping for migration. Editor: - EditorToolbar shows variant switcher pills (e.g., [RAF] [JPG]) when the current image belongs to a group. Labels use the actual file extension, not generic RAW/JPEG. - Clicking a pill switches to that variant via handleImageSelect. Filmstrip: - When the user views a non-primary variant (e.g., switched from JPG to RAF), the filmstrip still highlights the group's primary so the active position doesn't jump. - activeDisplayPath prop flows: App -> BottomBar -> Filmstrip.
When 'Group Variants' is selected in the file type filter, a 'Prefer RAW / JPEG' toggle appears below it. Controls which variant is shown as the primary in the library grid. Defaults to RAW, persisted via AppSettings.groupPreferredType.
Replace the O(n*m) startsWith stem-matching with a direct group_id lookup against the pre-computed groupVariantCounts map.
Grouping is now driven entirely by the rawStatus filter (GroupVariants or migrated RawOverNonRaw). The separate boolean toggle had no UI and defaulted to false, so it was dead code.
aad3ede to
cc465bc
Compare
Three issues found during code review: 1. hasAssociatedFiles relied on groupVariantCounts, which is empty when grouping UI is inactive. Since Rust always populates group_id for files with shared stems, checking group_id != null is correct regardless of the UI state. 2. Virtual copies inherit group_id from their source, so the variant switcher pills would appear when editing a VC and point to the original files. Suppress the switcher for VCs to avoid silently leaving the VC context. 3. getFileExtension had a redundant first lastIndexOf call that was only used for an early return before the real logic. Simplified.
|
Alrighty, both of your replies motivated me to pick up the work sooner rather than later 😁 Now, I just pushed a complete rewrite from the original experiment. Given the old branch was 612 commits behind Key differences from the earlier iteration:
This pull request still needs group-aware operations (delete/copy/move with confirmation modals), though, I'd like to open that question rather than implement too much right now. What do you think operations like delete/copy/move should behave with the raw/jpeg grouping enabled? I'd build that as a follow-up in a separate pull request, I'd like to focus on the foundation here.
@CyberTimon, regarding your earlier feedback: the design now follows the existing component patterns (same padding, border-radius, and color tokens as the rest of the dropdowns and toolbars). The associated files detection for delete uses Let me know what you think. Happy to hear your feedback on this 👼 |
Swap the variant count number for a compact Layers icon (12px) that works at any thumbnail size. Tooltip shows the actual extensions joined (e.g. 'RAF+JPG', 'DNG+ARW+JPG'). Renames groupVariantCounts to groupBadgeInfo carrying both count and label.
Issues found during code review: - filmstripActivePath was missing the is_virtual_copy guard that variantOptions already had. A VC of a grouped file would highlight the group primary in the filmstrip instead of itself. - pickPrimary switch had no default case. A corrupt settings file sending an unknown preference string would return undefined. - Use Set for extension dedup in groupBadgeInfo (was indexOf loop). - Add skip_serializing to deprecated group_associated_files so it stops being written back to the settings file. - Remove stale comment.
1fea563 to
97fa495
Compare
Simplify make_group_key to just path.with_extension(""). Drop
group_associated_files entirely since it never shipped. Introduce
GroupId type alias instead of bare strings. Rename the filter label
to 'Group RAW + JPEG' for clarity. Tighten up comments.







I'm using a Fuji camera and regularly shoot JPEG+RAW to have the best of both worlds: straight out of camera JPEGs which are 80% of the time great, and RAWs for special occasions. Seeing every shot twice in the library has always bugged me, so I started experimenting with grouping.
The core idea: Rust assigns a
group_idto images that share a stem in the same directory (e.g.DSCF0644.RAFandDSCF0644.JPG), and the frontend collapses each group into a single entry, picking a primary based on your preference. Virtual copies inherit the group but don't count toward it, so a file + its VC alone won't create a false group.What I implemented:
[RAF] [JPG]instead of generic labelsAlong the way I also fixed a pre-existing bug where
delete_files_with_associatedwould grab the wrong stem for filenames with dots (was usingsplit('.').next(), now usesPath::file_stem()), and simplified thehasAssociatedFilescontext menu check from O(n*m) string matching to a directgroup_idlookup.Group-aware operations (delete/copy/move/export) aren't in this PR yet. When you delete a grouped file, should it delete all variants? Offer a choice? I'd rather discuss that and build it as a follow-up than guess wrong here.