Skip to content

feat: auto-convert files on extension change with confirmation dialog and progress indicator - #623

Open
gtxPrime wants to merge 9 commits into
KnifeLemon:mainfrom
gtxPrime:feature/extension-change-conversion
Open

gtxPrime wants to merge 9 commits into
KnifeLemon:mainfrom
gtxPrime:feature/extension-change-conversion

Conversation

@gtxPrime

Copy link
Copy Markdown
Contributor

What does this change?

Adds an automatic conversion feature when renaming a file's extension to a supported target format (such as .png → .pdf, image-to-image, video-to-video, or document-to-document). It introduces:

  • Safe file extension monitoring across user directories on Windows, macOS, and Linux without inotify exhaustion or permission deadlocks.
  • General Settings toggles (ConvertOnExtensionChange and ConfirmExtensionChange) with full localization across all 17 supported languages.
  • Material You-styled confirmation dialog (ExtensionChangeConfirmWindow) and non-blocking progress dialog (ExtensionChangeProgressWindow) for conversion feedback.
  • Atomic file replacement with automatic ReadOnly attribute clearing, retry backoff against momentary shell/antivirus file locks, cross-filesystem (EXDEV) copy fallback, and original file preservation on failure.

Type of change

  • Bug fix
  • New feature or improvement
  • New format or engine
  • Translation
  • Documentation
  • Build, CI or dependencies

How was it tested?

  • dotnet format --verify-no-changes
  • dotnet test (All 644 tests in Filee.Core.Tests, plus targeted test runs in Filee.App.Tests)
  • Tried it in the app (what you converted or clicked, and on which OS):
    • OS: Windows 11 (tested locally via Filee.exe)
    • Extension changes:
      • Renamed .png to .pdf: prompt dialog appeared, converted safely, replaced file with valid PDF signature.
      • Renamed to same extension or unsupported extension (e.g. .png to .random): safely ignored.
      • Renamed files that internally already matched the target format: safely ignored without redundant re-conversion.
      • Tested with read-only destination files: ReadOnly attribute cleared and converted without UnauthorizedAccessException.
    • Settings UI: Toggled auto-convert and confirmation switches in Settings → General; verified settings persistence and reactivity.

Screenshots (UI changes)

image image image
  • Settings Toggle: Added "File extension rename" card in Settings → General.
  • Confirmation Dialog: Material You modal card asking to confirm conversion from {Source} to {Target} with "Always convert directly without asking" option.
  • Progress Dialog: Material You card with animated soft indeterminate accent progress bar and filename details.

Checklist

  • New UI strings are in all src/Filee.App/Assets/i18n/*.json files (verified across all 17 catalogs with LocalizationTests)
  • New third-party code or binaries are listed in THIRD-PARTY-NOTICES.md (N/A — uses existing native APIs)
  • The PR does one thing (unrelated changes go in a separate PR)

@gtxPrime
gtxPrime requested a review from KnifeLemon as a code owner October 26, 2026 13:27
@github-actions github-actions Bot added the feature New features label Oct 26, 2026

@KnifeLemon KnifeLemon left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the work and the screenshots, @gtxPrime. Before going further, a few things need to change, and one question about the feature itself.

Whether Filee should do this at all. There's no issue for this feature, and it's a big behavior change: Filee would watch the user's home folders in the background and replace files in place when they're renamed. Please open an issue (or a discussion) first so the scope can be agreed before more code goes in. Watch folders already cover "convert what lands here" on folders the user picks explicitly.

Must fix regardless of that decision:

  1. global.json pins 10.0.100-rc.2.25502.107 with allowPrerelease: true. Please revert it. The repo builds on the released 10.0.100 SDK, and a prerelease pin would affect every contributor and CI.
  2. GetWatchRoots watches the whole user profile recursively on Windows. That includes AppData, browser caches, IDE folders, node_modules and so on, where programs rename files constantly (e.g. file.tmp to file.json). Every such rename gets a signature check and possibly a conversion prompt. If the feature goes ahead, it should only watch folders the user picks, the same way watch folders do.
  3. The original file is replaced. A rename is something people also do to fix a wrong extension (a JPEG saved as .png, a ZIP that's really an .epub). Converting there silently changes their file. The "already matches the target signature" check helps for some formats but not all. At minimum the original must be kept (or moved to the trash), not overwritten.
  4. Start() subscribes to SettingsChanged each time it's called and never unsubscribes; and turning the setting off then on again keeps the old root list even if folders changed.
  5. Tests: ExtensionChangeWatcherTests drive HandleRenameAsync directly, so nothing covers the real FileSystemWatcher path, the settle/lock retry, or the replace step with the original kept on failure.

Happy to look again once there's an agreed issue and these are addressed.


Generated by Claude Code

@KnifeLemon KnifeLemon left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the follow-up, @gtxPrime. Some of the earlier points are in: global.json is back to the repo's SDK, the original is now moved to the trash or kept as name.original.ext instead of being overwritten, and Start() subscribes once and recomputes the roots when settings change.

The main question from the last review is still open, though, and the new commit goes the other way on it:

  1. No agreed issue yet. Please open an issue (or a discussion) describing the feature before more code goes in. Watching folders in the background and converting files on rename is a big behavior change, and whether Filee does it at all, and where, needs to be settled first. Until then this can't be merged.
  2. "Entire computer" watches every fixed drive recursively (ExtensionChangeService.GetWatchRoots, ExtensionChangeScope.EntireSystem). That is C:\ with Program Files, AppData and every cache under it, which is wider than the user-profile watch point 2 asked to drop. If the feature goes ahead, it should only watch folders the user picks, so please remove the EntireSystem scope and the setting rather than keep it as an option.
  3. Broken strings in all 17 language files. The four general.extension_change_scope* strings are double-encoded (UTF-8 read as Windows-1252 and saved again), e.g. für in de.json and ì´ë¦„ì´ in ko.json. They would show up like that in the app.
  4. Unrelated change in ar.json. crop.skip and presets.conflict.Skip had their two Arabic diacritics (U+0651 shadda, U+064D kasratan) swapped. Please revert those two lines; the PR shouldn't touch existing strings.
  5. Point 5 from the last review (tests only drive HandleRenameAsync, nothing covers the real FileSystemWatcher path or the replace/restore step) is still open.

If there's agreement in the issue, the smallest version to aim for would be: off by default, only the user's watch folders, always ask, and keep the original. Happy to look again then.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New features

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants