Repository navigation
CmdPal: Update custom card elements in place, including in release builds - #51204
Merged
Merged
Conversation
âĶilds The incremental Adaptive Cards updater never patched in place in release builds. Those builds compile Command Palette with native AOT, where CsWinRT finds an object's type by name and needs its metadata. Element tags came back as bare IInspectable objects and text runs as Inline, so nothing mapped and every update rebuilt the whole card. The methods that read tags and runs now keep that metadata with DynamicDependency. Custom elements can also update in place. A host registers their types with IncrementalPatchableElements, their controls implement IIncrementalAdaptiveElementControl, and their data stays out of the card's fingerprint. The new constructor parameter is optional, so existing callers don't change.
ð§ PR intakeVisual evidence: Not needed â The changed files do not indicate a visible UI change. No visual evidence is expected. Recommendation
â Ready for reviewThis PR passed the automated intake checks and is ready for maintainer review. Automated PR intake; PowerToys maintainers make final decisions. |
4 of 11 tasks
4 of 11 tasks
Mike Griese (zadjii-msft)
left a comment
Member
There was a problem hiding this comment.
I really wish that the IncrementalAdaptiveCardVisualTree changes in here to fix the AOT issue would have been their own PR, frankly. They were a needle in a haystack
Mike Griese (zadjii-msft)
approved these changes
Oct 15, 2026
Boliang Zhang (LegendaryBlair)
pushed a commit
that referenced
this pull request
Oct 16, 2026
âĶilds (#51204) <!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? --> ## Summary of the Pull Request **This PR is layer 1 of 13 in a stack.** The stack divides #51125 (native Adaptive Cards charts and new Performance Monitor cards) into smaller PRs. Refer to #51125 for the full design. This layer starts from `main`. It changes only `Microsoft.CmdPal.AdaptiveCards.IncrementalRendering` and its tests. This PR corrects two faults in the incremental updater for Adaptive Cards: - **Release builds do not update cards in place.** Under native AOT, the updater cannot read the element tags of the renderer. As a result, each update replaces the full card. - **Custom elements cannot update in place.** With this PR, a host can register custom element types. The controls of these types accept new data directly. Layer 2 (the chart polyfills library) uses this feature. Thus, live charts scroll, and the updater does not replace the card each second. ## Detailed Description of the Pull Request / Additional comments ### The fault in release builds Release builds compile Command Palette with native AOT. `.pipelines/v2/release.yml` sets `/p:EnableCmdPalAOT=true`. This property turns on `PublishAot` for `Microsoft.CmdPal.UI`. CI builds do not use native AOT. Thus, PR validation does not test this path. The updater uses element tags to find the controls that it can patch. A patchable control is a `TextBlock` or `Image` whose `Tag` is an `ElementTagContent` for an `AdaptiveTextBlock` or `AdaptiveImage`. Under native AOT, CsWinRT needs type metadata to identify an object that it receives as `object` or as a base type. No code kept this metadata. As a result, element tags came back as bare `IInspectable` objects. Text runs came back as `Inline`, not as `Run`. The fingerprint then treated all text and images as structure, and each update replaced the full card. The fix adds `[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(...))]` to the methods that read tags and runs. The attribute keeps the metadata for `ElementTagContent`, `AdaptiveTextBlock`, `AdaptiveImage`, and `Run`. I found this fault in #51125 with a native AOT build of a local card harness. The Performance Monitor cards in `main` show the same fault (refer to Validation). ### Patches for custom elements - `IncrementalPatchableElements` (new) is the list of custom element types that the updater can patch. `Add(type, rendersItself)` is for a type whose renderer sometimes shows the `fallback`. - `IIncrementalAdaptiveElementControl` (new) is the interface for the control of the element. The control gives a deterministic `IncrementalState`. First, the updater calls `CanApplyIncrementalState` on all changed controls. Then it calls `ApplyIncrementalState`. - `IncrementalAdaptiveCardUpdater` gets the registration through a new optional constructor parameter. Existing callers, for example `ContentFormControl`, do not change. Without a registration, the behavior does not change, except for the AOT fix. - `AdaptiveCardSemanticFingerprint` does not include the data of a registered element. Thus, a data change patches the control and does not replace the card. A change to a host-owned property (`id`, `isVisible`, `spacing`, `height`, or the actions) still replaces the card. - Fallbacks: A registered element that renders itself never shows its `fallback`. Thus, the updater ignores changes in that fallback, unless the element has `requires`. If an element shows its fallback, the updater cannot patch a fallback root that is a `TextBlock` or `Image`. The updater can patch text in a fallback container. - `README.md` tells how to register and implement a patchable element. The examples use `Chart.Line` and `Icon`, which layers 2 and 3 add. No code in Command Palette registers a patchable element yet. Layer 2 does this. The UI does not change, so this PR has no screenshots. (cherry picked from commit 28acb1b) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Mike Griese (zadjii-msft)
pushed a commit
that referenced
this pull request
Nov 3, 2026
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? --> ## Summary of the Pull Request **Layer 2 of 13 in a stack that splits #51125** (native Adaptive Cards charts and redesigned Performance Monitor cards) into reviewable PRs. See #51125 for the full picture. This PR targets layer 1's branch (#51204), so its diff shows only this layer. It adds `Microsoft.CmdPal.AdaptiveCards.Polyfills`, a library that renders Adaptive Cards schema elements the WinUI 3 renderer doesn't support yet, with its first element, `Chart.Line`: - Smooth, filled lines with gridlines, axis titles, a legend, and a marker on the latest point. - New data updates the chart in place, through layer 1's patchable elements. When live data gains one sample, the lines scroll left instead of the card being rebuilt. - Narrower than 400 px, the chart draws as a sparkline, as in a dashboard tile. - Colors come from the schema's color names, in light and dark variants. In a contrast theme, series take the theme's colors and also differ by dash pattern. Screen readers get the chart's name and a summary of each series. Any extension can send `Chart.Line` as standard Adaptive Cards JSON. Today the renderer drops it. Nothing in Command Palette uses it yet; the Performance Monitor cards start in layer 5. <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [ ] Closes: N/A. There's no issue; this is layer 2 of 13 splitting #51125 <!-- - [ ] Closes: #yyy (add separate lines for additional resolved issues) --> - [ ] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected - [x] **Tests:** Added/updated and all pass - [x] **Localization:** All end-user-facing strings can be localized (7 new strings in Command Palette's `Resources.resw`; the library gets them from the host) - [x] **Dev docs:** Added/updated (the library's `README.md`) - [x] **New binaries:** Added on the required places. `Microsoft.CmdPal.AdaptiveCards.Polyfills.dll` ships inside Command Palette's MSIX, which is signed as a whole, like `Microsoft.CmdPal.AdaptiveCards.IncrementalRendering.dll`, so it only needs the solution entries. - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries (not needed: the MSIX is signed) - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder (not needed: it's in the MSIX) - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects (not needed: CI runs every `**\*UnitTest*.dll`) - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) (not needed) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx <!-- Provide a more detailed description of the PR, other things fixed, or any additional comments/features here --> ## Detailed Description of the Pull Request / Additional comments ### The library - **It fills gaps in the WinUI 3 renderer without a fork.** Elements are registered through the renderer's public custom element APIs: a parser in the element parser registration and a renderer in `ElementRenderers`. - **Nothing in it is specific to Command Palette.** Each element can move to the renderer in [microsoft/AdaptiveCards](https://github.com/microsoft/AdaptiveCards) and be deleted here. The README's "Upstream" section covers what moving one takes. - **It has no XAML or resources.** Controls are built in code, and the host passes its strings in: `RegisterRenderers(renderer, getString)` takes a lookup for the `AdaptiveChart_*` strings in Command Palette's `Resources.resw`. - **Each element is a model and a control.** Models parse the element JSON leniently (invalid values read as missing, with a warning) and don't use WinUI, so the unit tests cover them without a window. Models read only the properties on the element's page in the [Adaptive Cards element reference](https://adaptivecards.microsoft.com/). ### Wiring it in - `ContentFormControl` calls `AdaptiveCardPolyfills.RegisterParsers` and `RegisterRenderers`, and passes `AdaptiveCardPolyfills.PatchableElements` to the incremental updater. - `AdaptiveCustomElementJson` moves from `Microsoft.CmdPal.UI` into the library and becomes public, so the settings inputs and the new elements read the properties every element has (`id`, `isVisible`, `spacing`, `fallback`, `requires`, and so on) the same way. Its new optional `requireId` parameter defaults to `true`; charts don't need an `id`. The settings inputs don't change. ### The fallback workaround When a custom element renderer asks the WinUI 3 renderer to draw the element's `fallback`, `XamlBuilder::RenderAsUIElement` renders the fallback but returns an empty result, so nothing shows. `AdaptiveFallbackRenderer` works around that bug: the element renderer draws the fallback itself and returns it. `Chart.Line` always draws itself, so nothing uses this yet; `Icon` does in layer 3, for icon names without a glyph. ### Shared code for the next layers The theme, palette, scale, curve, shape, and number formatting code lands here whole, with its tests, so the later layers only add elements. Some of it isn't used by `Chart.Line`: - `ChartArc` and the arc, wedge, and ring helpers in `ChartShapes`: gauge and donut charts (layer 10). - `ChartDataPoint` and `ChartJson.GetDataPoints`: bar charts (layer 4) and gauge and donut charts. - The track, semantic, and contrasting text colors in `ChartTheme`, and high contrast outlines in `ChartPalette`: progress bars, badges, and icons (layer 3), bar charts, and gauge and donut charts. - The `AdaptiveVisualControl` base class and `AdaptiveFallbackRenderer`: layer 3 on. `LineChartControl` implements its own updates, so that it can animate a one-sample shift as a scroll. ### Screenshots Live line charts, captured with a local card harness (not in the repo): an unpackaged WinUI app that renders inline `Chart.Line` cards through the real renderer, this library, and `IncrementalAdaptiveCardUpdater`, at the dock window's content width (776 px), with `main`'s host config and data that gains one sample a second. | | Dark | Light | |---|---|---| | One series |  |  | | Two series |  |  | | Narrow columns, drawn as sparklines |  |  | The two-series chart in a simulated Aquatic contrast theme (the harness applies the theme's colors to the charts only). The series take the theme's colors and differ by dash pattern, which the legend shows:  <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed Every library and test file is identical to its version at #51125's head (`8858a9e0a7`), except `AdaptiveCardPolyfills.cs` and `README.md`, which are cut down to `Chart.Line`. In `Microsoft.CmdPal.UI`, `ContentFormControl.xaml.cs` takes only the polyfills lines; the host config lines are in #51205. ARM64 Debug builds, run from a `subst` drive (common build options: `-nologo -m -restore -t:Build -v:minimal -p:Configuration=Debug -p:Platform=ARM64 -p:RuntimeIdentifier=win-arm64 -p:CIBuild=false -p:GeneratePackageLocally=false -p:GenerateAppxPackageOnBuild=false -p:PublishAppxPackage=false -p:SkipLongPathsCheck=true`): - `MSBuild.exe src\modules\cmdpal\Tests\Microsoft.CmdPal.AdaptiveCards.Polyfills.UnitTests\Microsoft.CmdPal.AdaptiveCards.Polyfills.UnitTests.csproj <options>`: exit code 0, no warnings. It builds the library, which is `IsAotCompatible`, so the trim and AOT analyzers run. - `vstest.console.arm64.exe ARM64\Debug\WinUI3Apps\CmdPal\tests\Microsoft.CmdPal.AdaptiveCards.Polyfills.UnitTests.dll /Platform:ARM64`: 81 passed, 0 failed. - `MSBuild.exe src\modules\cmdpal\Microsoft.CmdPal.UI\Microsoft.CmdPal.UI.csproj <options>`: exit code 0. No warnings from C# projects; the only warnings are this machine's missing `NETFXSDK` LIB path (CS1668) and LNK4075 in the C++ projects, which this PR doesn't touch. - `MSBuild.exe src\modules\cmdpal\Tests\Microsoft.CmdPal.UI.UnitTests\Microsoft.CmdPal.UI.UnitTests.csproj <options>`: exit code 0, no warnings. - `vstest.console.arm64.exe ARM64\Debug\WinUI3Apps\CmdPal\tests\Microsoft.CmdPal.UI.UnitTests.dll /Platform:ARM64`: 715 passed, 0 failed. - `.pipelines\verifyCommonProps.ps1 -sourceDir <worktree>\src`: exit code 0. - Spelling: the repo's check-spelling workflow is disabled, so I ran a local approximation of it (its dictionaries plus `expect.txt`) over this PR's diff: no unknown words and no forbidden patterns. `sparkline` is added to `expect.txt`. The unit tests run under JIT, and release builds compile Command Palette with native AOT, so I also ran the card harness both ways. Each run renders the three cards in dark and light, updates them once a second, and counts the updates that replaced the whole card instead of patching the chart: | Harness build | Updates per card | Updates that replaced the whole card | |---|---|---| | JIT, library sources compiled in | 8 | 0 | | Native AOT (`PublishAot`, optimized), referencing the library assembly | 10 | 0 | The native AOT publish had no trim or AOT warnings, and its screenshots match the JIT ones.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary of the Pull Request
This PR is layer 1 of 13 in a stack. The stack divides #51125 (native Adaptive Cards charts and new Performance Monitor cards) into smaller PRs. Refer to #51125 for the full design. This layer starts from
main. It changes onlyMicrosoft.CmdPal.AdaptiveCards.IncrementalRenderingand its tests.This PR corrects two faults in the incremental updater for Adaptive Cards:
Detailed Description of the Pull Request / Additional comments
The fault in release builds
Release builds compile Command Palette with native AOT.
.pipelines/v2/release.ymlsets/p:EnableCmdPalAOT=true. This property turns onPublishAotforMicrosoft.CmdPal.UI. CI builds do not use native AOT. Thus, PR validation does not test this path.The updater uses element tags to find the controls that it can patch. A patchable control is a
TextBlockorImagewhoseTagis anElementTagContentfor anAdaptiveTextBlockorAdaptiveImage. Under native AOT, CsWinRT needs type metadata to identify an object that it receives asobjector as a base type. No code kept this metadata. As a result, element tags came back as bareIInspectableobjects. Text runs came back asInline, not asRun. The fingerprint then treated all text and images as structure, and each update replaced the full card.The fix adds
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(...))]to the methods that read tags and runs. The attribute keeps the metadata forElementTagContent,AdaptiveTextBlock,AdaptiveImage, andRun. I found this fault in #51125 with a native AOT build of a local card harness. The Performance Monitor cards inmainshow the same fault (refer to Validation).Patches for custom elements
IncrementalPatchableElements(new) is the list of custom element types that the updater can patch.Add(type, rendersItself)is for a type whose renderer sometimes shows thefallback.IIncrementalAdaptiveElementControl(new) is the interface for the control of the element. The control gives a deterministicIncrementalState. First, the updater callsCanApplyIncrementalStateon all changed controls. Then it callsApplyIncrementalState.IncrementalAdaptiveCardUpdatergets the registration through a new optional constructor parameter. Existing callers, for exampleContentFormControl, do not change. Without a registration, the behavior does not change, except for the AOT fix.AdaptiveCardSemanticFingerprintdoes not include the data of a registered element. Thus, a data change patches the control and does not replace the card. A change to a host-owned property (id,isVisible,spacing,height, or the actions) still replaces the card.fallback. Thus, the updater ignores changes in that fallback, unless the element hasrequires. If an element shows its fallback, the updater cannot patch a fallback root that is aTextBlockorImage. The updater can patch text in a fallback container.README.mdtells how to register and implement a patchable element. The examples useChart.LineandIcon, which layers 2 and 3 add.No code in Command Palette registers a patchable element yet. Layer 2 does this. The UI does not change, so this PR has no screenshots.