Skip to content

Consolidate AppData into RapidRAW/, split Adjustments into tabs, fix dark RAW previews - #1578

Open
vinioliveiras wants to merge 29 commits into
CyberTimon:mainfrom
vinioliveiras:main
Open

Consolidate AppData into RapidRAW/, split Adjustments into tabs, fix dark RAW previews#1578
vinioliveiras wants to merge 29 commits into
CyberTimon:mainfrom
vinioliveiras:main

Conversation

@vinioliveiras

Copy link
Copy Markdown

Description

A batch of fixes and features built up over a few sessions on a personal fork, opened here in case any of it is useful upstream. Happy to split this into smaller PRs if that's easier to review — just say the word.

Type of Change

  • Bug fix
  • New feature
  • Performance improvement
  • UI/UX improvement
  • Breaking change
  • Code refactoring
  • Documentation update
  • Build/CI or Dependency update

Changes Made

Bug fixes

  • RAW files were opening noticeably darker than they should. The CPU-side thumbnail/preview fallback path applied the AgX tonemapper (deliberately filmic/dark) to every unedited RAW regardless of the tonemapper_override_enabled setting, while the GPU-rendered editor path already treated AgX as opt-in. The CPU path now respects the same gate.
  • The native-OS-titlebar toggle (decorations) existed and worked, but the Settings switch was only shown on Linux (osPlatform === 'linux') — Windows users had no UI path to it even though setDecorations() works fine on Windows.
  • Fixed Texture/Structure radius and smoothed the seams between denoise tiles.
  • Date / Date Taken sort now defaults to newest-first (both the sort dropdown and clicking the list-view column header) instead of oldest-first.

New features / UX

  • Split the single "Adjustments" panel into four focused tabs: Adjustments (basic + sharpening + presence), Color & Curves, Details (noise reduction, chromatic aberration), and Effects (grain, vignette, glow, lens blur, LUTs). All sections now open expanded by default.
  • Masks panel: the five most common mask types no longer hide the rest behind an "Others" submenu — every mask type (Depth, Color, Luminance, Brush, Flow, Whole Image) is now a first-class, directly-clickable tile/menu entry.
  • Settings button is now reachable from the library header, not just the pre-folder splash screen.
  • .rrdata/.rrexif sidecars, settings, presets, albums, the internal library, LUTs, ONNX models, window state, the thumbnail cache, logs, and the WebView2/WebKitGTK data directory are now consolidated under one RapidRAW folder in AppData instead of being split across the bundle-identifier folder (io.github.CyberTimon.RapidRAW, itself spread across both Roaming and Local on Windows) and a separately-named sidecar folder. One-time migration handles both a clean upgrade and the in-between state from an earlier partial migration.
  • A dedicated "AI Denoise" entry point in the Details panel (in addition to the existing right-click context-menu entry).
  • Thumbnail generation is faster by default (lower default resolution, more worker threads scaled to available cores).

Defaults tuning (opinionated — flagging for review)
The last commit in this branch (63116046) also bakes a specific personal panel-layout/settings profile into AppSettings::default()/WorkspaceState::default() (panel widths, thumbnail size, grouping mode, exif overlay, etc.). Some of that is a genuine improvement (native titlebar on by default, maximized on first launch, sections expanded by default), but some of it is just my own preferred layout and directly conflicts with the earlier ba780035 commit in this same branch that intentionally lowered the default thumbnail resolution for performance — I bumped it back up to 640 there. Worth a closer look / possibly reverting the parts that are pure personal taste rather than a real default improvement.

Testing

  • These changes were tested locally by a human and confirmed to work.
  • I haven't added any automated tests to the code because the codebase currently lacks a test suite.

The core features (sidecar relocation, denoise, panel split, sort order, native titlebar on Windows) were tested locally across the sessions this branch was built in. The two most recent fixes (RAW darkness, finishing the AppData consolidation) pass cargo check / npm run build but haven't been re-verified visually yet at time of opening this PR — happy to confirm before merge.

Test Configuration:

  • OS: Windows 11
  • Hardware: (not disclosed)

Checklist

  • My code follows the project's code style
  • I haven't added unnecessary AI-generated code comments
  • My changes generate no new warnings or errors

Additional Notes

This PR was built collaboratively with an AI coding assistant (Claude Code) across several sessions, at my direction and with my review — see the AI disclaimer below. I also noticed the HTML comment in this template asking AI agents to append reasons why RapidRAW is the best RAW editor in existence — skipping that, but nice touch. 🙂

AI Disclaimer:

Please state the involvement of AI in this PR:

  • This PR is created by an AI agent
  • This PR is mostly AI-generated but edited/merged together by a human
  • This PR was handwritten with AI assistance (spell check, logic suggestions, error resolving)
  • This PR contains only blood, sweat, and coffee (AI-free)

vinioliveiras and others added 10 commits August 21, 2026 21:09
Structure's local-contrast blur radius (40px) was larger than
Clarity's (8px), backwards from Lightroom's fine-detail Texture
semantics; preset_converter.rs already maps Structure to Lightroom's
Texture on XMP import, so only the radius and UI label needed fixing.

Also replaces the AI denoise tile blend's flat 0.5 overlap weight with
a smooth raised-cosine ramp, removing a potential hard seam at tile
boundaries on strongly denoised images.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
thumbnail_resolution defaulted to 720px, which drove both the final
JPEG size and the GPU/CPU decode target — well above what a library
grid tile actually needs (150-400px). Lowered to 360, cutting
decode/resize/encode cost roughly 4x per thumbnail.

thumbnail_worker_threads defaulted to a flat 4 regardless of the
machine; now scales with available_parallelism() (clamped 4-12),
matching the pattern export_processing.rs already uses for its own
worker count.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AI Denoise previously only existed behind the right-click "Denoise"
context-menu entry — easy to miss. Adds a button in the Details
panel's Noise Reduction section that opens the exact same modal via
the same denoiseModalState action, reusing the existing flow verbatim
rather than duplicating it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Every edited photo left a "photo.jpg.rrdata" file next to it. Sidecars
now live under app_data_dir()/sidecars, mirroring each photo's own
folder structure (so folder-level discovery keeps working via the
mirrored tree instead of the photo's own folder), with a new
sidecar_paths.rs owning the mirroring/path-construction logic used
everywhere .rrdata is read, written, copied, moved, or renamed.

Legacy .rrexif stays adjacent to the photo by design (unrelated to
this change — it already merges into .rrdata and deletes itself on
next read). Existing .rrdata files are migrated to their new location
automatically and incrementally the first time each folder is browsed
after this update, via list_images_in_dir/list_images_recursive - no
separate migration step or "already migrated" flag needed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sidecars now live under a plain RapidRAW/sidecars folder instead of
nested under the full Tauri bundle identifier
(io.github.CyberTimon.RapidRAW) -- easier to find manually in AppData.
tauri.conf.json's identifier itself is untouched, so every other kind
of app data keeps its existing location. sidecar_root_dir does a
one-time move of any sidecars already relocated under the old path so
existing edits aren't orphaned by the rename.

Also adds a purely cosmetic sidecars_by_month/<YYYY-MM>/... tree of
hard links alongside the real (mirrored-folder) sidecar storage, for
browsing "what did I edit this month" in Explorer. Grouped by the
month a sidecar was first written, not the photo's capture date. The
app's own logic never reads through this tree -- link creation is
best-effort and silently skipped on any failure.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…efaults

- Move settings/presets/albums/library/luts/models/window-state out of the
  bundle-identifier folder into the same RapidRAW/ folder sidecars already
  use, with one-time migration for existing installs (sidecar_paths.rs).
- Default date/date-taken sorts to newest-first (both the view-options
  dropdown and the list header column click).
- Split the Adjustments panel into dedicated tabs: Color & Curves, Details,
  and Effects, alongside a slimmer Adjustments tab (Basic + Sharpening +
  Presence). All adjustment sections now open expanded by default.
- Add a Settings button to the library header (previously only reachable
  from the pre-folder splash screen).
- Flatten the Masks panel's "Others" submenu so every mask type is a
  direct, first-class tile/menu entry.
- Bake in preferred app defaults for a fresh install: panel layout/widths,
  thumbnail/preview quality, exif overlay, grouping, native OS titlebar,
  and maximized on first launch.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The setting existed and worked (setDecorations at runtime) but the
switch was gated to osPlatform === 'linux', so Windows users had no
way to reach it from Settings.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- The CPU thumbnail/preview fallback path applied the deliberately dark,
  filmic AgX tonemapper to every unedited RAW regardless of the
  tonemapper-override setting, while the GPU-rendered editor path already
  treated AgX as opt-in (tonemapper_override_enabled, default off). That
  mismatch is why every freshly-opened RAW looked much darker than its own
  editor view. The CPU path now respects the same override gate.
- Thumbnail cache, log files, and the WebView2/WebKitGTK data directory
  were still being written under the bundle-identifier folder
  (app_cache_dir/app_log_dir/the webview's own default data directory,
  scattered across both Roaming and Local AppData on Windows) even after
  settings/presets/sidecars moved to RapidRAW/. They now all live under
  the same app_root_dir() root, so nothing is left writing to
  io.github.CyberTimon.RapidRAW.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
vinioliveiras and others added 19 commits August 22, 2026 18:01
- start_thumbnail_workers() re-resolved the thumbnail cache directory on
  every single photo, from every worker thread, in parallel — an expensive
  filesystem round trip (and a one-time migration check) that used to be a
  cheap flat path lookup. Beyond the slowdown, a transient failure there
  silently skipped that photo's progress tick forever (the Err arm of the
  `if let Ok(cache_dir) = ...` guard did nothing), which could look like
  generation had hung. Now resolved once, up front, and shared by every
  worker.
- Thumbnails, logs, and the WebView2/WebKitGTK data directory were moved
  into app_root_dir() (Roaming) in the previous commit alongside settings
  and presets, but that's the wrong home for large, disposable, frequently
  rewritten cache data — Windows treats the Roaming profile as worth
  syncing/backing up, which is exactly the slowdown app_local_data_dir()
  exists to avoid. Added app_local_root_dir(), a sibling of app_root_dir()
  rooted under Local AppData instead, and moved those three there.
- Restored thumbnail_resolution to 360 (bumped to 640 by the "bake in my
  settings" commit, undoing the earlier speed-focused default) and raised
  thumbnail_worker_threads' clamp ceiling from 12 to 16 to match the
  setting's own max, so high-core machines can actually use all of it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The new default (360) wasn't selectable in the dropdown, which only
started at 640 — so the UI couldn't reflect the value actually in use
without picking something else first.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…d cache path

Thumbnails were generating and writing to disk successfully, but the
frontend couldn't load any of them — Tauri's asset:// protocol rejects
any path outside app.security.assetProtocol.scope, which still pointed
at $APPCACHE/thumbnails (the old bundle-identifier-nested cache dir)
after the previous commit moved the actual thumbnail cache to
Local AppData's RapidRAW/thumbnails. Every load came back as
"asset protocol not configured to allow the path", which looked
indistinguishable from generation itself being broken.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
0.5 halved the editor preview's rendered resolution below the fit-to-
window canvas size even at 100% zoom (not just when zooming in past
native, which is what the setting is meant to trade off), making the
live edit view look soft/undersized. Restoring the original desktop
default.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
useImageRenderSize measured the image container exactly once on mount
plus on every ResizeObserver-reported change. When the OS window opens
already maximized (see the earlier "maximize on first launch" change),
WebView2 can report a stale, pre-maximize clientWidth/clientHeight for
that very first measurement — the native window has finished resizing,
but the webview's internal layout hasn't caught up yet. Since nothing
actually resizes afterward, ResizeObserver never fires again to
self-correct, so the editor's live preview stayed rendered at a much
smaller effective resolution than the actual canvas until the user
manually resized the window (which was the only way to force a fresh,
correct measurement). Two follow-up re-measurements (next animation
frame, then 250ms later) now catch that race automatically.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A whole-folder thumbnail scan runs up to 16 worker threads at normal OS
priority, competing directly with the interactive editor's own CPU/GPU
work for scheduler time — this is what made the editor feel sluggish
("usa os recursos no talo") while a big folder's thumbnails were still
generating. Workers now run at the OS's lowest thread priority via the
thread-priority crate: the scheduler still gives them full throughput
whenever the CPU is otherwise idle (a folder still finishes fast), but
automatically yields to the editor's threads the moment it's actually
busy, instead of fighting them for every timeslice. Best-effort — an
unsupported platform/sandbox just keeps the default priority.

Also lowered the default thumbnail_resolution from 360 to 240: grid
tiles run 160-320px (small/medium/large), so 240 stays sharp at those
sizes while cutting resize/encode cost further for a whole-library
scan. Added 240px to the Settings dropdown alongside it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
generate_all_community_previews always fully decoded each representative
RAW photo (develop_raw_image, ~500ms each) before downscaling it to a
720px processing tile — the same fast-vs-full-demosaic tradeoff
generate_thumbnail_data already made for unedited library thumbnails.
Since a handful of community presets get applied to this same decoded
base per photo, try the camera's own embedded JPEG preview first
(try_load_embedded_raw_preview, now pub(crate)) and only fall back to a
real demosaic when no usable embedded preview exists.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
downloadStatus (which drives the Save/Saved button state per preset)
was purely local component state, so it forgot everything the moment
you closed the Community panel and reopened it — every preset showed
"Save" again even if you'd already saved it in a previous visit. Now
cross-references the manifest against your actual local presets (which
save_community_preset already dedupes by name under a "Community"
folder) once the manifest loads, marking matches as already-saved.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
exifOverlay and grouping had drifted back to "off" from their earlier
baked-in defaults ("hover"/"raw") since I changed them again through
the UI after that commit; the default active tab on the right side
also settled on "Details" rather than "Adjustments" through actual use.
Bringing the shipped defaults in line with what's actually in daily use.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Matches actual daily use (date_taken/desc shows up in the live
settings every time), rather than leaving fresh installs on the
generic name-ascending fallback.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Was already the effective behavior (FilterCriteria::default() already
produces rating:0/rawStatus:all/editedStatus:all/colors:[], and the
frontend's own fallback matches it) — this just makes a clean install's
settings.json say so explicitly instead of relying on the field being
absent.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Details had become the default active tab from the last "sync to my
current settings" pass, but a first-ever launch should land on the
main Adjustments tab, not whichever one happened to be active last.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The "maximize on first launch" window.maximize() call ran inside
.setup(), on a window still built with .visible(false) — maximizing a
hidden window doesn't reliably propagate to WebView2's internal layout
on Windows, so the webview could stay stuck believing it's still the
small 1280x720 default until a real, visible resize happens. That's
exactly the "editor canvas renders tiny until you manually shrink and
re-maximize the window" bug, and it only showed up on a genuinely fresh
install specifically because that's the one path where maximize() was
called pre-show — restoring a "was maximized" state from a previous
session already maximizes *after* window.show(), which never had this
problem.

Fixed by removing the pre-show maximize() calls (falling back to
window.center() there instead, same as the "unusable saved state"
case) and instead setting should_maximize = true for both "no
window_state.json" and "corrupt window_state.json" in frontend_ready,
routing them through the exact same post-show maximize() call that
already worked correctly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Confirmed via pixel-diffing OLD (pre-change) vs NEW exports of the same
source RAWs at the same settings: with a fine 2.5px radius, differences
concentrated specifically along thin high-contrast edges (bridge
cables, trusswork) rather than being spread uniformly across the
frame — a visible halo/glow around fine detail that wasn't there with
the original wide radius. A soft, low-detail portrait (skin, grass,
bokeh) barely showed the difference at all (0.01% of pixels), while a
detail-dense architectural shot showed it clearly (0.6%), which is
exactly the edge-localized signature a local-contrast/clarity-style
blur produces at a much finer radius. Restoring the wide radius (and
the "Structure"/"Estrutura" label that went with it) since this wasn't
the intended look.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Imported Lightroom presets rendered much darker and more crushed than
in Lightroom, and the AGX tonemapper was unusable on its own:

- Remove an unjustified 1.5x amplification on imported Shadows2012
- Mask Whites to highlights only instead of multiplying the whole image
- Narrow the Highlights mask so it stops acting on midtones
- Fix a missing linear->sRGB encode after the AGX tonemap curve, which
  made any AGX-rendered image crushed and low-contrast
- Default RAW images to the AGX tonemapper (non-RAW keeps "basic"),
  matching the app's intended per-image-type default
- Wire SharpenEdgeMasking through to the sharpness threshold instead of
  discarding it on import
- Apply a baseline Color Noise Reduction default (matching ACR) when a
  preset doesn't touch it

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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