Modernize CameraCaptureUI sample to Single Project (.NET 10, Uno.Sdk 6.5.31)#912
Modernize CameraCaptureUI sample to Single Project (.NET 10, Uno.Sdk 6.5.31)#912mtmattei wants to merge 2 commits intounoplatform:masterfrom
Conversation
…dk 6.5.31) - Migrated from multi-head (Shared + Mobile + Windows) to Single Project - Upgraded from .NET 8 / Uno.WinUI 5.1.87 to .NET 10 / Uno.Sdk 6.5.31 - Added Desktop (Skia) and WebAssembly targets with FileOpenPicker fallback - Added required Android and iOS camera permissions - Fixed image loading to use stream-based SetSourceAsync Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Modernizes the CameraCaptureUI sample by migrating it from the legacy multi-head structure to a Uno Platform Single Project layout, updating SDK/tooling and adding platform heads and assets required by the new structure.
Changes:
- Migrated the sample solution/projects to a Uno.Sdk Single Project targeting .NET 10 (with Desktop + WebAssembly heads added).
- Updated the sample UI/logic to support capture/pick flows and stream-based image loading.
- Added/updated platform-specific scaffolding (manifests, launch settings, iOS/Android/WASM platform folders, assets).
Reviewed changes
Copilot reviewed 55 out of 99 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| UI/CameraCaptureUI/global.json | Pins Uno.Sdk version via MSBuild SDK resolver. |
| UI/CameraCaptureUI/Directory.Packages.props | Enables Central Package Management file (empty placeholder). |
| UI/CameraCaptureUI/Directory.Build.targets | Adds solution-level build targets placeholder for the sample. |
| UI/CameraCaptureUI/Directory.Build.props | Enables repo-wide build settings (nullable, implicit usings, NoWarn). |
| UI/CameraCaptureUI/CameraCaptureUISample.sln | Simplifies solution to the single project + solution items. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.csproj | New Uno.Sdk single-project app definition + target frameworks. |
| UI/CameraCaptureUI/CameraCaptureUISample/GlobalUsings.cs | Adds global using directives for common app infrastructure. |
| UI/CameraCaptureUI/CameraCaptureUISample/App.xaml | Defines app resources and WinUI resources merge. |
| UI/CameraCaptureUI/CameraCaptureUISample/App.xaml.cs | New single-project app bootstrap, window setup, and logging configuration. |
| UI/CameraCaptureUI/CameraCaptureUISample/MainPage.xaml | New sample UI layout (button, image preview, status text). |
| UI/CameraCaptureUI/CameraCaptureUISample/MainPage.xaml.cs | New capture/pick flow and stream-based image loading. |
| UI/CameraCaptureUI/CameraCaptureUISample/Package.appxmanifest | Adds Win32/MSIX packaging manifest stub for Windows packaging. |
| UI/CameraCaptureUI/CameraCaptureUISample/app.manifest | Updates Windows app manifest identity + compatibility block. |
| UI/CameraCaptureUI/CameraCaptureUISample/ReadMe.md | Adds basic getting started links for the sample. |
| UI/CameraCaptureUI/CameraCaptureUISample/Properties/launchSettings.json | Adds WebAssembly + Desktop launch profiles. |
| UI/CameraCaptureUI/CameraCaptureUISample/Strings/en/Resources.resw | Updates Windows resource strings (including ApplicationName). |
| UI/CameraCaptureUI/CameraCaptureUISample/Assets/SharedAssets.md | New shared asset guidance doc in the single-project structure. |
| UI/CameraCaptureUI/CameraCaptureUISample/Assets/Icons/icon.svg | Adds base app icon vector asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/Assets/Icons/icon_foreground.svg | Adds foreground icon vector asset (for generated icons). |
| UI/CameraCaptureUI/CameraCaptureUISample/Assets/Splash/splash_screen.svg | Adds splash screen vector asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/Platforms/Desktop/Program.cs | Adds Skia Desktop host entry point. |
| UI/CameraCaptureUI/CameraCaptureUISample/Platforms/WebAssembly/Program.cs | Adds WebAssembly host entry point. |
| UI/CameraCaptureUI/CameraCaptureUISample/Platforms/WebAssembly/LinkerConfig.xml | Adds WASM linker configuration file. |
| UI/CameraCaptureUI/CameraCaptureUISample/Platforms/WebAssembly/manifest.webmanifest | Adds PWA manifest for WebAssembly target. |
| UI/CameraCaptureUI/CameraCaptureUISample/Platforms/WebAssembly/WasmScripts/AppManifest.js | Adds Uno WASM app manifest script. |
| UI/CameraCaptureUI/CameraCaptureUISample/Platforms/WebAssembly/WasmCSS/Fonts.css | Adds WASM font-face configuration. |
| UI/CameraCaptureUI/CameraCaptureUISample/Platforms/WebAssembly/wwwroot/web.config | Adds IIS web.config for precompressed/static content handling. |
| UI/CameraCaptureUI/CameraCaptureUISample/Platforms/WebAssembly/wwwroot/staticwebapp.config.json | Adds Azure Static Web Apps routing/cache configuration. |
| UI/CameraCaptureUI/CameraCaptureUISample/Platforms/iOS/Main.iOS.cs | Adds iOS host entry point using UnoPlatformHostBuilder. |
| UI/CameraCaptureUI/CameraCaptureUISample/Platforms/iOS/Info.plist | Updates iOS Info.plist (capabilities + privacy usage strings). |
| UI/CameraCaptureUI/CameraCaptureUISample/Platforms/iOS/Entitlements.plist | Adds iOS entitlements placeholder. |
| UI/CameraCaptureUI/CameraCaptureUISample/Platforms/iOS/PrivacyInfo.xcprivacy | Adds iOS privacy manifest file. |
| UI/CameraCaptureUI/CameraCaptureUISample/Platforms/iOS/Media.xcassets/LaunchImages.launchimage/Contents.json | Adds iOS launch images asset catalog metadata. |
| UI/CameraCaptureUI/CameraCaptureUISample/Platforms/Android/environment.conf | Updates Android runtime GC parameters. |
| UI/CameraCaptureUI/CameraCaptureUISample/Platforms/Android/AndroidManifest.xml | Adds Android manifest + permissions required by sample. |
| UI/CameraCaptureUI/CameraCaptureUISample/Platforms/Android/Main.Android.cs | Adds Android Application class for Uno single project. |
| UI/CameraCaptureUI/CameraCaptureUISample/Platforms/Android/MainActivity.Android.cs | Adds Android MainActivity with SplashScreen install. |
| UI/CameraCaptureUI/CameraCaptureUISample/Platforms/Android/Resources/AboutResources.txt | Adds Android resources documentation file. |
| UI/CameraCaptureUI/CameraCaptureUISample/Platforms/Android/Resources/values/Styles.xml | Adds Android themes/styles (including splash screen theme). |
| UI/CameraCaptureUI/CameraCaptureUISample/Platforms/Android/Resources/values/Strings.xml | Adds Android string resources (including ApplicationName). |
| UI/CameraCaptureUI/CameraCaptureUISample/Platforms/Android/Assets/AboutAssets.txt | Fixes text in Android assets documentation file. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Windows/Properties/launchSettings.json | Removes legacy Windows head launch settings (multi-head structure). |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Windows/Package.appxmanifest | Removes legacy Windows head manifest (multi-head structure). |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Windows/CameraCaptureUISample.Windows.csproj | Removes legacy Windows head project. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Windows/Images/Wide310x150Logo.scale-200.png | Removes legacy Windows head image asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Windows/Images/StoreLogo.png | Removes legacy Windows head image asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Windows/Images/Square44x44Logo.targetsize-24_altform-unplated.png | Removes legacy Windows head image asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Windows/Images/Square44x44Logo.scale-200.png | Removes legacy Windows head image asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Windows/Images/Square150x150Logo.scale-200.png | Removes legacy Windows head image asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Windows/Images/SplashScreen.scale-200.png | Removes legacy Windows head image asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Windows/Images/LockScreenLogo.scale-200.png | Removes legacy Windows head image asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Shared/MainPage.xaml | Removes legacy Shared project page. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Shared/MainPage.xaml.cs | Removes legacy Shared project code-behind. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Shared/App.xaml | Removes legacy Shared project App.xaml. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Shared/App.xaml.cs | Removes legacy Shared project App.xaml.cs. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Shared/CameraCaptureUISample.Shared.shproj | Removes legacy shared project file. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Shared/CameraCaptureUISample.Shared.projitems | Removes legacy shared-items definition. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Shared/CameraCaptureUISample.Shared.globs.props | Removes legacy shared-items globbing props. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Shared/Assets/SharedAssets.md | Removes legacy shared-assets documentation (moved to new location). |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Shared/Assets/wide310x150logo.scale-200.png | Removes legacy shared asset image. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Shared/Assets/storelogo.png | Removes legacy shared asset image. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Shared/Assets/square44x44logo.scale-200.png | Removes legacy shared asset image. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Shared/Assets/square150x150logo.scale-200.png | Removes legacy shared asset image. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Shared/Assets/splashscreen.scale-200.png | Removes legacy shared asset image. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Shared/Assets/lockscreenlogo.scale-200.png | Removes legacy shared asset image. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/CameraCaptureUISample.Mobile.csproj | Removes legacy Mobile head project. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/iOS/Main.iOS.cs | Removes legacy iOS entry point (old structure). |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/iOS/LaunchScreen.storyboard | Removes legacy iOS launch storyboard. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/iOS/Media.xcassets/AppIcons.appiconset/Contents.json | Removes legacy iOS app icon catalog metadata. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/iOS/Media.xcassets/AppIcons.appiconset/Icon20.png | Removes legacy iOS icon asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/iOS/Media.xcassets/AppIcons.appiconset/Icon29.png | Removes legacy iOS icon asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/iOS/Media.xcassets/AppIcons.appiconset/Icon40.png | Removes legacy iOS icon asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/iOS/Media.xcassets/AppIcons.appiconset/Icon58.png | Removes legacy iOS icon asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/iOS/Media.xcassets/AppIcons.appiconset/Icon60.png | Removes legacy iOS icon asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/iOS/Media.xcassets/AppIcons.appiconset/Icon76.png | Removes legacy iOS icon asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/iOS/Media.xcassets/AppIcons.appiconset/Icon80.png | Removes legacy iOS icon asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/iOS/Media.xcassets/AppIcons.appiconset/Icon87.png | Removes legacy iOS icon asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/iOS/Media.xcassets/AppIcons.appiconset/Icon120.png | Removes legacy iOS icon asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/iOS/Media.xcassets/AppIcons.appiconset/Icon152.png | Removes legacy iOS icon asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/iOS/Media.xcassets/AppIcons.appiconset/Icon167.png | Removes legacy iOS icon asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/iOS/Media.xcassets/AppIcons.appiconset/Icon180.png | Removes legacy iOS icon asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/iOS/Media.xcassets/AppIcons.appiconset/Icon1024.png | Removes legacy iOS icon asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/iOS/Resources/SplashScreen@2x.png | Removes legacy iOS splash asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/iOS/Resources/SplashScreen@3x.png | Removes legacy iOS splash asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/iOS/Resources/Default-568h@2x.png | Removes legacy iOS launch image asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/iOS/Resources/Fonts/uno-fluentui-assets.ttf | Removes legacy iOS font asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/Android/AndroidManifest.xml | Removes legacy Android manifest (old structure). |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/Android/Main.Android.cs | Removes legacy Android Application class (old structure). |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/Android/MainActivity.Android.cs | Removes legacy Android MainActivity (old structure). |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/Android/Resources/values/Styles.xml | Removes legacy Android styles file. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/Android/Resources/values/Strings.xml | Removes legacy Android strings file. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/Android/Resources/mipmap-ldpi/icon.png | Removes legacy Android icon asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/Android/Resources/mipmap-mdpi/icon.png | Removes legacy Android icon asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/Android/Resources/mipmap-hdpi/icon.png | Removes legacy Android icon asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/Android/Resources/mipmap-tvdpi/icon.png | Removes legacy Android icon asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/Android/Resources/mipmap-xhdpi/icon.png | Removes legacy Android icon asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/Android/Resources/mipmap-xxhdpi/icon.png | Removes legacy Android icon asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/Android/Resources/mipmap-xxxhdpi/icon.png | Removes legacy Android icon asset. |
| UI/CameraCaptureUI/CameraCaptureUISample/CameraCaptureUISample.Mobile/Android/Assets/Fonts/uno-fluentui-assets.ttf | Removes legacy Android font asset. |
Comments suppressed due to low confidence (2)
UI/CameraCaptureUI/CameraCaptureUISample/Platforms/iOS/Info.plist:40
NSMicrophoneUsageDescriptionis declared, but the sample only captures photos (CameraCaptureUIMode.Photo) and never records video/audio. Including the microphone permission will prompt users unnecessarily and may raise App Store review/privacy concerns; please remove it unless video capture is actually supported in the sample UI.
UI/CameraCaptureUI/CameraCaptureUISample/Strings/en/Resources.resw:122ApplicationNameis set toCameraCaptureUISample-en, which will likely surface as a user-visible app name on Windows for the English resources. Other samples use the plain app name here; consider changing it back toCameraCaptureUISample(and localize via separate language resources instead of suffixing the value).
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| <uses-permission android:name="android.permission.CAMERA" /> | ||
| <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> | ||
| <application android:allowBackup="true" android:supportsRtl="true"></application> |
There was a problem hiding this comment.
WRITE_EXTERNAL_STORAGE is deprecated/ignored on newer Android versions and is a high-friction permission for a camera/photo sample. If it’s only needed for older Android versions, add an appropriate android:maxSdkVersion (commonly 28) or remove it if capture works without it to avoid unnecessary permissions and Play policy issues.
| <TargetFrameworks>net10.0-android;net10.0-ios;net10.0-browserwasm;net10.0-desktop</TargetFrameworks> | ||
|
|
There was a problem hiding this comment.
The PR description and MainPage logic indicate Windows should use CameraCaptureUI, but the project currently doesn’t target a Windows TFM (e.g., net10.0-windows...)—only net10.0-desktop. If Windows camera capture is intended, add the Windows target framework (and any required Uno features) or update the PR description/platform matrix accordingly.
| #if __ANDROID__ || __IOS__ || __WINDOWS__ | ||
| using Windows.Media.Capture; | ||
| #endif | ||
|
|
||
| namespace CameraCaptureUISample; | ||
|
|
||
| public sealed partial class MainPage : Page | ||
| { | ||
| public MainPage() | ||
| { | ||
| this.InitializeComponent(); | ||
| } | ||
|
|
||
| private async void CaptureButton_Click(object sender, RoutedEventArgs e) | ||
| { | ||
| try | ||
| { | ||
| StorageFile? photo = null; | ||
|
|
||
| #if __ANDROID__ || __IOS__ || __WINDOWS__ | ||
| var captureUI = new CameraCaptureUI(); | ||
| captureUI.PhotoSettings.Format = CameraCaptureUIPhotoFormat.Jpeg; | ||
| captureUI.PhotoSettings.CroppedSizeInPixels = new Windows.Foundation.Size(200, 200); | ||
|
|
||
| photo = await captureUI.CaptureFileAsync(CameraCaptureUIMode.Photo); |
There was a problem hiding this comment.
The platform guard uses __WINDOWS__, but this symbol isn’t used elsewhere in the repo (most code uses WINDOWS/WINDOWS_UWP). As written, the CameraCaptureUI path may never compile/run on Windows, pushing Windows builds into the file-picker fallback. Consider switching to the repo’s established Windows symbol(s), or using a runtime capability check and falling back when CameraCaptureUI isn’t supported.
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using System.Text; | ||
| using Android.App; | ||
| using Android.Content; | ||
| using Android.OS; | ||
| using Android.Runtime; | ||
| using Android.Views; | ||
| using Android.Widget; | ||
| using Microsoft.UI.Xaml.Media; | ||
|
|
There was a problem hiding this comment.
There are a number of unused using directives here (e.g., System.Collections.Generic, System.Linq, Android.OS, Android.Views, Android.Widget, Microsoft.UI.Xaml.Media). Cleaning them up will avoid build warnings and keep the platform head minimal.
| <plist version="1.0"> | ||
| <!-- see https://aka.platform/uno/apple-privacy-manifest for more information --> | ||
|
|
||
| <!-- .NET Runtime/BCL --> | ||
| <dict> | ||
| <key>NSPrivacyAccessedAPIType</key> | ||
| <string>NSPrivacyAccessedAPICategoryFileTimestamp</string> | ||
| <key>NSPrivacyAccessedAPITypeReasons</key> | ||
| <array> | ||
| <string>C617.1</string> | ||
| </array> | ||
| </dict> | ||
| <dict> |
There was a problem hiding this comment.
Privacy manifest is not a valid plist: <plist> must contain a single root object, but this file has multiple top-level <dict> elements. iOS tooling/App Store privacy checks may ignore or reject it. Wrap these entries under a single root (e.g., a <dict> containing an NSPrivacyAccessedAPITypes array of dicts) per Apple’s privacy manifest schema.
| <?xml version="1.0" encoding="utf-8"?> | ||
| <configuration> | ||
| <system.web> | ||
| <customErrors mode="Off"/> |
There was a problem hiding this comment.
The customErrors setting is configured with mode="Off", which causes IIS/ASP.NET to return full detailed error pages (including stack traces, file paths, and potentially secrets) to all remote clients whenever a server error occurs. An attacker can intentionally trigger server-side errors with malformed or crafted requests to harvest this diagnostic information and use it to pivot to more serious attacks. For production deployments, configure customErrors to RemoteOnly or On so that detailed error information is only available locally and generic error responses are sent to remote users.
PR Type:
What is the current behavior? 🤔
The CameraCaptureUI sample uses the legacy multi-head project structure
(Shared + Mobile + Windows), targets .NET 8 with Uno.WinUI 5.1.87 (NuGet), and
only supports Android, iOS, and Windows. It is missing required Android
(
CAMERA,WRITE_EXTERNAL_STORAGE) and iOS (NSCameraUsageDescription)permissions, and uses
Uri-based image loading which is unreliable onsandboxed platforms.
What is the new behavior? 🚀
FileOpenPickerfallbackwhere
CameraCaptureUIis unsupportedOpenReadAsync+SetSourceAsyncforcross-platform reliability
CameraCaptureUI)FileOpenPicker)FileOpenPicker)PR Checklist ✅
Commits
specification.
sample](https://github.com/unoplatform/uno/blob/master/doc/articles/uno-develo
pment/working-with-the-samples-apps.md) for the changes have been added (for
bug fixes / features) (if applicable)
://github.com/unoplatform/uno/blob/master/doc/.feature-template.md) (for bug
fixes / features)
Screenshots Compare Test Runresults.Other information ℹ️
Tested manually on Desktop (Skia/Windows). File picker opens and displays
selected images correctly.