From ac11c68488b64eb81ca04bbedbc3ce3a2a3d09a2 Mon Sep 17 00:00:00 2001 From: Martin Zikmund Date: Fri, 9 Oct 2026 11:01:04 +0200 Subject: [PATCH 1/9] Migrate to Uno.Sdk Squashed recreation of #305 on top of #315. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01E8bkzyS24p8CYDWLUPsaPC --- .github/workflows/build.yml | 66 +++++++-- Build-Toolkit-Components.ps1 | 8 +- Build-Toolkit-Gallery.ps1 | 88 +++++++++-- .../Renderers/Markdown/MarkdownTextBlock.cs | 4 +- GenerateAllSolution.ps1 | 41 ++++-- GenerateSingleSolution.ps1 | 35 ++++- GenerateVSCodeLaunchConfig.ps1 | 33 ++++- MultiTarget/AvailableTargetFrameworks.props | 3 +- MultiTarget/Defaults.props | 2 +- MultiTarget/DefinedConstants.props | 5 +- MultiTarget/EnabledTargetFrameworks.props | 14 +- MultiTarget/Filter-Supported-Components.ps1 | 4 +- MultiTarget/GenerateAllProjectReferences.ps1 | 4 +- ...eMultiTargetAwareProjectReferenceProps.ps1 | 9 +- MultiTarget/Library.props | 6 +- ...TargetAwareProjectReference.props.template | 3 +- ...getAwareSharedProjectImport.props.template | 2 +- MultiTarget/MultiTargetIdentifiers.props | 16 +- MultiTarget/PackageReferences/Uno.props | 52 +++++-- MultiTarget/ReadMe.md | 28 +++- MultiTarget/Test-Component-Support.ps1 | 17 ++- MultiTarget/UseTargetFrameworks.ps1 | 6 +- MultiTarget/UseUnoWinUI.ps1 | 3 - .../AllComponents/Uno/Assets/Icons/icon.svg | 42 ++++++ .../Uno/Assets/Icons/icon_foreground.svg | 137 ++++++++++++++++++ .../AllComponents/Uno/Assets/SharedAssets.md | 32 ++++ .../Uno/Assets/Splash/splash_screen.svg | 137 ++++++++++++++++++ .../Uno/CommunityToolkit.App.Uno.csproj | 127 ++++++++++++++++ .../Uno/Platforms/Android/AndroidManifest.xml | 5 + .../Platforms/Android/Assets/AboutAssets.txt | 5 + .../Uno/Platforms/Android/Main.Android.cs | 23 +++ .../Platforms/Android/MainActivity.Android.cs | 20 +++ .../Android/Resources/values/Strings.xml | 4 + .../Android/Resources/values/Styles.xml | 8 + .../Uno/Platforms/Android/environment.conf | 2 + .../Uno/Platforms/Desktop/Program.cs | 23 +++ .../Platforms/WebAssembly/LinkerConfig.xml | 3 + .../Uno/Platforms/WebAssembly/Program.cs | 19 +++ .../WebAssembly/WasmScripts/AppManifest.js | 3 + .../WebAssembly/manifest.webmanifest | 10 ++ .../Uno/Platforms/iOS/Entitlements.plist | 6 + .../Uno/Platforms/iOS/Info.plist | 43 ++++++ .../Uno/Platforms/iOS/Main.iOS.cs | 21 +++ .../LaunchImages.launchimage/Contents.json | 20 +++ .../Uno/Platforms/iOS/PrivacyInfo.xcprivacy | 31 ++++ ProjectHeads/AllComponents/Wasm/Program.cs | 14 +- ProjectHeads/App.Head.Android.props | 7 + .../App.Head.Uno.UI.Dependencies.props | 2 - .../App.Head.Uno.WinUI.Dependencies.props | 2 +- ProjectHeads/App.Head.Uno.props | 8 +- ProjectHeads/App.Head.Wasm.props | 3 +- ProjectHeads/GenerateSingleSampleHeads.ps1 | 2 +- .../SingleComponent/Uno/Assets/Icons/icon.svg | 42 ++++++ .../Uno/Assets/Icons/icon_foreground.svg | 137 ++++++++++++++++++ .../Uno/Assets/SharedAssets.md | 32 ++++ .../Uno/Assets/Splash/splash_screen.svg | 137 ++++++++++++++++++ .../Uno/Platforms/Android/AndroidManifest.xml | 5 + .../Platforms/Android/Assets/AboutAssets.txt | 5 + .../Uno/Platforms/Android/Main.Android.cs | 23 +++ .../Platforms/Android/MainActivity.Android.cs | 20 +++ .../Android/Resources/values/Strings.xml | 4 + .../Android/Resources/values/Styles.xml | 8 + .../Uno/Platforms/Android/environment.conf | 2 + .../Uno/Platforms/Desktop/Program.cs | 23 +++ .../Platforms/WebAssembly/LinkerConfig.xml | 3 + .../Uno/Platforms/WebAssembly/Program.cs | 19 +++ .../WebAssembly/WasmScripts/AppManifest.js | 3 + .../WebAssembly/manifest.webmanifest | 10 ++ .../Uno/Platforms/iOS/Entitlements.plist | 6 + .../Uno/Platforms/iOS/Info.plist | 43 ++++++ .../Uno/Platforms/iOS/Main.iOS.cs | 21 +++ .../LaunchImages.launchimage/Contents.json | 20 +++ .../Uno/Platforms/iOS/PrivacyInfo.xcprivacy | 31 ++++ .../Uno/ProjectTemplate.Uno.csproj | 119 +++++++++++++++ ProjectHeads/SingleComponent/Wasm/Program.cs | 16 ++ ...AddMultiTargetCompatibleSampleDocs.targets | 12 ++ ProjectHeads/Tasks/CheckMultiTarget.props | 17 ++- ProjectTemplate/src/MultiTarget.props | 2 +- global.json | 5 +- 79 files changed, 1863 insertions(+), 110 deletions(-) create mode 100644 ProjectHeads/AllComponents/Uno/Assets/Icons/icon.svg create mode 100644 ProjectHeads/AllComponents/Uno/Assets/Icons/icon_foreground.svg create mode 100644 ProjectHeads/AllComponents/Uno/Assets/SharedAssets.md create mode 100644 ProjectHeads/AllComponents/Uno/Assets/Splash/splash_screen.svg create mode 100644 ProjectHeads/AllComponents/Uno/CommunityToolkit.App.Uno.csproj create mode 100644 ProjectHeads/AllComponents/Uno/Platforms/Android/AndroidManifest.xml create mode 100644 ProjectHeads/AllComponents/Uno/Platforms/Android/Assets/AboutAssets.txt create mode 100644 ProjectHeads/AllComponents/Uno/Platforms/Android/Main.Android.cs create mode 100644 ProjectHeads/AllComponents/Uno/Platforms/Android/MainActivity.Android.cs create mode 100644 ProjectHeads/AllComponents/Uno/Platforms/Android/Resources/values/Strings.xml create mode 100644 ProjectHeads/AllComponents/Uno/Platforms/Android/Resources/values/Styles.xml create mode 100644 ProjectHeads/AllComponents/Uno/Platforms/Android/environment.conf create mode 100644 ProjectHeads/AllComponents/Uno/Platforms/Desktop/Program.cs create mode 100644 ProjectHeads/AllComponents/Uno/Platforms/WebAssembly/LinkerConfig.xml create mode 100644 ProjectHeads/AllComponents/Uno/Platforms/WebAssembly/Program.cs create mode 100644 ProjectHeads/AllComponents/Uno/Platforms/WebAssembly/WasmScripts/AppManifest.js create mode 100644 ProjectHeads/AllComponents/Uno/Platforms/WebAssembly/manifest.webmanifest create mode 100644 ProjectHeads/AllComponents/Uno/Platforms/iOS/Entitlements.plist create mode 100644 ProjectHeads/AllComponents/Uno/Platforms/iOS/Info.plist create mode 100644 ProjectHeads/AllComponents/Uno/Platforms/iOS/Main.iOS.cs create mode 100644 ProjectHeads/AllComponents/Uno/Platforms/iOS/Media.xcassets/LaunchImages.launchimage/Contents.json create mode 100644 ProjectHeads/AllComponents/Uno/Platforms/iOS/PrivacyInfo.xcprivacy create mode 100644 ProjectHeads/App.Head.Android.props create mode 100644 ProjectHeads/SingleComponent/Uno/Assets/Icons/icon.svg create mode 100644 ProjectHeads/SingleComponent/Uno/Assets/Icons/icon_foreground.svg create mode 100644 ProjectHeads/SingleComponent/Uno/Assets/SharedAssets.md create mode 100644 ProjectHeads/SingleComponent/Uno/Assets/Splash/splash_screen.svg create mode 100644 ProjectHeads/SingleComponent/Uno/Platforms/Android/AndroidManifest.xml create mode 100644 ProjectHeads/SingleComponent/Uno/Platforms/Android/Assets/AboutAssets.txt create mode 100644 ProjectHeads/SingleComponent/Uno/Platforms/Android/Main.Android.cs create mode 100644 ProjectHeads/SingleComponent/Uno/Platforms/Android/MainActivity.Android.cs create mode 100644 ProjectHeads/SingleComponent/Uno/Platforms/Android/Resources/values/Strings.xml create mode 100644 ProjectHeads/SingleComponent/Uno/Platforms/Android/Resources/values/Styles.xml create mode 100644 ProjectHeads/SingleComponent/Uno/Platforms/Android/environment.conf create mode 100644 ProjectHeads/SingleComponent/Uno/Platforms/Desktop/Program.cs create mode 100644 ProjectHeads/SingleComponent/Uno/Platforms/WebAssembly/LinkerConfig.xml create mode 100644 ProjectHeads/SingleComponent/Uno/Platforms/WebAssembly/Program.cs create mode 100644 ProjectHeads/SingleComponent/Uno/Platforms/WebAssembly/WasmScripts/AppManifest.js create mode 100644 ProjectHeads/SingleComponent/Uno/Platforms/WebAssembly/manifest.webmanifest create mode 100644 ProjectHeads/SingleComponent/Uno/Platforms/iOS/Entitlements.plist create mode 100644 ProjectHeads/SingleComponent/Uno/Platforms/iOS/Info.plist create mode 100644 ProjectHeads/SingleComponent/Uno/Platforms/iOS/Main.iOS.cs create mode 100644 ProjectHeads/SingleComponent/Uno/Platforms/iOS/Media.xcassets/LaunchImages.launchimage/Contents.json create mode 100644 ProjectHeads/SingleComponent/Uno/Platforms/iOS/PrivacyInfo.xcprivacy create mode 100644 ProjectHeads/SingleComponent/Uno/ProjectTemplate.Uno.csproj diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 80543cae..bcb52541 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -112,6 +112,12 @@ jobs: with: global-json-file: tooling/global.json + # Run from ./tooling so global.json scopes the install to the pinned SDK feature + # band; otherwise the workloads land on the runner's newer preinstalled SDK. + - name: Install .NET workloads + working-directory: tooling + run: dotnet workload install wasm-tools + - name: .NET Info (if diagnostics) if: ${{ env.ENABLE_DIAGNOSTICS == 'true' }} run: dotnet --info @@ -151,6 +157,14 @@ jobs: # WinUI 3 not supported on uwp - winui: 3 multitarget: uwp + include: + # The Uno.Sdk head replaces the classic Wasm head on WinUI 3, so it rides on the + # existing wasm/winui3 job as a flag rather than a fake entry in the multitarget axis. + # GenerateSingleSampleHeads.ps1 derives the same thing itself (wasm + WinUI 3); this flag + # only drives which CI steps run below (workloads, build command), not the script's own behavior. + - winui: 3 + multitarget: wasm + unoSdkHead: true env: HEADS_DIRECTORY: tooling/ProjectHeads @@ -180,6 +194,13 @@ jobs: with: global-json-file: tooling/global.json + # Run from ./tooling so global.json scopes the install to the pinned SDK feature + # band; otherwise the workloads land on the runner's newer preinstalled SDK. + - name: Install .NET workloads + if: ${{ matrix.multitarget == 'wasm' }} + working-directory: tooling + run: dotnet workload install wasm-tools ${{ matrix.unoSdkHead && 'android ios maui' || '' }} + - name: .NET Info (if diagnostics) if: ${{ env.ENABLE_DIAGNOSTICS == 'true' }} run: dotnet --info @@ -217,8 +238,17 @@ jobs: - name: Add project heads to ${{ env.TEST_PROJECT_NAME }} working-directory: ./${{ env.TEST_PROJECT_DIRECTORY }} - run: ${{ github.workspace }}/${{ env.HEADS_DIRECTORY }}/GenerateSingleSampleHeads.ps1 -MultiTargets ${{ matrix.multitarget }} -winui ${{ matrix.winui }} -componentPath "${{ github.workspace }}/${{ env.TEST_PROJECT_DIRECTORY }}"${{ env.ENABLE_DIAGNOSTICS == 'true' && ' -UseDiagnostics' || '' }} - + run: >- + ${{ github.workspace }}/${{ env.HEADS_DIRECTORY }}/GenerateSingleSampleHeads.ps1 + -MultiTargets ${{ matrix.multitarget }} + -winui ${{ matrix.winui }} + -componentPath "${{ github.workspace }}/${{ env.TEST_PROJECT_DIRECTORY }}" + ${{ env.ENABLE_DIAGNOSTICS == 'true' && ' -UseDiagnostics' || '' }} + + # The generated solution already contains only the head(s) for the requested MultiTarget(s) + # (GenerateSingleSolution.ps1 swaps in the Uno.Sdk head instead of the classic Wasm one for + # WinUI 3 + wasm), so one MSBuild invocation covers the Uno.Sdk head too - msbuild.exe builds + # it fine, verified locally against dotnet build with identical results. - name: MSBuild working-directory: ./${{ env.TEST_PROJECT_DIRECTORY }} run: msbuild.exe ${{ env.TEST_PROJECT_NAME }}.sln /restore /nowarn:MSB4011 -p:Configuration=Release /m ${{ env.ENABLE_DIAGNOSTICS == 'true' && '/bl' || '' }} /v:${{ env.MSBUILD_VERBOSITY }} @@ -249,10 +279,11 @@ jobs: name: new-experiment-logs-${{ matrix.multitarget }}-winui${{ matrix.winui }} path: ./**/*.*log - wasm-linux: + uno-linux: runs-on: ubuntu-latest env: HEADS_DIRECTORY: tooling/ProjectHeads + PROJECT_DIRECTORY: tooling/ProjectTemplate TEST_PROJECT_NAME: CiTestExp steps: @@ -267,6 +298,18 @@ jobs: with: global-json-file: tooling/global.json + # iOS/maccatalyst workloads aren't installable on Linux (Uno.Sdk + # auto-filters those TFMs there). android + wasm-tools are both + # required: even a `-f net9.0-browserwasm` build restores all of the + # head's TargetFrameworks, so the net9.0-android TFM's workload is + # validated during restore. + # Run from ./tooling so global.json scopes the install to the pinned + # SDK feature band; otherwise the workloads land on the runner's newer + # preinstalled SDK and the 9.0.x build can't see them (NETSDK1147). + - name: Install .NET workloads + working-directory: tooling + run: dotnet workload install android wasm-tools + - name: .NET Info (if diagnostics) if: ${{ env.ENABLE_DIAGNOSTICS == 'true' }} run: dotnet --info @@ -295,21 +338,18 @@ jobs: working-directory: ./components run: dotnet new ctk-component -n ${{ env.TEST_PROJECT_NAME }} - - name: Generate solution - shell: pwsh - working-directory: ./ - run: ./tooling/GenerateAllSolution.ps1${{ env.ENABLE_DIAGNOSTICS == 'true' && ' -UseDiagnostics' || '' }} -MultiTargets wasm - - - name: dotnet build + # Build-Toolkit-Gallery.ps1 sets up TFMs/WinUI/project references and builds the Uno head + # directly (components are pulled in transitively as ProjectReferences), so this replaces + # the previous solution-gen + component prebuild + two head builds with one step - the old + # sequence built every component up to 3x over. + - name: Build Uno head (gallery) shell: pwsh working-directory: ./ - run: ./tooling/Build-Toolkit-Components.ps1 -Release -MultiTargets wasm ${{ env.ENABLE_DIAGNOSTICS == 'true' && ' -BinlogOutput ./ -EnableBinLogs' || '' }} - - # TODO: Do we want to run tests here? Can we do that on linux easily? + run: ./tooling/Build-Toolkit-Gallery.ps1 -Heads Uno -Release -MultiTargets wasm,linux ${{ env.ENABLE_DIAGNOSTICS == 'true' && ' -BinlogOutput ./ -EnableBinLogs' || '' }} - name: Artifact - Diagnostic Logs uses: actions/upload-artifact@v4 if: ${{ (env.ENABLE_DIAGNOSTICS == 'true' || env.COREHOST_TRACE != '') && always() }} with: - name: linux-logs + name: uno-linux-logs path: ./**/*.*log diff --git a/Build-Toolkit-Components.ps1 b/Build-Toolkit-Components.ps1 index 6cbc0ec5..4fac8c41 100644 --- a/Build-Toolkit-Components.ps1 +++ b/Build-Toolkit-Components.ps1 @@ -57,11 +57,11 @@ Date: 2/19/2024 #> Param ( - [ValidateSet('all', 'wasm', 'uwp', 'wasdk', 'wpf', 'linuxgtk', 'macos', 'ios', 'android', 'netstandard')] + [ValidateSet('all', 'wasm', 'uwp', 'wasdk', 'wpf', 'win32', 'linux', 'macos', 'ios', 'android', 'netstandard')] [Alias("mt")] [string[]]$MultiTargets = @('uwp', 'wasm', 'wasdk'), # default settings - - [ValidateSet('wasm', 'uwp', 'wasdk', 'wpf', 'linuxgtk', 'macos', 'ios', 'android', 'netstandard')] + + [ValidateSet('wasm', 'uwp', 'wasdk', 'wpf', 'win32', 'linux', 'macos', 'ios', 'android', 'netstandard')] [string[]]$ExcludeMultiTargets = @(), # default settings [Alias("c")] @@ -97,7 +97,7 @@ Param ( if ($MultiTargets -eq 'all') { - $MultiTargets = @('wasm', 'uwp', 'wasdk', 'wpf', 'linuxgtk', 'macos', 'ios', 'android', 'netstandard') + $MultiTargets = @('wasm', 'uwp', 'wasdk', 'wpf', 'win32', 'linux', 'macos', 'ios', 'android', 'netstandard') } if ($null -eq $ExcludeMultiTargets) diff --git a/Build-Toolkit-Gallery.ps1 b/Build-Toolkit-Gallery.ps1 index 7d92154e..cf27564c 100644 --- a/Build-Toolkit-Gallery.ps1 +++ b/Build-Toolkit-Gallery.ps1 @@ -9,7 +9,7 @@ Specifies the MultiTarget TFM(s) to include for building the components. The default value is 'all'. .PARAMETER ExcludeMultiTargets - Specifies the MultiTarget TFM(s) to exclude for building the components. The default value excludes targets that require additional tooling or workloads to build: 'wpf', 'linuxgtk', 'macos', 'ios', and 'android'. Run uno-check to install the required workloads. + Specifies the MultiTarget TFM(s) to exclude for building the components. The default value excludes targets that require additional tooling or workloads to build: 'wpf', 'linux', 'macos', 'ios', and 'android'. Run uno-check to install the required workloads. .PARAMETER Heads The heads to include in the build. Default is 'Uwp', 'Wasdk', 'Wasm'. @@ -46,17 +46,17 @@ Date: 2/19/2024 #> Param ( - [ValidateSet('all', 'wasm', 'uwp', 'wasdk', 'wpf', 'linuxgtk', 'macos', 'ios', 'android', 'netstandard')] + [ValidateSet('all', 'wasm', 'uwp', 'wasdk', 'wpf', 'win32', 'linux', 'macos', 'ios', 'android', 'netstandard')] [Alias("mt")] [string[]]$MultiTargets = @('uwp', 'wasdk', 'wasm'), # default settings - - [ValidateSet('wasm', 'uwp', 'wasdk', 'wpf', 'linuxgtk', 'macos', 'ios', 'android', 'netstandard')] + + [ValidateSet('wasm', 'uwp', 'wasdk', 'wpf', 'win32', 'linux', 'macos', 'ios', 'android', 'netstandard')] [string[]]$ExcludeMultiTargets = @(), # default settings - [ValidateSet('all', 'Uwp', 'Wasdk', 'Wasm', 'Tests.Uwp', 'Tests.Wasdk')] + [ValidateSet('all', 'Uwp', 'Wasdk', 'Wasm', 'Uno', 'Tests.Uwp', 'Tests.Wasdk')] [string[]]$Heads = @('Uwp', 'Wasdk', 'Wasm'), - [ValidateSet('Uwp', 'Wasdk', 'Wasm', 'Tests.Uwp', 'Tests.Wasdk')] + [ValidateSet('Uwp', 'Wasdk', 'Wasm', 'Uno', 'Tests.Uwp', 'Tests.Wasdk')] [string[]]$ExcludeHeads, [Alias("bl")] @@ -102,7 +102,7 @@ if ($MultiTargets.Contains('uwp') -and $MultiTargets.Contains('wasdk')) } if ($MultiTargets -eq 'all') { - $MultiTargets = @('wasm', 'uwp', 'wasdk', 'wpf', 'linuxgtk', 'macos', 'ios', 'android', 'netstandard') + $MultiTargets = @('wasm', 'uwp', 'wasdk', 'wpf', 'win32', 'linux', 'macos', 'ios', 'android', 'netstandard') } if ($ExcludeMultiTargets) { @@ -131,17 +131,31 @@ if ($Components -notcontains 'Converters') { & $PSScriptRoot\MultiTarget\GenerateAllProjectReferences.ps1 -MultiTarget $MultiTargets -Components $Components if ($Heads -eq 'all') { - $Heads = @('Uwp', 'Wasdk', 'Wasm', 'Tests.Uwp', 'Tests.Wasdk') + $Heads = @('Uwp', 'Wasdk', 'Wasm', 'Uno', 'Tests.Uwp', 'Tests.Wasdk') } function Invoke-MSBuildWithBinlog { param ( - [string]$TargetHeadPath + [string]$TargetHeadPath, + [string]$TargetFramework ) # Reset build args to default $msbuildArgs = @("-r", "-m", "-t:Clean,Build") + if ($TargetFramework) { + # dotnet build's "-f" does a scoped inner-build restore for just that TFM; plain + # "/p:TargetFramework=" on a multi-targeted project restores the outer project's full TFM + # closure first, which fails for components that don't support every TFM the head declares. + # msbuild.exe has no "-f" equivalent, so this only reliably supports one TFM at a time there. + if ($($PSVersionTable.Platform) -eq "Unix") { + $msbuildArgs += "-f:$TargetFramework" + } + else { + $msbuildArgs += "/p:TargetFramework=$TargetFramework" + } + } + # Add additional properties to the msbuild arguments if ($AdditionalProperties) { foreach ($property in $AdditionalProperties.GetEnumerator()) { @@ -153,12 +167,13 @@ function Invoke-MSBuildWithBinlog { if ($EnableBinLogs) { # Get binlog filename and output path $csprojFileName = [System.IO.Path]::GetFileNameWithoutExtension($TargetHeadPath) - $defaultBinlogFilename = "$csprojFileName.msbuild.binlog" + $binlogSuffix = $TargetFramework ? ".$TargetFramework" : "" + $defaultBinlogFilename = "$csprojFileName$binlogSuffix.msbuild.binlog" $finalBinlogPath = $defaultBinlogFilename; # Set default binlog output location if not provided if ($BinlogOutput) { - $finalBinlogPath = "$BinlogOutput/$defaultBinlogFilename" + $finalBinlogPath = "$BinlogOutput/$defaultBinlogFilename" } $msbuildArgs += "/bl:$finalBinlogPath" @@ -172,7 +187,40 @@ function Invoke-MSBuildWithBinlog { $msbuildArgs += "/verbosity:detailed" } - msbuild $msbuildArgs $TargetHeadPath + # On Linux there's no msbuild.exe on PATH; dotnet build is the only option there anyway. + if ($($PSVersionTable.Platform) -eq "Unix") { + dotnet build $msbuildArgs $TargetHeadPath + } + else { + msbuild $msbuildArgs $TargetHeadPath + } +} + +# The Uno.Sdk head covers several MultiTargets from one multi-TFM csproj (see +# ProjectHeads/AllComponents/Uno/CommunityToolkit.App.Uno.csproj). Building all of its enabled TFMs +# in one invocation isn't reliable across every combination, so - matching what CI already does - +# build it one TFM at a time via `-f`, only for the TFMs the requested $MultiTargets actually enable. +function Get-UnoSdkHeadTargetFrameworks { + param ( + [string[]]$MultiTargets + ) + + $targetFrameworks = [System.Collections.ArrayList]::new() + + if (($MultiTargets | Where-Object { @('win32', 'linux', 'macos') -contains $_ }).Count -gt 0) { + [void]$targetFrameworks.Add('net9.0-desktop') + } + if ($MultiTargets -contains 'wasm') { + [void]$targetFrameworks.Add('net9.0-browserwasm') + } + if ($MultiTargets -contains 'android') { + [void]$targetFrameworks.Add('net9.0-android') + } + if ($MultiTargets -contains 'ios') { + [void]$targetFrameworks.Add('net9.0-ios') + } + + return $targetFrameworks } foreach ($head in $Heads) { @@ -182,5 +230,19 @@ foreach ($head in $Heads) { $targetHeadPath = Get-ChildItem "$PSScriptRoot/ProjectHeads/AllComponents/$head/*.csproj" - Invoke-MSBuildWithBinlog $targetHeadPath $EnableBinLogs $BinlogOutput + if ($head -eq 'Uno') { + $unoTargetFrameworks = Get-UnoSdkHeadTargetFrameworks -MultiTargets $MultiTargets + + if ($unoTargetFrameworks.Count -eq 0) { + Write-Warning "None of the requested MultiTargets ($MultiTargets) are served by the Uno.Sdk head. Skipping." + continue + } + + foreach ($targetFramework in $unoTargetFrameworks) { + Invoke-MSBuildWithBinlog $targetHeadPath $targetFramework + } + } + else { + Invoke-MSBuildWithBinlog $targetHeadPath + } } diff --git a/CommunityToolkit.App.Shared/Renderers/Markdown/MarkdownTextBlock.cs b/CommunityToolkit.App.Shared/Renderers/Markdown/MarkdownTextBlock.cs index 2b0d3293..76ef5e8d 100644 --- a/CommunityToolkit.App.Shared/Renderers/Markdown/MarkdownTextBlock.cs +++ b/CommunityToolkit.App.Shared/Renderers/Markdown/MarkdownTextBlock.cs @@ -16,7 +16,7 @@ #endif #endif -#if HAS_UNO_WASM +#if HAS_UNO_WASM && !WINUI3 using Markdig; using Uno.Foundation.Interop; using Uno.UI.Runtime.WebAssembly; @@ -27,7 +27,7 @@ namespace CommunityToolkit.App.Shared.Renderers; /// /// Provide an abstraction around the Toolkit MarkdownTextBlock for both UWP and WinUI 3 in the same namespace (until 8.0) as well as a polyfill for WebAssembly/WASM. /// -#if HAS_UNO_WASM +#if HAS_UNO_WASM && !WINUI3 [HtmlElement("div")] public partial class MarkdownTextBlock : TextBlock { diff --git a/GenerateAllSolution.ps1 b/GenerateAllSolution.ps1 index 72eb9bac..f2ea256f 100644 --- a/GenerateAllSolution.ps1 +++ b/GenerateAllSolution.ps1 @@ -38,11 +38,11 @@ Date: April 27, 2022 #> Param ( - [ValidateSet('all', 'wasm', 'uwp', 'wasdk', 'wpf', 'linuxgtk', 'macos', 'ios', 'android', 'netstandard')] + [ValidateSet('all', 'wasm', 'uwp', 'wasdk', 'wpf', 'win32', 'linux', 'macos', 'ios', 'android', 'netstandard')] [Alias("mt")] - [string[]]$MultiTargets = @('uwp', 'wasm', 'wasdk'), + [string[]]$MultiTargets = @('uwp', 'wasm', 'wasdk', 'win32'), - [ValidateSet('wasm', 'uwp', 'wasdk', 'wpf', 'linuxgtk', 'macos', 'ios', 'android', 'netstandard')] + [ValidateSet('wasm', 'uwp', 'wasdk', 'wpf', 'win32', 'linux', 'macos', 'ios', 'android', 'netstandard')] [string[]]$ExcludeMultiTargets = @(), # default settings [Alias("c")] @@ -54,12 +54,12 @@ Param ( [string[]]$ExcludeComponents, [switch]$UseDiagnostics = $false, - + [bool]$Launch = $true ) if ($MultiTargets.Contains('all')) { - $MultiTargets = @('wasm', 'uwp', 'wasdk', 'wpf', 'linuxgtk', 'macos', 'ios', 'android', 'netstandard') + $MultiTargets = @('wasm', 'uwp', 'wasdk', 'wpf', 'win32', 'linux', 'macos', 'ios', 'android', 'netstandard') } if ($null -eq $ExcludeMultiTargets) @@ -150,11 +150,25 @@ foreach ($componentName in $Components) { # Deployable sample gallery heads # Only include heads for requested MultiTargets if components were included that use them. -# === -# TODO: this handles separate project heads, but won't directly handle the unified Skia head from Uno. -# Once we have that, just do a transform on the csproj filename inside this loop to decide the same csproj for those separate MultiTargets. -# === +# These have no head project of their own - they're served by the unified Uno.Sdk head added below. +$unoSdkHeadMultiTargets = @('win32', 'linux', 'macos', 'ios', 'android') + +# The Uno.Sdk head is implied by the requested MultiTargets, not a separate switch: it's the only +# head that can serve win32/linux/macos/ios/android, and for wasm it replaces the classic Wasm head +# once WinUI 3 is requested (WinUI 2 + wasm keeps using the classic head). +$includeUnoSdkHead = (($allUsedMultiTargetPrefs | Where-Object { $unoSdkHeadMultiTargets -contains $_ }).Count -gt 0) -or ` + ($allUsedMultiTargetPrefs.Contains('wasm') -and $WinUIMajorVersion -eq 3) + foreach ($multitarget in $allUsedMultiTargetPrefs) { + if ($unoSdkHeadMultiTargets -contains $multitarget) { + continue + } + + # When using the Uno.Sdk head, skip the traditional Wasm head (Uno.Sdk covers wasm for WinUI 3) + if ($multitarget -eq 'wasm' -and $includeUnoSdkHead) { + continue + } + # capitalize first letter, avoid case sensitivity issues on linux $csprojFileNamePartForMultiTarget = $multitarget.substring(0,1).ToUpper() + $multitarget.Substring(1).ToLower() @@ -168,6 +182,15 @@ foreach ($multitarget in $allUsedMultiTargetPrefs) { } } +if ($includeUnoSdkHead) { + $unoHeadPath = "./tooling/ProjectHeads/AllComponents/Uno/CommunityToolkit.App.Uno.csproj" + if (Test-Path $unoHeadPath) { + [void]$projects.Add($unoHeadPath) + } else { + Write-Warning "Uno.Sdk head project not found at $unoHeadPath." + } +} + if ($UseDiagnostics.IsPresent) { $sdkoptions = "-d" diff --git a/GenerateSingleSolution.ps1 b/GenerateSingleSolution.ps1 index e0e73d7d..a3294ad4 100644 --- a/GenerateSingleSolution.ps1 +++ b/GenerateSingleSolution.ps1 @@ -33,7 +33,7 @@ Date: Feb 9, 2023 #> Param ( - [ValidateSet('all', 'wasm', 'uwp', 'wasdk', 'wpf', 'linuxgtk', 'macos', 'ios', 'android')] + [ValidateSet('all', 'wasm', 'uwp', 'wasdk', 'wpf', 'win32', 'linux', 'macos', 'ios', 'android')] [Alias("mt")] [string[]]$MultiTargets = @('uwp', 'wasm', 'wasdk'), @@ -98,7 +98,7 @@ if (-not (Test-Path "$componentPath/src" -PathType Container)) # ----------------- if ($MultiTargets.Contains('all')) { - $MultiTargets = @('wasm', 'uwp', 'wasdk', 'wpf', 'linuxgtk', 'macos', 'ios', 'android') + $MultiTargets = @('wasm', 'uwp', 'wasdk', 'wpf', 'win32', 'linux', 'macos', 'ios', 'android') } if ($null -eq $ExcludeMultiTargets) @@ -188,9 +188,25 @@ Write-Output "Generating solution for $componentName in $generatedSolutionFilePa # All heads are included by default since they reside in the same folder as the component. # Remove any heads that are not required for the solution. -# TODO: this handles separate project heads, but won't directly handle the unified Skia head from Uno. -# Once we have that, just do a transform on the csproj filename inside this loop to decide the same csproj for those separate MultiTargets. +# These have no head project of their own - they're served by the unified Uno.Sdk head added below. +$unoSdkHeadMultiTargets = @('win32', 'linux', 'macos', 'ios', 'android') + +# The Uno.Sdk head is implied by the requested MultiTargets, not a separate switch: it's the only +# head that can serve win32/linux/macos/ios/android, and for wasm it replaces the classic Wasm head +# once WinUI 3 is requested (WinUI 2 + wasm keeps using the classic head). +$includeUnoSdkHead = (($MultiTargets | Where-Object { $unoSdkHeadMultiTargets -contains $_ }).Count -gt 0) -or ` + ($MultiTargets.Contains('wasm') -and $WinUIMajorVersion -eq 3) + foreach ($multitarget in $MultiTargets) { + if ($unoSdkHeadMultiTargets -contains $multitarget) { + continue + } + + # When using the Uno.Sdk head, skip the traditional Wasm head (Uno.Sdk covers wasm for WinUI 3) + if ($multitarget -eq 'wasm' -and $includeUnoSdkHead) { + continue + } + # capitalize first letter, avoid case sensitivity issues on linux $csprojFileNamePartForMultiTarget = $multitarget.substring(0,1).ToUpper() + $multitarget.Substring(1).ToLower() @@ -208,6 +224,17 @@ foreach ($multitarget in $MultiTargets) { } } +if ($includeUnoSdkHead) { + $unoHeadPath = "$outputHeadsDir\Uno\*Uno.csproj" + if (Test-Path $unoHeadPath) { + foreach ($foundItem in Get-ChildItem $unoHeadPath) { + $projects = $projects + $foundItem.FullName + } + } else { + Write-Warning "Uno.Sdk head project not found at $unoHeadPath." + } +} + # Include common dependencies required for solution to build $projects = $projects + "$PSScriptRoot\CommunityToolkit.App.Shared\**\*.*proj" $projects = $projects + "$PSScriptRoot\CommunityToolkit.Tests.Shared\**\*.*proj" diff --git a/GenerateVSCodeLaunchConfig.ps1 b/GenerateVSCodeLaunchConfig.ps1 index 58ced9ed..f8809ea2 100644 --- a/GenerateVSCodeLaunchConfig.ps1 +++ b/GenerateVSCodeLaunchConfig.ps1 @@ -9,7 +9,7 @@ function CreateVsCodeLaunchConfigJson { ) return "{ - `"name`": `"$projectName`", + `"name`": `"$projectName (Wasm)`", `"type`": `"coreclr`", `"request`": `"launch`", `"program`": `"dotnet`", @@ -29,6 +29,32 @@ function CreateVsCodeLaunchConfigJson { }"; } +function CreateUnoVsCodeLaunchConfigJson { + param ( + [string]$projectName + ) + + return "{ + `"name`": `"$projectName (Uno)`", + `"type`": `"coreclr`", + `"request`": `"launch`", + `"program`": `"dotnet`", + `"args`": [ + `"run`", + `"build`", + `"/r`", + `"/p:UnoSourceGeneratorUseGenerationHost=true`", + `"/p:UnoSourceGeneratorUseGenerationController=false`", + `"/p:UnoRemoteControlPort=443`", + `"--project=`$`{workspaceFolder`}/components/$projectName/heads/Uno/$projectName.Uno.csproj`" + ], + `"presentation`": { + `"group`": `"2`" + }, + `"cwd`": `"`$`{workspaceFolder`}/components/$projectName/heads/Uno`" + }"; +} + $launchConfigJson = Get-Content -Path "$PSScriptRoot/../.vscode/launch.json" -ErrorAction Stop; $launchConfig = $launchConfigJson | ConvertFrom-Json; @@ -46,8 +72,11 @@ foreach ($projectPath in Get-ChildItem -Directory -Depth 0 -Path "$PSScriptRoot/ $configJson = CreateVsCodeLaunchConfigJson $projectName; $config = $configJson | ConvertFrom-Json; - $launchConfig.configurations += $config; + + $unoConfigJson = CreateUnoVsCodeLaunchConfigJson $projectName; + $unoConfig = $unoConfigJson | ConvertFrom-Json; + $launchConfig.configurations += $unoConfig; } if ($allowGitChanges.IsPresent) { diff --git a/MultiTarget/AvailableTargetFrameworks.props b/MultiTarget/AvailableTargetFrameworks.props index 1c02bc51..fbf0a593 100644 --- a/MultiTarget/AvailableTargetFrameworks.props +++ b/MultiTarget/AvailableTargetFrameworks.props @@ -15,7 +15,7 @@ net9.0; net9.0; net9.0; - + net9.0-android; net9.0-maccatalyst; net9.0-ios; @@ -24,6 +24,7 @@ net9.0; net9.0; net9.0; + net9.0; netstandard2.0; diff --git a/MultiTarget/Defaults.props b/MultiTarget/Defaults.props index 86e5e82f..fbf71733 100644 --- a/MultiTarget/Defaults.props +++ b/MultiTarget/Defaults.props @@ -1,5 +1,5 @@ - uwp;wasdk;wpf;wasm;linuxgtk;macos;ios;android; + uwp;wasdk;wpf;win32;wasm;linux;macos;ios;android; \ No newline at end of file diff --git a/MultiTarget/DefinedConstants.props b/MultiTarget/DefinedConstants.props index b5c8c2c9..6152d9e8 100644 --- a/MultiTarget/DefinedConstants.props +++ b/MultiTarget/DefinedConstants.props @@ -13,7 +13,10 @@ $(DefineConstants);__IOS__; $(DefineConstants);__MACOS__; $(DefineConstants);HAS_UNO_SKIA;__SKIA__;WINDOWS_WPF; - $(DefineConstants);HAS_UNO_SKIA;__SKIA__;__GTK__; + $(DefineConstants);HAS_UNO_SKIA;__SKIA__;__GTK__; + $(DefineConstants);HAS_UNO_SKIA;__SKIA__; + + $(DefineConstants);HAS_UNO_SKIA;__SKIA__; $(DefineConstants);NET9_0_OR_GREATER $(DefineConstants);NET8_0_OR_GREATER diff --git a/MultiTarget/EnabledTargetFrameworks.props b/MultiTarget/EnabledTargetFrameworks.props index 6f1c0ab9..e321af61 100644 --- a/MultiTarget/EnabledTargetFrameworks.props +++ b/MultiTarget/EnabledTargetFrameworks.props @@ -10,14 +10,20 @@ $(TargetFrameworks);$(WinAppSdkTargetFrameworks) - true - true + true + true $(TargetFrameworks);$(DotnetCommonTargetFramework) - $(TargetFrameworks);$(MacOSLibTargetFrameworks) + + $(TargetFrameworks);$(MacOSLibTargetFrameworks) $(TargetFrameworks);$(iOSLibTargetFrameworks) $(TargetFrameworks);$(AndroidLibTargetFrameworks) diff --git a/MultiTarget/Filter-Supported-Components.ps1 b/MultiTarget/Filter-Supported-Components.ps1 index bc187e85..2ba1a0bb 100644 --- a/MultiTarget/Filter-Supported-Components.ps1 +++ b/MultiTarget/Filter-Supported-Components.ps1 @@ -17,7 +17,7 @@ Date: 6/6/2025 #> Param ( - [ValidateSet('all', 'wasm', 'uwp', 'wasdk', 'wpf', 'linuxgtk', 'macos', 'ios', 'android', 'netstandard')] + [ValidateSet('all', 'wasm', 'uwp', 'wasdk', 'wpf', 'win32', 'linux', 'macos', 'ios', 'android', 'netstandard')] [Alias("mt")] [Parameter(Mandatory=$true)] [string[]]$MultiTargets, @@ -32,7 +32,7 @@ Param ( ) if ($MultiTargets -eq 'all') { - $MultiTargets = @('wasm', 'uwp', 'wasdk', 'wpf', 'linuxgtk', 'macos', 'ios', 'android', 'netstandard') + $MultiTargets = @('wasm', 'uwp', 'wasdk', 'wpf', 'win32', 'linux', 'macos', 'ios', 'android', 'netstandard') } $supportedComponents = @(); diff --git a/MultiTarget/GenerateAllProjectReferences.ps1 b/MultiTarget/GenerateAllProjectReferences.ps1 index 62ca3b02..ab495bf4 100644 --- a/MultiTarget/GenerateAllProjectReferences.ps1 +++ b/MultiTarget/GenerateAllProjectReferences.ps1 @@ -4,8 +4,8 @@ Param ( [Parameter(HelpMessage = "Only projects that support these targets will have references generated for use by deployable heads.")] [Alias("mt")] - [ValidateSet('wasm', 'uwp', 'wasdk', 'wpf', 'linuxgtk', 'macos', 'ios', 'android', 'netstandard')] - [string[]]$MultiTargets = @("uwp", "wasdk", "wpf", "wasm", "linuxgtk", "macos", "ios", "android", "netstandard"), + [ValidateSet('wasm', 'uwp', 'wasdk', 'wpf', 'win32', 'linux', 'macos', 'ios', 'android', 'netstandard')] + [string[]]$MultiTargets = @("uwp", "wasdk", "wpf", "win32", "wasm", "linux", "macos", "ios", "android", "netstandard"), [Parameter(HelpMessage = "The names of the components to generate references for. Defaults to all components.")] [string[]]$Components = @("all"), diff --git a/MultiTarget/GenerateMultiTargetAwareProjectReferenceProps.ps1 b/MultiTarget/GenerateMultiTargetAwareProjectReferenceProps.ps1 index dbbd6728..461ca388 100644 --- a/MultiTarget/GenerateMultiTargetAwareProjectReferenceProps.ps1 +++ b/MultiTarget/GenerateMultiTargetAwareProjectReferenceProps.ps1 @@ -15,9 +15,9 @@ Param ( [string]$projectRootPlaceholder = "[ProjectRoot]", [Parameter(HelpMessage = "Only projects that support these targets will have references generated for use by deployable heads.")] - [ValidateSet("uwp", "wasdk", "wpf", "wasm", "linuxgtk", "macos", "ios", "android", "netstandard")] + [ValidateSet("uwp", "wasdk", "wpf", "win32", "wasm", "linux", "macos", "ios", "android", "netstandard")] [Alias("mt")] - [string[]] $MultiTargets = @("uwp", "wasdk", "wpf", "wasm", "linuxgtk", "macos", "ios", "android", "netstandard") + [string[]] $MultiTargets = @("uwp", "wasdk", "wpf", "win32", "wasm", "linux", "macos", "ios", "android", "netstandard") ) if ($projectPath.EndsWith(".projitems")) { @@ -74,7 +74,7 @@ function ShouldMultiTargetMsBuildValue([string] $target) { return $(ShouldMultiTarget $target).ToString().ToLower() } -$targeted = @("uwp", "wasdk", "wpf", "wasm", "linuxgtk", "macos", "ios", "android", "netstandard").Where({ ShouldMultiTarget $_ }) +$targeted = @("uwp", "wasdk", "wpf", "win32", "wasm", "linux", "macos", "ios", "android", "netstandard").Where({ ShouldMultiTarget $_ }) if ($targeted.Count -gt 0) { Write-Host "Generating project references for $([System.IO.Path]::GetFileNameWithoutExtension($projectFileName)): $($targeted -Join ', ')" @@ -84,7 +84,8 @@ $templateContents = $templateContents -replace [regex]::escape("[CanTargetWasm]" $templateContents = $templateContents -replace [regex]::escape("[CanTargetUwp]"), "'$(ShouldMultiTargetMsBuildValue "uwp")'"; $templateContents = $templateContents -replace [regex]::escape("[CanTargetWasdk]"), "'$(ShouldMultiTargetMsBuildValue "wasdk")'"; $templateContents = $templateContents -replace [regex]::escape("[CanTargetWpf]"), "'$(ShouldMultiTargetMsBuildValue "wpf")'"; -$templateContents = $templateContents -replace [regex]::escape("[CanTargetLinuxGtk]"), "'$(ShouldMultiTargetMsBuildValue "linuxgtk")'"; +$templateContents = $templateContents -replace [regex]::escape("[CanTargetWin32]"), "'$(ShouldMultiTargetMsBuildValue "win32")'"; +$templateContents = $templateContents -replace [regex]::escape("[CanTargetLinux]"), "'$(ShouldMultiTargetMsBuildValue "linux")'"; $templateContents = $templateContents -replace [regex]::escape("[CanTargetMacOS]"), "'$(ShouldMultiTargetMsBuildValue "macos")'"; $templateContents = $templateContents -replace [regex]::escape("[CanTargetiOS]"), "'$(ShouldMultiTargetMsBuildValue "ios")'"; $templateContents = $templateContents -replace [regex]::escape("[CanTargetDroid]"), "'$(ShouldMultiTargetMsBuildValue "android")'"; diff --git a/MultiTarget/Library.props b/MultiTarget/Library.props index 61ab36c8..99e59e36 100644 --- a/MultiTarget/Library.props +++ b/MultiTarget/Library.props @@ -7,13 +7,15 @@ + + + - - diff --git a/MultiTarget/MultiTargetAwareProjectReference.props.template b/MultiTarget/MultiTargetAwareProjectReference.props.template index 86b3c4d8..9ab42496 100644 --- a/MultiTarget/MultiTargetAwareProjectReference.props.template +++ b/MultiTarget/MultiTargetAwareProjectReference.props.template @@ -11,7 +11,8 @@ ($(IsUwp) == 'true' AND [CanTargetUwp] == 'true') OR ($(IsWinAppSdk) == 'true' AND [CanTargetWasdk] == 'true') OR ($(IsWpf) == 'true' AND [CanTargetWpf] == 'true') OR - ($(IsGtk) == 'true' AND [CanTargetLinuxGtk] == 'true') OR + ($(IsWin32) == 'true' AND [CanTargetWin32] == 'true') OR + ($(IsLinux) == 'true' AND [CanTargetLinux] == 'true') OR ($(IsMacOS) == 'true' AND [CanTargetMacOS] == 'true') OR ($(IsiOS) == 'true' AND [CanTargetiOS] == 'true') OR ($(IsDroid) == 'true' AND [CanTargetDroid] == 'true') OR diff --git a/MultiTarget/MultiTargetAwareSharedProjectImport.props.template b/MultiTarget/MultiTargetAwareSharedProjectImport.props.template index 06c9c8e0..0108d531 100644 --- a/MultiTarget/MultiTargetAwareSharedProjectImport.props.template +++ b/MultiTarget/MultiTargetAwareSharedProjectImport.props.template @@ -7,7 +7,7 @@ ($(IsUwp) == 'true' AND 'false' == 'true') OR ($(IsWinAppSdk) == 'true' AND 'true' == 'true') OR ($(IsWpf) == 'true' AND 'false' == 'true') OR - ($(IsGtk) == 'true' AND 'false' == 'true') OR + ($(IsLinux) == 'true' AND 'false' == 'true') OR ($(IsMacOS) == 'true' AND 'false' == 'true') OR ($(IsiOS) == 'true' AND 'false' == 'true') OR ($(IsDroid) == 'true' AND 'false' == 'true') OR diff --git a/MultiTarget/MultiTargetIdentifiers.props b/MultiTarget/MultiTargetIdentifiers.props index 47c8eb48..37fdfca2 100644 --- a/MultiTarget/MultiTargetIdentifiers.props +++ b/MultiTarget/MultiTargetIdentifiers.props @@ -17,7 +17,8 @@ true true - true + true + true true true true @@ -33,9 +34,14 @@ true true - true - true - true + + true + true + + true + true + true true true @@ -47,7 +53,7 @@ true true - true + true + 5.5.87 + + 6.4.229 + + $(UnoUIPackageVersion) + $(UnoWinUIPackageVersion) - - - + + - + + + + + + + + true - - + + + + + + + + - - + + + - - + + @@ -45,7 +68,12 @@ $(NoWarn);CS1570; - + + + + + + diff --git a/MultiTarget/ReadMe.md b/MultiTarget/ReadMe.md index fd8d7f19..d6ad1704 100644 --- a/MultiTarget/ReadMe.md +++ b/MultiTarget/ReadMe.md @@ -17,7 +17,7 @@ By default, all available targets are enabled: ```xml - uwp;wasdk;wpf;wasm;linuxgtk;macos;ios;android; + uwp;wasdk;wpf;win32;wasm;linux;macos;ios;android; ``` @@ -32,6 +32,32 @@ For example, to only target UWP, WASM and Android: ``` +### Available targets + +A MultiTarget names a *platform your component runs on*, not a TFM. The TFMs and the deployable +head it maps to depend on which WinUI version is selected, since Uno 5.x (WinUI 2, `Uno.UI`) and +Uno 6.x (WinUI 3, `Uno.WinUI`) ship different heads. + +| MultiTarget | Platform | WinUI 2 (Uno 5.x) | WinUI 3 (Uno 6.x) | +| --- | --- | --- | --- | +| `uwp` | Windows, UWP XAML | `uap10.0.17763`, `net{x}-windows10.0.26100` | n/a | +| `wasdk` | Windows, Windows App SDK | n/a | `net{x}-windows10.0.19041` | +| `wasm` | WebAssembly | `net{x}`, Wasm head | `net{x}`, `net{x}-browserwasm` head | +| `wpf` | Windows desktop, Skia WPF | `net{x}`, Skia WPF head | n/a — use `win32` | +| `win32` | Windows desktop, Skia Win32 | n/a — use `wpf` | `net{x}`, `net{x}-desktop` head | +| `linux` | Linux desktop | `net{x}`, Skia GTK head | `net{x}`, `net{x}-desktop` head | +| `macos` | macOS | `net{x}-maccatalyst` | `net{x}`, `net{x}-desktop` head | +| `ios` | iOS | `net{x}-ios` | `net{x}-ios` | +| `android` | Android | `net{x}-android` | `net{x}-android` | +| `netstandard` | .NET Standard, no WinUI | `netstandard2.0` | `netstandard2.0` | + +`win32`, `linux` and `macos` all deploy from the same build output on WinUI 3: Uno 6 replaced the +per-OS Skia heads with a single cross-platform `net{x}-desktop` head that picks its backend at +runtime. They stay separate MultiTargets so a component can declare — and solution generation and +CI can request — only the desktop platforms that are actually supported. + +Mac Catalyst is WinUI 2 only. Uno 7 removes support for it, and on WinUI 3 macOS is reached through +the Skia desktop head instead, so `net{x}-maccatalyst` is not part of the WinUI 3 package surface. ## ProjectReference Generation diff --git a/MultiTarget/Test-Component-Support.ps1 b/MultiTarget/Test-Component-Support.ps1 index 3844dedd..1ef8f054 100644 --- a/MultiTarget/Test-Component-Support.ps1 +++ b/MultiTarget/Test-Component-Support.ps1 @@ -32,11 +32,11 @@ Date: 6/6/2025 #> Param ( - [ValidateSet('wasm', 'uwp', 'wasdk', 'wpf', 'linuxgtk', 'macos', 'ios', 'android', 'netstandard')] + [ValidateSet('wasm', 'uwp', 'wasdk', 'wpf', 'win32', 'linux', 'macos', 'ios', 'android', 'netstandard')] [Alias("smt")] [string[]]$SupportedMultiTargets, - [ValidateSet('all', 'wasm', 'uwp', 'wasdk', 'wpf', 'linuxgtk', 'macos', 'ios', 'android', 'netstandard')] + [ValidateSet('all', 'wasm', 'uwp', 'wasdk', 'wpf', 'win32', 'linux', 'macos', 'ios', 'android', 'netstandard')] [Alias("rmt")] [Parameter(Mandatory=$true)] [string[]]$RequestedMultiTargets, @@ -51,17 +51,20 @@ Param ( ) if ($RequestedMultiTargets -eq 'all') { - $RequestedMultiTargets = @('wasm', 'uwp', 'wasdk', 'wpf', 'linuxgtk', 'macos', 'ios', 'android', 'netstandard') + $RequestedMultiTargets = @('wasm', 'uwp', 'wasdk', 'wpf', 'win32', 'linux', 'macos', 'ios', 'android', 'netstandard') } # List of WinUI-0 (non-WinUI) compatible multitargets $WinUI0MultiTargets = @('netstandard') -# List of WinUI-2 compatible multitargets -$WinUI2MultiTargets = @('uwp', 'wasm', 'wpf', 'linuxgtk', 'macos', 'ios', 'android') +# List of WinUI-2 (Uno 5.x / Uno.UI) compatible multitargets. +# 'linux' is the Skia GTK head there; 'wpf' is the Skia WPF head. +$WinUI2MultiTargets = @('uwp', 'wasm', 'wpf', 'linux', 'macos', 'ios', 'android') -# List of WinUI-3 compatible multitargets -$WinUI3MultiTargets = @('wasdk', 'wasm', 'wpf', 'linuxgtk', 'macos', 'ios', 'android') +# List of WinUI-3 (Uno 6.x / Uno.WinUI) compatible multitargets. +# win32, linux and macos are the desktop surface, all served by the single net9.0-desktop head that +# replaced Uno 5's per-OS Skia heads. 'wpf' stays WinUI 2 only, since Uno 6 dropped Uno.UI.Skia.Wpf. +$WinUI3MultiTargets = @('wasdk', 'wasm', 'win32', 'linux', 'macos', 'ios', 'android') # If WinUI 0 is requested, the component must not support WinUI 2 or WinUI 3 to be built. # If WinUI 2 or 3 is requested, the component must have a target that supports WinUI 2 or 3 to be built. diff --git a/MultiTarget/UseTargetFrameworks.ps1 b/MultiTarget/UseTargetFrameworks.ps1 index 1fa00ae4..927df5dc 100644 --- a/MultiTarget/UseTargetFrameworks.ps1 +++ b/MultiTarget/UseTargetFrameworks.ps1 @@ -22,19 +22,19 @@ #> Param ( [Parameter(HelpMessage = "The target frameworks to enable.")] - [ValidateSet('all', 'wasm', 'uwp', 'wasdk', 'wpf', 'linuxgtk', 'macos', 'ios', 'android', 'netstandard')] + [ValidateSet('all', 'wasm', 'uwp', 'wasdk', 'wpf', 'win32', 'linux', 'macos', 'ios', 'android', 'netstandard')] [Alias("mt")] [string[]]$MultiTargets = @('uwp', 'wasdk', 'wasm'), # default settings [Parameter(HelpMessage = "The target frameworks to disable.")] - [ValidateSet('wasm', 'uwp', 'wasdk', 'wpf', 'linuxgtk', 'macos', 'ios', 'android', 'netstandard')] + [ValidateSet('wasm', 'uwp', 'wasdk', 'wpf', 'win32', 'linux', 'macos', 'ios', 'android', 'netstandard')] [string[]]$ExcludeMultiTargets = @() # default settings ) $fileContents = Get-Content -Path $PSScriptRoot/EnabledMultiTargets.props $newFileContents = $fileContents; -$AllMultiTargets = @('wasm', 'uwp', 'wasdk', 'wpf', 'linuxgtk', 'macos', 'ios', 'android', 'netstandard') +$AllMultiTargets = @('wasm', 'uwp', 'wasdk', 'wpf', 'win32', 'linux', 'macos', 'ios', 'android', 'netstandard') # Exclude as needed foreach ($excluded in $ExcludeMultiTargets) { diff --git a/MultiTarget/UseUnoWinUI.ps1 b/MultiTarget/UseUnoWinUI.ps1 index 7b419a43..e260c3dd 100644 --- a/MultiTarget/UseUnoWinUI.ps1 +++ b/MultiTarget/UseUnoWinUI.ps1 @@ -11,14 +11,12 @@ function ApplyWinUISwap([string] $filePath) { $fileContents = $fileContents -replace '2', '3'; $fileContents = $fileContents -replace 'Uwp', 'WinUI'; $fileContents = $fileContents -replace 'Uwp', 'WinUI'; - $fileContents = $fileContents -replace 'Uno.UI', 'Uno.WinUI'; } if ($winUIMajorVersion -eq "2") { $fileContents = $fileContents -replace '3', '2'; $fileContents = $fileContents -replace 'WinUI', 'Uwp'; $fileContents = $fileContents -replace 'WinUI', 'Uwp'; - $fileContents = $fileContents -replace 'Uno.WinUI', 'Uno.UI'; } Set-Content -Force -Path $filePath -Value $fileContents; @@ -27,7 +25,6 @@ function ApplyWinUISwap([string] $filePath) { Write-Output "Switching Uno to WinUI $winUIMajorVersion"; -ApplyWinUISwap $PSScriptRoot/../ProjectHeads/App.Head.Uno.props ApplyWinUISwap $PSScriptRoot/PackageReferences/Uno.props ApplyWinUISwap $PSScriptRoot/WinUI.TargetVersion.props diff --git a/ProjectHeads/AllComponents/Uno/Assets/Icons/icon.svg b/ProjectHeads/AllComponents/Uno/Assets/Icons/icon.svg new file mode 100644 index 00000000..a15af53a --- /dev/null +++ b/ProjectHeads/AllComponents/Uno/Assets/Icons/icon.svg @@ -0,0 +1,42 @@ + + + + + + diff --git a/ProjectHeads/AllComponents/Uno/Assets/Icons/icon_foreground.svg b/ProjectHeads/AllComponents/Uno/Assets/Icons/icon_foreground.svg new file mode 100644 index 00000000..8ffc41ae --- /dev/null +++ b/ProjectHeads/AllComponents/Uno/Assets/Icons/icon_foreground.svg @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ProjectHeads/AllComponents/Uno/Assets/SharedAssets.md b/ProjectHeads/AllComponents/Uno/Assets/SharedAssets.md new file mode 100644 index 00000000..b1cc4e76 --- /dev/null +++ b/ProjectHeads/AllComponents/Uno/Assets/SharedAssets.md @@ -0,0 +1,32 @@ +# Shared Assets + +See documentation about assets here: https://github.com/unoplatform/uno/blob/master/doc/articles/features/working-with-assets.md + +## Here is a cheat sheet + +1. Add the image file to the `Assets` directory of a shared project. +2. Set the build action to `Content`. +3. (Recommended) Provide an asset for various scales/dpi + +### Examples + +```text +\Assets\Images\logo.scale-100.png +\Assets\Images\logo.scale-200.png +\Assets\Images\logo.scale-400.png + +\Assets\Images\scale-100\logo.png +\Assets\Images\scale-200\logo.png +\Assets\Images\scale-400\logo.png +``` + +### Table of scales + +| Scale | WinUI | iOS | Android | +|-------|:-----------:|:---------------:|:-------:| +| `100` | scale-100 | @1x | mdpi | +| `125` | scale-125 | N/A | N/A | +| `150` | scale-150 | N/A | hdpi | +| `200` | scale-200 | @2x | xhdpi | +| `300` | scale-300 | @3x | xxhdpi | +| `400` | scale-400 | N/A | xxxhdpi | diff --git a/ProjectHeads/AllComponents/Uno/Assets/Splash/splash_screen.svg b/ProjectHeads/AllComponents/Uno/Assets/Splash/splash_screen.svg new file mode 100644 index 00000000..8ffc41ae --- /dev/null +++ b/ProjectHeads/AllComponents/Uno/Assets/Splash/splash_screen.svg @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ProjectHeads/AllComponents/Uno/CommunityToolkit.App.Uno.csproj b/ProjectHeads/AllComponents/Uno/CommunityToolkit.App.Uno.csproj new file mode 100644 index 00000000..8e8965b2 --- /dev/null +++ b/ProjectHeads/AllComponents/Uno/CommunityToolkit.App.Uno.csproj @@ -0,0 +1,127 @@ + + + + + + + + true + + + $(TargetFrameworks)net9.0-desktop; + $(TargetFrameworks)net9.0-browserwasm; + $(TargetFrameworks)net9.0-android; + $(TargetFrameworks)net9.0-ios; + + + + Exe + + + SkiaRenderer; + + + false + true + CommunityToolkit.App.Uno + + + true + true + true + true + true + true + true + true + true + true + 3 + true + WinUI + + $(DefineConstants);WINUI3 + + + + + + + + + + + + net9.0 + net9.0-android + net9.0-ios + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SourceAssets/%(RecursiveDir)%(FileName)%(Extension) + + + SourceAssets/%(RecursiveDir)%(FileName)%(Extension) + + + SourceAssets/%(RecursiveDir)%(FileName)%(Extension).dat + + + + + + $(NoWarn);IL2026;Uno0006 + + + + diff --git a/ProjectHeads/AllComponents/Uno/Platforms/Android/AndroidManifest.xml b/ProjectHeads/AllComponents/Uno/Platforms/Android/AndroidManifest.xml new file mode 100644 index 00000000..24ee4aea --- /dev/null +++ b/ProjectHeads/AllComponents/Uno/Platforms/Android/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/ProjectHeads/AllComponents/Uno/Platforms/Android/Assets/AboutAssets.txt b/ProjectHeads/AllComponents/Uno/Platforms/Android/Assets/AboutAssets.txt new file mode 100644 index 00000000..6b8fc66e --- /dev/null +++ b/ProjectHeads/AllComponents/Uno/Platforms/Android/Assets/AboutAssets.txt @@ -0,0 +1,5 @@ +Any raw assets you want to be deployed with your application can be placed in this +directory (and child directories). Deployment of the asset to your application is done +using the IDE or by using the following MSBuild property in your .csproj: + + diff --git a/ProjectHeads/AllComponents/Uno/Platforms/Android/Main.Android.cs b/ProjectHeads/AllComponents/Uno/Platforms/Android/Main.Android.cs new file mode 100644 index 00000000..31d89a6f --- /dev/null +++ b/ProjectHeads/AllComponents/Uno/Platforms/Android/Main.Android.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Android.App; +using Android.Runtime; + +namespace CommunityToolkit.App.Uno; + +[global::Android.App.ApplicationAttribute( + Label = "@string/ApplicationName", + Icon = "@mipmap/icon", + LargeHeap = true, + HardwareAccelerated = true, + Theme = "@style/AppTheme" +)] +public class Application : Microsoft.UI.Xaml.NativeApplication +{ + public Application(IntPtr javaReference, JniHandleOwnership transfer) + : base(() => new CommunityToolkit.App.Shared.App(), javaReference, transfer) + { + } +} \ No newline at end of file diff --git a/ProjectHeads/AllComponents/Uno/Platforms/Android/MainActivity.Android.cs b/ProjectHeads/AllComponents/Uno/Platforms/Android/MainActivity.Android.cs new file mode 100644 index 00000000..ad38d011 --- /dev/null +++ b/ProjectHeads/AllComponents/Uno/Platforms/Android/MainActivity.Android.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Android.App; +using Android.Content.PM; +using Android.OS; +using Android.Views; +using Android.Widget; + +namespace CommunityToolkit.App.Uno; + +[Activity( + MainLauncher = true, + ConfigurationChanges = global::Uno.UI.ActivityHelper.AllConfigChanges, + WindowSoftInputMode = SoftInput.AdjustNothing | SoftInput.StateHidden +)] +public class MainActivity : Microsoft.UI.Xaml.ApplicationActivity +{ +} \ No newline at end of file diff --git a/ProjectHeads/AllComponents/Uno/Platforms/Android/Resources/values/Strings.xml b/ProjectHeads/AllComponents/Uno/Platforms/Android/Resources/values/Strings.xml new file mode 100644 index 00000000..ee06a129 --- /dev/null +++ b/ProjectHeads/AllComponents/Uno/Platforms/Android/Resources/values/Strings.xml @@ -0,0 +1,4 @@ + + + CommunityToolkit + diff --git a/ProjectHeads/AllComponents/Uno/Platforms/Android/Resources/values/Styles.xml b/ProjectHeads/AllComponents/Uno/Platforms/Android/Resources/values/Styles.xml new file mode 100644 index 00000000..2c3bf24a --- /dev/null +++ b/ProjectHeads/AllComponents/Uno/Platforms/Android/Resources/values/Styles.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/ProjectHeads/AllComponents/Uno/Platforms/Android/environment.conf b/ProjectHeads/AllComponents/Uno/Platforms/Android/environment.conf new file mode 100644 index 00000000..9d9709a9 --- /dev/null +++ b/ProjectHeads/AllComponents/Uno/Platforms/Android/environment.conf @@ -0,0 +1,2 @@ +## Mono GC tuning params +MONO_GC_PARAMS=soft-heap-limit=512m,nursery-size=64m,evacuation-threshold=66,major=marksweep,concurrent-sweep diff --git a/ProjectHeads/AllComponents/Uno/Platforms/Desktop/Program.cs b/ProjectHeads/AllComponents/Uno/Platforms/Desktop/Program.cs new file mode 100644 index 00000000..2f5400b3 --- /dev/null +++ b/ProjectHeads/AllComponents/Uno/Platforms/Desktop/Program.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Uno.UI.Hosting; +using CommunityToolkit.App.Shared; + +internal class Program +{ + [STAThread] + public static void Main(string[] args) + { + var host = UnoPlatformHostBuilder.Create() + .App(() => new CommunityToolkit.App.Shared.App()) + .UseX11() + .UseLinuxFrameBuffer() + .UseMacOS() + .UseWin32() + .Build(); + + host.Run(); + } +} \ No newline at end of file diff --git a/ProjectHeads/AllComponents/Uno/Platforms/WebAssembly/LinkerConfig.xml b/ProjectHeads/AllComponents/Uno/Platforms/WebAssembly/LinkerConfig.xml new file mode 100644 index 00000000..cc3e5cdd --- /dev/null +++ b/ProjectHeads/AllComponents/Uno/Platforms/WebAssembly/LinkerConfig.xml @@ -0,0 +1,3 @@ + + + diff --git a/ProjectHeads/AllComponents/Uno/Platforms/WebAssembly/Program.cs b/ProjectHeads/AllComponents/Uno/Platforms/WebAssembly/Program.cs new file mode 100644 index 00000000..f07914ec --- /dev/null +++ b/ProjectHeads/AllComponents/Uno/Platforms/WebAssembly/Program.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Uno.UI.Hosting; +using CommunityToolkit.App.Shared; + +public class Program +{ + public static async Task Main(string[] args) + { + var host = UnoPlatformHostBuilder.Create() + .App(() => new CommunityToolkit.App.Shared.App()) + .UseWebAssembly() + .Build(); + + await host.RunAsync(); + } +} \ No newline at end of file diff --git a/ProjectHeads/AllComponents/Uno/Platforms/WebAssembly/WasmScripts/AppManifest.js b/ProjectHeads/AllComponents/Uno/Platforms/WebAssembly/WasmScripts/AppManifest.js new file mode 100644 index 00000000..8018667f --- /dev/null +++ b/ProjectHeads/AllComponents/Uno/Platforms/WebAssembly/WasmScripts/AppManifest.js @@ -0,0 +1,3 @@ +var UnoAppManifest = { + displayName: "CommunityToolkit" +} diff --git a/ProjectHeads/AllComponents/Uno/Platforms/WebAssembly/manifest.webmanifest b/ProjectHeads/AllComponents/Uno/Platforms/WebAssembly/manifest.webmanifest new file mode 100644 index 00000000..8d6e55be --- /dev/null +++ b/ProjectHeads/AllComponents/Uno/Platforms/WebAssembly/manifest.webmanifest @@ -0,0 +1,10 @@ +{ + "name": "CommunityToolkit", + "description": "CommunityToolkit", + "short_name": "CommunityToolkit", + "start_url": "/index.html", + "display": "standalone", + "background_color": "#FFFFFF", + "theme_color": "#FFFFFF", + "scope": "/" +} diff --git a/ProjectHeads/AllComponents/Uno/Platforms/iOS/Entitlements.plist b/ProjectHeads/AllComponents/Uno/Platforms/iOS/Entitlements.plist new file mode 100644 index 00000000..6631ffa6 --- /dev/null +++ b/ProjectHeads/AllComponents/Uno/Platforms/iOS/Entitlements.plist @@ -0,0 +1,6 @@ + + + + + + diff --git a/ProjectHeads/AllComponents/Uno/Platforms/iOS/Info.plist b/ProjectHeads/AllComponents/Uno/Platforms/iOS/Info.plist new file mode 100644 index 00000000..8f81dd88 --- /dev/null +++ b/ProjectHeads/AllComponents/Uno/Platforms/iOS/Info.plist @@ -0,0 +1,43 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + + CFBundleIdentifier + com.companyname.CommunityToolkit + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + CommunityToolkit + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + MinimumOSVersion + 16.0 + UIDeviceFamily + + 1 + 2 + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/ProjectHeads/AllComponents/Uno/Platforms/iOS/Main.iOS.cs b/ProjectHeads/AllComponents/Uno/Platforms/iOS/Main.iOS.cs new file mode 100644 index 00000000..b163d7dd --- /dev/null +++ b/ProjectHeads/AllComponents/Uno/Platforms/iOS/Main.iOS.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Uno.UI.Hosting; +using CommunityToolkit.App.Shared; + +namespace CommunityToolkit.App.Uno; + +public class EntryPoint +{ + public static void Main(string[] args) + { + var host = UnoPlatformHostBuilder.Create() + .App(() => new CommunityToolkit.App.Shared.App()) + .UseAppleUIKit() + .Build(); + + host.Run(); + } +} \ No newline at end of file diff --git a/ProjectHeads/AllComponents/Uno/Platforms/iOS/Media.xcassets/LaunchImages.launchimage/Contents.json b/ProjectHeads/AllComponents/Uno/Platforms/iOS/Media.xcassets/LaunchImages.launchimage/Contents.json new file mode 100644 index 00000000..4103a707 --- /dev/null +++ b/ProjectHeads/AllComponents/Uno/Platforms/iOS/Media.xcassets/LaunchImages.launchimage/Contents.json @@ -0,0 +1,20 @@ +{ + "images": [ + { + "idiom": "universal", + "scale": "1x" + }, + { + "idiom": "universal", + "scale": "2x" + }, + { + "idiom": "universal", + "scale": "3x" + } + ], + "info": { + "version": 1, + "author": "xcode" + } +} diff --git a/ProjectHeads/AllComponents/Uno/Platforms/iOS/PrivacyInfo.xcprivacy b/ProjectHeads/AllComponents/Uno/Platforms/iOS/PrivacyInfo.xcprivacy new file mode 100644 index 00000000..1d00d2f5 --- /dev/null +++ b/ProjectHeads/AllComponents/Uno/Platforms/iOS/PrivacyInfo.xcprivacy @@ -0,0 +1,31 @@ + + + + + NSPrivacyTracking + + NSPrivacyTrackingDomains + + NSPrivacyCollectedDataTypes + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategorySystemBootTime + NSPrivacyAccessedAPITypeReasons + + 35F9.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + C617.1 + + + + + diff --git a/ProjectHeads/AllComponents/Wasm/Program.cs b/ProjectHeads/AllComponents/Wasm/Program.cs index 43e990c1..a7aae2ee 100644 --- a/ProjectHeads/AllComponents/Wasm/Program.cs +++ b/ProjectHeads/AllComponents/Wasm/Program.cs @@ -5,7 +5,7 @@ using CommunityToolkit.App.Shared; #if WINAPPSDK -using Microsoft.UI.Xaml; +using Uno.UI.Hosting; #else using Windows.UI.Xaml; #endif @@ -14,6 +14,17 @@ namespace CommunityToolkit.App.Wasm; public class Program { +#if WINAPPSDK + static async Task Main(string[] args) + { + var host = UnoPlatformHostBuilder.Create() + .App(() => new CommunityToolkit.App.Shared.App()) + .UseWebAssembly() + .Build(); + + await host.RunAsync(); + } +#else private static CommunityToolkit.App.Shared.App? _app; static int Main(string[] args) @@ -22,4 +33,5 @@ static int Main(string[] args) return 0; } +#endif } diff --git a/ProjectHeads/App.Head.Android.props b/ProjectHeads/App.Head.Android.props new file mode 100644 index 00000000..2a864dec --- /dev/null +++ b/ProjectHeads/App.Head.Android.props @@ -0,0 +1,7 @@ + + + + + false + + diff --git a/ProjectHeads/App.Head.Uno.UI.Dependencies.props b/ProjectHeads/App.Head.Uno.UI.Dependencies.props index 361463b9..49fe326c 100644 --- a/ProjectHeads/App.Head.Uno.UI.Dependencies.props +++ b/ProjectHeads/App.Head.Uno.UI.Dependencies.props @@ -6,5 +6,3 @@ - - diff --git a/ProjectHeads/App.Head.Uno.WinUI.Dependencies.props b/ProjectHeads/App.Head.Uno.WinUI.Dependencies.props index d18fbfb5..77c9bc88 100644 --- a/ProjectHeads/App.Head.Uno.WinUI.Dependencies.props +++ b/ProjectHeads/App.Head.Uno.WinUI.Dependencies.props @@ -4,6 +4,6 @@ - + diff --git a/ProjectHeads/App.Head.Uno.props b/ProjectHeads/App.Head.Uno.props index 865ea717..6f5cafab 100644 --- a/ProjectHeads/App.Head.Uno.props +++ b/ProjectHeads/App.Head.Uno.props @@ -1,6 +1,7 @@ - WinUI + WinUI + Uwp @@ -25,8 +26,9 @@ - + + diff --git a/ProjectHeads/App.Head.Wasm.props b/ProjectHeads/App.Head.Wasm.props index ed7c9705..32487037 100644 --- a/ProjectHeads/App.Head.Wasm.props +++ b/ProjectHeads/App.Head.Wasm.props @@ -2,6 +2,7 @@ Exe $(WasmHeadTargetFramework.Split(';')[0]) + false + + + + + true + + + $(TargetFrameworks)net9.0-desktop; + $(TargetFrameworks)net9.0-browserwasm; + $(TargetFrameworks)net9.0-android; + $(TargetFrameworks)net9.0-ios; + + + + Exe + ProjectTemplate.Samples + ProjectTemplate.Samples.Uno + + + SkiaRenderer; + + + false + true + + + true + true + true + true + true + true + true + true + true + true + 3 + true + WinUI + + $(DefineConstants);WINUI3 + + + + + + + + net9.0 + net9.0-android + net9.0-ios + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SourceAssets/%(RecursiveDir)%(FileName)%(Extension).dat + + + + + + + $(NoWarn);IL2026;Uno0006 + + + + diff --git a/ProjectHeads/SingleComponent/Wasm/Program.cs b/ProjectHeads/SingleComponent/Wasm/Program.cs index 9522ede6..91bd1b52 100644 --- a/ProjectHeads/SingleComponent/Wasm/Program.cs +++ b/ProjectHeads/SingleComponent/Wasm/Program.cs @@ -4,10 +4,25 @@ using CommunityToolkit.App.Shared; +#if WINAPPSDK +using Uno.UI.Hosting; +#endif + namespace ProjectTemplateExperiment.Samples.Wasm; public class Program { +#if WINAPPSDK + static async Task Main(string[] args) + { + var host = UnoPlatformHostBuilder.Create() + .App(() => new App()) + .UseWebAssembly() + .Build(); + + await host.RunAsync(); + } +#else private static App? _app; static int Main(string[] args) @@ -16,4 +31,5 @@ static int Main(string[] args) return 0; } +#endif } diff --git a/ProjectHeads/Targets/AddMultiTargetCompatibleSampleDocs.targets b/ProjectHeads/Targets/AddMultiTargetCompatibleSampleDocs.targets index 02d0ea88..bda9cdc7 100644 --- a/ProjectHeads/Targets/AddMultiTargetCompatibleSampleDocs.targets +++ b/ProjectHeads/Targets/AddMultiTargetCompatibleSampleDocs.targets @@ -8,10 +8,22 @@ uwp wasm wasdk + wpf + win32 + linux macos ios android netstandard + + + + $(MultiTargetIdentifier)win32; + $(MultiTargetIdentifier)linux; + $(MultiTargetIdentifier)macos; diff --git a/ProjectHeads/Tasks/CheckMultiTarget.props b/ProjectHeads/Tasks/CheckMultiTarget.props index 812b68fe..2b9c581b 100644 --- a/ProjectHeads/Tasks/CheckMultiTarget.props +++ b/ProjectHeads/Tasks/CheckMultiTarget.props @@ -120,7 +120,22 @@ multiTargetIdentifier = ExtractMultiTargetIdentifier(multiTargetPropsContent); } - IsSupported = multiTargetIdentifier.Split(';').Contains(MultiTargetIdentifier); + // MultiTargetIdentifier may be a ';' separated list: the unified Uno desktop head covers + // several MultiTargets with a single build, so a doc matching any one of them is supported. + string[] componentMultiTargets = multiTargetIdentifier.Split(';'); + IsSupported = false; + + foreach (string headMultiTarget in MultiTargetIdentifier.Split(';')) + { + string trimmedHeadMultiTarget = headMultiTarget.Trim(); + + if (trimmedHeadMultiTarget.Length > 0 && componentMultiTargets.Contains(trimmedHeadMultiTarget)) + { + IsSupported = true; + break; + } + } + FilePath.SetMetadata("IsSupported", IsSupported.ToString()); ]]> diff --git a/ProjectTemplate/src/MultiTarget.props b/ProjectTemplate/src/MultiTarget.props index b11c1942..7d1e11c3 100644 --- a/ProjectTemplate/src/MultiTarget.props +++ b/ProjectTemplate/src/MultiTarget.props @@ -4,6 +4,6 @@ MultiTarget is a custom property that indicates which target a project is designed to be built for / run on. Used to create project references, generate solution files, enable/disable TargetFrameworks, and build nuget packages. --> - uwp;wasdk;wpf;wasm;linuxgtk;macos;ios;android; + uwp;wasdk;wpf;win32;wasm;linux;macos;ios;android; \ No newline at end of file diff --git a/global.json b/global.json index 5d4b9f49..7f21440e 100644 --- a/global.json +++ b/global.json @@ -3,8 +3,9 @@ "version": "10.0.401", "rollForward": "latestFeature" }, - "msbuild-sdks": + "msbuild-sdks": { - "MSBuild.Sdk.Extras":"3.0.44" + "MSBuild.Sdk.Extras":"3.0.44", + "Uno.Sdk": "7.0.0-dev.51" } } From 13dced76ea29b9a5c181ec1ab4645d0b3454a224 Mon Sep 17 00:00:00 2001 From: Martin Zikmund Date: Fri, 9 Oct 2026 11:01:05 +0200 Subject: [PATCH 2/9] Target Uno Platform 7.0 for WinUI 3 Uno.Sdk 7.0.0-dev.51 / Uno.WinUI 7.0.0-dev.1656, which require .NET 10. WinUI 2 (Uno.UI 5.x) stays on .NET 9. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01E8bkzyS24p8CYDWLUPsaPC --- .github/workflows/build.yml | 4 +-- Build-Toolkit-Gallery.ps1 | 8 ++--- MultiTarget/AvailableTargetFrameworks.props | 26 ++++++++------ MultiTarget/DefinedConstants.props | 3 +- MultiTarget/EnabledTargetFrameworks.props | 6 ++-- MultiTarget/MultiTargetIdentifiers.props | 2 +- MultiTarget/PackageReferences/Uno.props | 6 ++-- MultiTarget/ReadMe.md | 7 ++-- MultiTarget/Test-Component-Support.ps1 | 4 +-- .../Uno/CommunityToolkit.App.Uno.csproj | 34 +++++++++---------- .../Wasm/CommunityToolkit.App.Wasm.csproj | 3 +- .../App.Head.Uno.WinUI.Dependencies.props | 2 +- .../Uno/ProjectTemplate.Uno.csproj | 30 ++++++++-------- ...AddMultiTargetCompatibleSampleDocs.targets | 2 +- 14 files changed, 72 insertions(+), 65 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bcb52541..0c5f8290 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -300,8 +300,8 @@ jobs: # iOS/maccatalyst workloads aren't installable on Linux (Uno.Sdk # auto-filters those TFMs there). android + wasm-tools are both - # required: even a `-f net9.0-browserwasm` build restores all of the - # head's TargetFrameworks, so the net9.0-android TFM's workload is + # required: even a `-f net10.0-browserwasm` build restores all of the + # head's TargetFrameworks, so the net10.0-android TFM's workload is # validated during restore. # Run from ./tooling so global.json scopes the install to the pinned # SDK feature band; otherwise the workloads land on the runner's newer diff --git a/Build-Toolkit-Gallery.ps1 b/Build-Toolkit-Gallery.ps1 index cf27564c..81a706c3 100644 --- a/Build-Toolkit-Gallery.ps1 +++ b/Build-Toolkit-Gallery.ps1 @@ -208,16 +208,16 @@ function Get-UnoSdkHeadTargetFrameworks { $targetFrameworks = [System.Collections.ArrayList]::new() if (($MultiTargets | Where-Object { @('win32', 'linux', 'macos') -contains $_ }).Count -gt 0) { - [void]$targetFrameworks.Add('net9.0-desktop') + [void]$targetFrameworks.Add('net10.0-desktop') } if ($MultiTargets -contains 'wasm') { - [void]$targetFrameworks.Add('net9.0-browserwasm') + [void]$targetFrameworks.Add('net10.0-browserwasm') } if ($MultiTargets -contains 'android') { - [void]$targetFrameworks.Add('net9.0-android') + [void]$targetFrameworks.Add('net10.0-android') } if ($MultiTargets -contains 'ios') { - [void]$targetFrameworks.Add('net9.0-ios') + [void]$targetFrameworks.Add('net10.0-ios') } return $targetFrameworks diff --git a/MultiTarget/AvailableTargetFrameworks.props b/MultiTarget/AvailableTargetFrameworks.props index fbf0a593..47ddf644 100644 --- a/MultiTarget/AvailableTargetFrameworks.props +++ b/MultiTarget/AvailableTargetFrameworks.props @@ -12,22 +12,26 @@ uap10.0.17763;net8.0-windows10.0.26100.0;net9.0-windows10.0.26100.0; net9.0-windows10.0.26100.0;net8.0-windows10.0.26100.0; - net9.0; - net9.0; - net9.0; + + net10.0 + net9.0 - net9.0-android; - net9.0-maccatalyst; - net9.0-ios; + $(UnoDotNetTargetFramework); + $(UnoDotNetTargetFramework); + $(UnoDotNetTargetFramework); + + $(UnoDotNetTargetFramework)-android; + $(UnoDotNetTargetFramework)-maccatalyst; + $(UnoDotNetTargetFramework)-ios; - net9.0; - net9.0; - net9.0; - net9.0; + $(UnoDotNetTargetFramework); + $(UnoDotNetTargetFramework); + $(UnoDotNetTargetFramework); + $(UnoDotNetTargetFramework); netstandard2.0; - net9.0; + $(UnoDotNetTargetFramework); \ No newline at end of file diff --git a/MultiTarget/DefinedConstants.props b/MultiTarget/DefinedConstants.props index 6152d9e8..d890e153 100644 --- a/MultiTarget/DefinedConstants.props +++ b/MultiTarget/DefinedConstants.props @@ -15,9 +15,10 @@ $(DefineConstants);HAS_UNO_SKIA;__SKIA__;WINDOWS_WPF; $(DefineConstants);HAS_UNO_SKIA;__SKIA__;__GTK__; $(DefineConstants);HAS_UNO_SKIA;__SKIA__; - + $(DefineConstants);HAS_UNO_SKIA;__SKIA__; + $(DefineConstants);NET10_0_OR_GREATER $(DefineConstants);NET9_0_OR_GREATER $(DefineConstants);NET8_0_OR_GREATER $(DefineConstants);NET7_0_OR_GREATER diff --git a/MultiTarget/EnabledTargetFrameworks.props b/MultiTarget/EnabledTargetFrameworks.props index e321af61..ce94724e 100644 --- a/MultiTarget/EnabledTargetFrameworks.props +++ b/MultiTarget/EnabledTargetFrameworks.props @@ -12,15 +12,15 @@ true true $(TargetFrameworks);$(DotnetCommonTargetFramework) $(TargetFrameworks);$(MacOSLibTargetFrameworks) diff --git a/MultiTarget/MultiTargetIdentifiers.props b/MultiTarget/MultiTargetIdentifiers.props index 37fdfca2..60f16dfa 100644 --- a/MultiTarget/MultiTargetIdentifiers.props +++ b/MultiTarget/MultiTargetIdentifiers.props @@ -34,7 +34,7 @@ true true - true true diff --git a/MultiTarget/PackageReferences/Uno.props b/MultiTarget/PackageReferences/Uno.props index d303c601..2b086f3f 100644 --- a/MultiTarget/PackageReferences/Uno.props +++ b/MultiTarget/PackageReferences/Uno.props @@ -2,8 +2,8 @@ 5.5.87 - - 6.4.229 + + 7.0.0-dev.1656 $(UnoUIPackageVersion) $(UnoWinUIPackageVersion) @@ -40,7 +40,7 @@ - + diff --git a/MultiTarget/ReadMe.md b/MultiTarget/ReadMe.md index d6ad1704..7c304f33 100644 --- a/MultiTarget/ReadMe.md +++ b/MultiTarget/ReadMe.md @@ -36,9 +36,10 @@ For example, to only target UWP, WASM and Android: A MultiTarget names a *platform your component runs on*, not a TFM. The TFMs and the deployable head it maps to depend on which WinUI version is selected, since Uno 5.x (WinUI 2, `Uno.UI`) and -Uno 6.x (WinUI 3, `Uno.WinUI`) ship different heads. +Uno 7.x (WinUI 3, `Uno.WinUI`) ship different heads and .NET versions: `net{x}` is `net9.0` on WinUI 2 and +`net10.0` on WinUI 3, since Uno 7 requires .NET 10. -| MultiTarget | Platform | WinUI 2 (Uno 5.x) | WinUI 3 (Uno 6.x) | +| MultiTarget | Platform | WinUI 2 (Uno 5.x) | WinUI 3 (Uno 7.x) | | --- | --- | --- | --- | | `uwp` | Windows, UWP XAML | `uap10.0.17763`, `net{x}-windows10.0.26100` | n/a | | `wasdk` | Windows, Windows App SDK | n/a | `net{x}-windows10.0.19041` | @@ -51,7 +52,7 @@ Uno 6.x (WinUI 3, `Uno.WinUI`) ship different heads. | `android` | Android | `net{x}-android` | `net{x}-android` | | `netstandard` | .NET Standard, no WinUI | `netstandard2.0` | `netstandard2.0` | -`win32`, `linux` and `macos` all deploy from the same build output on WinUI 3: Uno 6 replaced the +`win32`, `linux` and `macos` all deploy from the same build output on WinUI 3: Uno 6+ replaced the per-OS Skia heads with a single cross-platform `net{x}-desktop` head that picks its backend at runtime. They stay separate MultiTargets so a component can declare — and solution generation and CI can request — only the desktop platforms that are actually supported. diff --git a/MultiTarget/Test-Component-Support.ps1 b/MultiTarget/Test-Component-Support.ps1 index 1ef8f054..a78c756f 100644 --- a/MultiTarget/Test-Component-Support.ps1 +++ b/MultiTarget/Test-Component-Support.ps1 @@ -61,8 +61,8 @@ $WinUI0MultiTargets = @('netstandard') # 'linux' is the Skia GTK head there; 'wpf' is the Skia WPF head. $WinUI2MultiTargets = @('uwp', 'wasm', 'wpf', 'linux', 'macos', 'ios', 'android') -# List of WinUI-3 (Uno 6.x / Uno.WinUI) compatible multitargets. -# win32, linux and macos are the desktop surface, all served by the single net9.0-desktop head that +# List of WinUI-3 (Uno 7.x / Uno.WinUI) compatible multitargets. +# win32, linux and macos are the desktop surface, all served by the single net10.0-desktop head that # replaced Uno 5's per-OS Skia heads. 'wpf' stays WinUI 2 only, since Uno 6 dropped Uno.UI.Skia.Wpf. $WinUI3MultiTargets = @('wasdk', 'wasm', 'win32', 'linux', 'macos', 'ios', 'android') diff --git a/ProjectHeads/AllComponents/Uno/CommunityToolkit.App.Uno.csproj b/ProjectHeads/AllComponents/Uno/CommunityToolkit.App.Uno.csproj index 8e8965b2..f9614f99 100644 --- a/ProjectHeads/AllComponents/Uno/CommunityToolkit.App.Uno.csproj +++ b/ProjectHeads/AllComponents/Uno/CommunityToolkit.App.Uno.csproj @@ -6,16 +6,16 @@ - + true - $(TargetFrameworks)net9.0-desktop; - $(TargetFrameworks)net9.0-browserwasm; - $(TargetFrameworks)net9.0-android; - $(TargetFrameworks)net9.0-ios; + $(TargetFrameworks)net10.0-desktop; + $(TargetFrameworks)net10.0-browserwasm; + $(TargetFrameworks)net10.0-android; + $(TargetFrameworks)net10.0-ios; @@ -38,13 +38,13 @@ (Uno.Sdk defines IsDesktop identically; set here too so the head stays self-describing.) --> true true - true - true - true - true - true - true - true + true + true + true + true + true + true + true true 3 true @@ -60,12 +60,12 @@ - - net9.0 - net9.0-android - net9.0-ios + net10.0 + net10.0-android + net10.0-ios diff --git a/ProjectHeads/AllComponents/Wasm/CommunityToolkit.App.Wasm.csproj b/ProjectHeads/AllComponents/Wasm/CommunityToolkit.App.Wasm.csproj index ad0e0d70..3510eeed 100644 --- a/ProjectHeads/AllComponents/Wasm/CommunityToolkit.App.Wasm.csproj +++ b/ProjectHeads/AllComponents/Wasm/CommunityToolkit.App.Wasm.csproj @@ -11,11 +11,12 @@ wasm + + - diff --git a/ProjectHeads/App.Head.Uno.WinUI.Dependencies.props b/ProjectHeads/App.Head.Uno.WinUI.Dependencies.props index 77c9bc88..e10bff86 100644 --- a/ProjectHeads/App.Head.Uno.WinUI.Dependencies.props +++ b/ProjectHeads/App.Head.Uno.WinUI.Dependencies.props @@ -4,6 +4,6 @@ - + diff --git a/ProjectHeads/SingleComponent/Uno/ProjectTemplate.Uno.csproj b/ProjectHeads/SingleComponent/Uno/ProjectTemplate.Uno.csproj index c063d6d7..7c534824 100644 --- a/ProjectHeads/SingleComponent/Uno/ProjectTemplate.Uno.csproj +++ b/ProjectHeads/SingleComponent/Uno/ProjectTemplate.Uno.csproj @@ -4,15 +4,15 @@ + macos all share Uno's single cross-platform Skia desktop TFM. --> true - $(TargetFrameworks)net9.0-desktop; - $(TargetFrameworks)net9.0-browserwasm; - $(TargetFrameworks)net9.0-android; - $(TargetFrameworks)net9.0-ios; + $(TargetFrameworks)net10.0-desktop; + $(TargetFrameworks)net10.0-browserwasm; + $(TargetFrameworks)net10.0-android; + $(TargetFrameworks)net10.0-ios; @@ -35,13 +35,13 @@ ProjectReference gating) that needs to know which one(s), not just "some desktop target". --> true true - true - true - true - true - true - true - true + true + true + true + true + true + true + true true 3 true @@ -55,9 +55,9 @@ - net9.0 - net9.0-android - net9.0-ios + net10.0 + net10.0-android + net10.0-ios diff --git a/ProjectHeads/Targets/AddMultiTargetCompatibleSampleDocs.targets b/ProjectHeads/Targets/AddMultiTargetCompatibleSampleDocs.targets index bda9cdc7..6f16a26e 100644 --- a/ProjectHeads/Targets/AddMultiTargetCompatibleSampleDocs.targets +++ b/ProjectHeads/Targets/AddMultiTargetCompatibleSampleDocs.targets @@ -16,7 +16,7 @@ android netstandard - From 527266c15f9aa6f34b2f4f882b16949653ffb52e Mon Sep 17 00:00:00 2001 From: Martin Zikmund Date: Fri, 9 Oct 2026 11:52:07 +0200 Subject: [PATCH 3/9] Work around MSBuild.Sdk.Extras inferring net10.0 as .NETFramework See https://github.com/novotnyllc/MSBuildSdkExtras/issues/305 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01E8bkzyS24p8CYDWLUPsaPC --- MultiTarget/Library.props | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MultiTarget/Library.props b/MultiTarget/Library.props index 99e59e36..60904221 100644 --- a/MultiTarget/Library.props +++ b/MultiTarget/Library.props @@ -16,6 +16,11 @@ + + + .NETCoreApp + + From c7898b0aa8c2972c129c8b5ed2685fad8eeaf093 Mon Sep 17 00:00:00 2001 From: Martin Zikmund Date: Fri, 9 Oct 2026 11:52:08 +0200 Subject: [PATCH 4/9] Align Uno.Fonts.Fluent with Uno.WinUI 7 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01E8bkzyS24p8CYDWLUPsaPC --- MultiTarget/PackageReferences/Uno.props | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MultiTarget/PackageReferences/Uno.props b/MultiTarget/PackageReferences/Uno.props index 2b086f3f..96be9e3a 100644 --- a/MultiTarget/PackageReferences/Uno.props +++ b/MultiTarget/PackageReferences/Uno.props @@ -23,7 +23,9 @@ - + + + From 25bf5d0381c3671ed1b7e5eb6d789082fb96e204 Mon Sep 17 00:00:00 2001 From: Martin Zikmund Date: Fri, 9 Oct 2026 11:52:08 +0200 Subject: [PATCH 5/9] Disable Hot Design and App MCP on Uno 7 heads The versions pinned by Uno.Sdk 7.0.0-dev still reference pre-7.0 Uno assemblies (unoplatform/uno#24297). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01E8bkzyS24p8CYDWLUPsaPC --- .../AllComponents/Uno/CommunityToolkit.App.Uno.csproj | 4 ++++ ProjectHeads/SingleComponent/Uno/ProjectTemplate.Uno.csproj | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/ProjectHeads/AllComponents/Uno/CommunityToolkit.App.Uno.csproj b/ProjectHeads/AllComponents/Uno/CommunityToolkit.App.Uno.csproj index f9614f99..27259efc 100644 --- a/ProjectHeads/AllComponents/Uno/CommunityToolkit.App.Uno.csproj +++ b/ProjectHeads/AllComponents/Uno/CommunityToolkit.App.Uno.csproj @@ -27,6 +27,10 @@ false true + + + true + true CommunityToolkit.App.Uno + true + true + + + From a1e28b7626defa8e9d4025af9b49922c1e1c03bc Mon Sep 17 00:00:00 2001 From: Martin Zikmund Date: Fri, 9 Oct 2026 12:08:58 +0200 Subject: [PATCH 8/9] Render docs with the Labs MarkdownTextBlock on Uno 7 Uno 7 supports RichTextBlock, so the Uno WinUI 3 heads can use the Labs MarkdownTextBlock instead of showing raw Markdown. Opt-in through LabsMarkdownTextBlockVersion until Labs publishes a build with Uno targets. Links now open through the same handler as on Windows. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01E8bkzyS24p8CYDWLUPsaPC --- .../Renderers/Markdown/MarkdownTextBlock.cs | 53 +++++++++++++++++-- .../ToolkitDocumentationRenderer.xaml.cs | 4 +- .../App.Head.Uno.WinUI.Dependencies.props | 8 +++ 3 files changed, 59 insertions(+), 6 deletions(-) diff --git a/CommunityToolkit.App.Shared/Renderers/Markdown/MarkdownTextBlock.cs b/CommunityToolkit.App.Shared/Renderers/Markdown/MarkdownTextBlock.cs index 76ef5e8d..0a6c0d5a 100644 --- a/CommunityToolkit.App.Shared/Renderers/Markdown/MarkdownTextBlock.cs +++ b/CommunityToolkit.App.Shared/Renderers/Markdown/MarkdownTextBlock.cs @@ -2,7 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if HAS_UNO +#if HAS_LABS_MARKDOWNTEXTBLOCK + using CommunityToolkit.WinUI.Controls; + using ToolkitMTB = CommunityToolkit.WinUI.Controls.MarkdownTextBlock; +#elif HAS_UNO #if WINAPPSDK using ToolkitMTB = Microsoft.UI.Xaml.Controls.TextBlock; #else @@ -27,7 +30,45 @@ namespace CommunityToolkit.App.Shared.Renderers; /// /// Provide an abstraction around the Toolkit MarkdownTextBlock for both UWP and WinUI 3 in the same namespace (until 8.0) as well as a polyfill for WebAssembly/WASM. /// -#if HAS_UNO_WASM && !WINUI3 +#if HAS_LABS_MARKDOWNTEXTBLOCK +public partial class MarkdownTextBlock : ToolkitMTB +{ + public MarkdownTextBlock() + { + IsTextSelectionEnabled = true; + + // Matches the Windows typography from ToolkitDocumentationRenderer.xaml + Config = new MarkdownConfig + { + Themes = new MarkdownThemes + { + H1FontSize = 28, + H1Margin = new Thickness(0, 20, 0, 4), + H2FontSize = 18, + H2Margin = new Thickness(0, 20, 0, 0), + H3FontSize = 16, + H4FontSize = 14, + H5FontSize = 14, + H5FontWeight = FontWeights.Normal, + H6FontSize = 14, + ParagraphMargin = new Thickness(0, 0, 0, 12), + ParagraphLineHeight = 20, + }, + }; + + OnLinkClicked += (_, e) => + { + LinkClicked?.Invoke(this, new LinkClickedEventArgs(e.Uri.ToString())); + e.Handled = true; + }; + } + + // RichTextBlock always wraps, kept so the shared XAML can set it + public TextWrapping TextWrapping { get; set; } + + public event EventHandler? LinkClicked; +} +#elif HAS_UNO_WASM && !WINUI3 [HtmlElement("div")] public partial class MarkdownTextBlock : TextBlock { @@ -108,6 +149,10 @@ public MarkdownTextBlock() #endif #if HAS_UNO -//// Polyfill dummy for event callback -public class LinkClickedEventArgs : EventArgs { } +public class LinkClickedEventArgs : EventArgs +{ + public LinkClickedEventArgs(string link) => Link = link; + + public string Link { get; } +} #endif diff --git a/CommunityToolkit.App.Shared/Renderers/ToolkitDocumentationRenderer.xaml.cs b/CommunityToolkit.App.Shared/Renderers/ToolkitDocumentationRenderer.xaml.cs index ddbcd721..3c30e82c 100644 --- a/CommunityToolkit.App.Shared/Renderers/ToolkitDocumentationRenderer.xaml.cs +++ b/CommunityToolkit.App.Shared/Renderers/ToolkitDocumentationRenderer.xaml.cs @@ -188,13 +188,13 @@ private static async Task GetDocumentationFileContents(ToolkitFrontMatte } } -#if HAS_UNO +#if HAS_UNO && !HAS_LABS_MARKDOWNTEXTBLOCK private void MarkdownTextBlock_LinkClicked(object sender, LinkClickedEventArgs e) { // No-op - WASM handles via browser 'a' tag, Windows has handler below. // TODO: For other platforms } -#elif !HAS_UNO +#else private async void MarkdownTextBlock_LinkClicked(object sender, LinkClickedEventArgs e) { if (!Uri.IsWellFormedUriString(e.Link, UriKind.Absolute)) diff --git a/ProjectHeads/App.Head.Uno.WinUI.Dependencies.props b/ProjectHeads/App.Head.Uno.WinUI.Dependencies.props index c7a00104..172299f4 100644 --- a/ProjectHeads/App.Head.Uno.WinUI.Dependencies.props +++ b/ProjectHeads/App.Head.Uno.WinUI.Dependencies.props @@ -8,4 +8,12 @@ + + + + $(DefineConstants);HAS_LABS_MARKDOWNTEXTBLOCK + + + + From 843a88ab27f6240dc1f540e981602955122cf3b0 Mon Sep 17 00:00:00 2001 From: Martin Zikmund Date: Fri, 9 Oct 2026 12:27:41 +0200 Subject: [PATCH 9/9] Fix the Labs MarkdownTextBlock wrapper build and inline code size FontWeights lives in Microsoft.UI.Text on WinUI 3, and the Labs default inline code size (10) is too small next to 14px body text. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01E8bkzyS24p8CYDWLUPsaPC --- .../Renderers/Markdown/MarkdownTextBlock.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CommunityToolkit.App.Shared/Renderers/Markdown/MarkdownTextBlock.cs b/CommunityToolkit.App.Shared/Renderers/Markdown/MarkdownTextBlock.cs index 0a6c0d5a..bb2e1cd7 100644 --- a/CommunityToolkit.App.Shared/Renderers/Markdown/MarkdownTextBlock.cs +++ b/CommunityToolkit.App.Shared/Renderers/Markdown/MarkdownTextBlock.cs @@ -4,6 +4,7 @@ #if HAS_LABS_MARKDOWNTEXTBLOCK using CommunityToolkit.WinUI.Controls; + using Microsoft.UI.Text; using ToolkitMTB = CommunityToolkit.WinUI.Controls.MarkdownTextBlock; #elif HAS_UNO #if WINAPPSDK @@ -53,6 +54,7 @@ public MarkdownTextBlock() H6FontSize = 14, ParagraphMargin = new Thickness(0, 0, 0, 12), ParagraphLineHeight = 20, + InlineCodeFontSize = 12, }, };