diff --git a/common/Contracts/IDevHomeActionSetRender.cs b/common/Contracts/IDevHomeActionSetRender.cs
new file mode 100644
index 0000000000..ee7424123c
--- /dev/null
+++ b/common/Contracts/IDevHomeActionSetRender.cs
@@ -0,0 +1,27 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using AdaptiveCards.Rendering.WinUI3;
+
+namespace DevHome.Common.Contracts;
+
+///
+/// Represents a renderer for an adaptive card action set that Dev Home can use to invoke actions from within
+/// the action set. This is useful for invoking an adaptive card action from an abitraty button within Dev Home's UI.
+///
+public interface IDevHomeActionSetRender : IAdaptiveElementRenderer
+{
+ ///
+ /// Attempts to validate the user inputs and initiate the action.
+ ///
+ /// The id of the button within the adaptive card Json template
+ /// The user input from the adaptive card session
+ /// True if the users inputs were validated and false otherwise
+ public bool TryValidateAndInitiateAction(string buttonId, AdaptiveInputs userInputs);
+
+ ///
+ /// Initiates the action without validating the user inputs.
+ ///
+ /// The id of the button within the adaptive card Json template
+ public void InitiateAction(string buttonId);
+}
diff --git a/common/DevHomeAdaptiveCards/CardInterfaces/IDevHomeSettingsCard.cs b/common/DevHomeAdaptiveCards/CardInterfaces/IDevHomeSettingsCard.cs
new file mode 100644
index 0000000000..2e523c7517
--- /dev/null
+++ b/common/DevHomeAdaptiveCards/CardInterfaces/IDevHomeSettingsCard.cs
@@ -0,0 +1,35 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using AdaptiveCards.ObjectModel.WinUI3;
+
+namespace DevHome.Common.DevHomeAdaptiveCards.CardInterfaces;
+
+///
+/// Represents a Dev Home settings card that can be rendered through an adaptive card.
+///
+public interface IDevHomeSettingsCard : IAdaptiveCardElement
+{
+ ///
+ /// gets or sets the subtitle of the card. The description naming is used because that is what
+ /// is used in the Windows Community Toolkit settings cards. This is displayed below the header.
+ ///
+ public string Description { get; set; }
+
+ ///
+ /// gets or sets the header of the card. This is the bolded text at the top of the card.
+ ///
+ public string Header { get; set; }
+
+ ///
+ /// gets or sets the icon base64 string that represents an image. This is the icon that is
+ /// displayed to the left of the header.
+ ///
+ public string HeaderIcon { get; set; }
+
+ // An element that is not expected to submit the adaptive card
+ public IDevHomeSettingsCardNonSubmitAction? NonSubmitActionElement { get; set; }
+
+ // An element that is expected to submit the adaptive card
+ public IAdaptiveActionElement? SubmitActionElement { get; set; }
+}
diff --git a/common/DevHomeAdaptiveCards/CardInterfaces/IDevHomeSettingsCardNonSubmitAction.cs b/common/DevHomeAdaptiveCards/CardInterfaces/IDevHomeSettingsCardNonSubmitAction.cs
new file mode 100644
index 0000000000..327f07b0a9
--- /dev/null
+++ b/common/DevHomeAdaptiveCards/CardInterfaces/IDevHomeSettingsCardNonSubmitAction.cs
@@ -0,0 +1,26 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Threading.Tasks;
+using AdaptiveCards.ObjectModel.WinUI3;
+using CommunityToolkit.Mvvm.Input;
+
+namespace DevHome.Common.DevHomeAdaptiveCards.CardInterfaces;
+
+///
+/// Represents an action that can be invoked from a Dev Home settings card that does not submit the card. E.g launches a dialog.
+///
+public interface IDevHomeSettingsCardNonSubmitAction : IAdaptiveCardElement
+{
+ ///
+ /// Gets the text that is displayed on the action element. E.g button text.
+ ///
+ public string ActionText { get; }
+
+ ///
+ /// Invokes the action through a relay command
+ ///
+ /// The UI object that the command originated from
+ [RelayCommand]
+ public Task InvokeActionAsync(object sender);
+}
diff --git a/common/DevHomeAdaptiveCards/CardModels/DevHomeContentDialogContent.cs b/common/DevHomeAdaptiveCards/CardModels/DevHomeContentDialogContent.cs
new file mode 100644
index 0000000000..c15373debd
--- /dev/null
+++ b/common/DevHomeAdaptiveCards/CardModels/DevHomeContentDialogContent.cs
@@ -0,0 +1,53 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Collections.Generic;
+using AdaptiveCards.ObjectModel.WinUI3;
+using Windows.Data.Json;
+
+namespace DevHome.Common.DevHomeAdaptiveCards.CardModels;
+
+///
+/// Represents a content dialog that can be rendered through an adaptive card based on the
+/// Json template.
+///
+public class DevHomeContentDialogContent : IAdaptiveCardElement
+{
+ // Specific properties for DevHomeContentDialogContent
+ public string Title { get; set; } = string.Empty;
+
+ // This is the adaptive card that will be shown within
+ // a content dialogs body.
+ public JsonObject? ContentDialogInternalAdaptiveCardJson { get; set; }
+
+ public string PrimaryButtonText { get; set; } = string.Empty;
+
+ public string SecondaryButtonText { get; set; } = string.Empty;
+
+ public static string AdaptiveElementType => "DevHome.ContentDialogContent";
+
+ // Properties for IAdaptiveCardElement
+ public string ElementTypeString { get; set; } = AdaptiveElementType;
+
+ public JsonObject AdditionalProperties { get; set; } = new();
+
+ public ElementType ElementType { get; set; } = ElementType.Custom;
+
+ public IAdaptiveCardElement? FallbackContent { get; set; }
+
+ public FallbackType FallbackType { get; set; }
+
+ public HeightType Height { get; set; } = HeightType.Stretch;
+
+ public string Id { get; set; } = string.Empty;
+
+ public bool IsVisible { get; set; } = true;
+
+ public IList Requirements { get; set; } = new List();
+
+ public bool Separator { get; set; }
+
+ public Spacing Spacing { get; set; } = Spacing.Default;
+
+ public JsonObject? ToJson() => [];
+}
diff --git a/common/DevHomeAdaptiveCards/CardModels/DevHomeLaunchContentDialogButton.cs b/common/DevHomeAdaptiveCards/CardModels/DevHomeLaunchContentDialogButton.cs
new file mode 100644
index 0000000000..355c3bb32d
--- /dev/null
+++ b/common/DevHomeAdaptiveCards/CardModels/DevHomeLaunchContentDialogButton.cs
@@ -0,0 +1,65 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using AdaptiveCards.ObjectModel.WinUI3;
+using CommunityToolkit.Mvvm.Input;
+using DevHome.Common.DevHomeAdaptiveCards.CardInterfaces;
+using DevHome.Common.Views.AdaptiveCardViews;
+using Microsoft.UI.Xaml.Controls;
+using Windows.Data.Json;
+
+namespace DevHome.Common.DevHomeAdaptiveCards.CardModels;
+
+public partial class DevHomeLaunchContentDialogButton : IDevHomeSettingsCardNonSubmitAction
+{
+ // Specific properties for DevHomeLaunchContentDialogButton
+ public string ActionText { get; set; } = string.Empty;
+
+ public IAdaptiveCardElement? DialogContent { get; set; }
+
+ public static string AdaptiveElementType => "DevHome.LaunchContentDialogButton";
+
+ // Properties for IAdaptiveActionElement
+ public string ElementTypeString { get; set; } = AdaptiveElementType;
+
+ public JsonObject AdditionalProperties { get; set; } = new();
+
+ public ElementType ElementType { get; set; } = ElementType.Custom;
+
+ public IAdaptiveCardElement? FallbackContent { get; set; }
+
+ public FallbackType FallbackType { get; set; }
+
+ public HeightType Height { get; set; } = HeightType.Stretch;
+
+ public string Id { get; set; } = string.Empty;
+
+ public bool IsVisible { get; set; } = true;
+
+ public IList Requirements { get; set; } = new List();
+
+ public bool Separator { get; set; }
+
+ public Spacing Spacing { get; set; } = Spacing.Default;
+
+ public JsonObject? ToJson() => [];
+
+ [RelayCommand]
+ public async Task InvokeActionAsync(object sender)
+ {
+ var senderObj = sender as Button;
+ if (DialogContent is not DevHomeContentDialogContent dialogContent || senderObj == null)
+ {
+ return;
+ }
+
+ var dialog = new ContentDialogWithNonInteractiveContent(dialogContent);
+
+ dialog.XamlRoot = senderObj.XamlRoot;
+
+ await dialog.ShowAsync();
+ }
+}
diff --git a/common/DevHomeAdaptiveCards/CardModels/DevHomeSettingsCard.cs b/common/DevHomeAdaptiveCards/CardModels/DevHomeSettingsCard.cs
new file mode 100644
index 0000000000..d566e158b8
--- /dev/null
+++ b/common/DevHomeAdaptiveCards/CardModels/DevHomeSettingsCard.cs
@@ -0,0 +1,66 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+using AdaptiveCards.ObjectModel.WinUI3;
+using DevHome.Common.DevHomeAdaptiveCards.CardInterfaces;
+using Microsoft.UI.Xaml.Controls;
+using Windows.Data.Json;
+
+namespace DevHome.Common.DevHomeAdaptiveCards.CardModels;
+
+///
+/// Represents a settings card that can be rendered through an adaptive card based on the ElementTypeString.
+///
+public class DevHomeSettingsCard : IDevHomeSettingsCard
+{
+ // Specific properties for DevHomeSettingsCard
+ // These properties relate to the Windows Community Toolkit's SettingsCard control.
+ // We'll allow extensions to provide the data for the SettingsCard control from an Adaptive Card.
+ // Then we'll render the actual SettingsCard control in the DevHome app.
+ ///
+ public string Description { get; set; } = string.Empty;
+
+ ///
+ public string Header { get; set; } = string.Empty;
+
+ ///
+ public string HeaderIcon { get; set; } = string.Empty;
+
+ [JsonIgnore]
+ public ImageIcon? HeaderIconImage { get; set; }
+
+ ///
+ /// Gets or sets the element that does not submit the card.
+ public IDevHomeSettingsCardNonSubmitAction? NonSubmitActionElement { get; set; }
+
+ public IAdaptiveActionElement? SubmitActionElement { get; set; }
+
+ public static string AdaptiveElementType => "DevHome.SettingsCard";
+
+ // Properties for IAdaptiveCardElement
+ public string ElementTypeString { get; set; } = AdaptiveElementType;
+
+ public JsonObject AdditionalProperties { get; set; } = new();
+
+ public ElementType ElementType { get; set; } = ElementType.Custom;
+
+ public IAdaptiveCardElement? FallbackContent { get; set; }
+
+ public FallbackType FallbackType { get; set; }
+
+ public HeightType Height { get; set; } = HeightType.Stretch;
+
+ public string Id { get; set; } = string.Empty;
+
+ public bool IsVisible { get; set; } = true;
+
+ public IList Requirements { get; set; } = new List();
+
+ public bool Separator { get; set; }
+
+ public Spacing Spacing { get; set; } = Spacing.Default;
+
+ public JsonObject? ToJson() => [];
+}
diff --git a/common/DevHomeAdaptiveCards/CardModels/DevHomeSettingsCardChoiceSet.cs b/common/DevHomeAdaptiveCards/CardModels/DevHomeSettingsCardChoiceSet.cs
new file mode 100644
index 0000000000..6dc43613f9
--- /dev/null
+++ b/common/DevHomeAdaptiveCards/CardModels/DevHomeSettingsCardChoiceSet.cs
@@ -0,0 +1,56 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Collections.Generic;
+using AdaptiveCards.ObjectModel.WinUI3;
+using Windows.Data.Json;
+
+namespace DevHome.Common.DevHomeAdaptiveCards.CardModels;
+
+public class DevHomeSettingsCardChoiceSet : IAdaptiveCardElement, IAdaptiveInputElement
+{
+ // Specific properties for DevHomeAdaptiveSettingsCardItemsViewChoiceSet
+ public IList SettingsCards { get; set; } = [];
+
+ public const int UnselectedIndex = -1;
+
+ public bool IsSelectionDisabled { get; set; }
+
+ public static string AdaptiveElementType => "DevHome.SettingsCardChoiceSet";
+
+ public bool IsMultiSelect { get; set; }
+
+ public int SelectedValue { get; set; } = UnselectedIndex;
+
+ // Specific properties for IAdaptiveInputElement
+ public ElementType ElementType { get; set; } = ElementType.Custom;
+
+ public string ElementTypeString { get; set; } = AdaptiveElementType;
+
+ // Specific properties for IAdaptiveCardElement
+ public string ErrorMessage { get; set; } = string.Empty;
+
+ public bool IsRequired { get; set; }
+
+ public string Label { get; set; } = string.Empty;
+
+ public JsonObject AdditionalProperties { get; set; } = new();
+
+ public IAdaptiveCardElement? FallbackContent { get; set; }
+
+ public FallbackType FallbackType { get; set; }
+
+ public HeightType Height { get; set; } = HeightType.Stretch;
+
+ public string Id { get; set; } = string.Empty;
+
+ public bool IsVisible { get; set; } = true;
+
+ public IList Requirements { get; set; } = new List();
+
+ public bool Separator { get; set; }
+
+ public Spacing Spacing { get; set; } = Spacing.Default;
+
+ public JsonObject? ToJson() => [];
+}
diff --git a/common/DevHomeAdaptiveCards/InputValues/ItemsViewInputValue.cs b/common/DevHomeAdaptiveCards/InputValues/ItemsViewInputValue.cs
new file mode 100644
index 0000000000..7bb58db5f7
--- /dev/null
+++ b/common/DevHomeAdaptiveCards/InputValues/ItemsViewInputValue.cs
@@ -0,0 +1,58 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Globalization;
+using AdaptiveCards.ObjectModel.WinUI3;
+using AdaptiveCards.Rendering.WinUI3;
+using DevHome.Common.Environments.Helpers;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+
+namespace DevHome.Common.DevHomeAdaptiveCards.InputValues;
+
+///
+/// Represents the current value of an ItemsView input element.
+///
+public class ItemsViewInputValue : IAdaptiveInputValue
+{
+ private readonly ItemsView _itemsView;
+
+ public ItemsViewInputValue(IAdaptiveInputElement input, ItemsView itemsView)
+ {
+ InputElement = input;
+ _itemsView = itemsView;
+ }
+
+ ///
+ /// Gets the current value of the input element. This is the index of the current item.
+ ///
+ public string CurrentValue => _itemsView.CurrentItemIndex.ToString(CultureInfo.InvariantCulture);
+
+ public UIElement? ErrorMessage { get; set; }
+
+ public IAdaptiveInputElement InputElement { get; set; }
+
+ public void SetFocus()
+ {
+ _itemsView.Focus(FocusState.Keyboard);
+ }
+
+ // If the items view selection mode isn't None, then the user must select an item.
+ public bool Validate()
+ {
+ if (_itemsView.SelectionMode == ItemsViewSelectionMode.None)
+ {
+ return true;
+ }
+
+ if ((_itemsView.SelectedItem == null) || (_itemsView.CurrentItemIndex < 0))
+ {
+ var errorMessage = StringResourceHelper.GetResource("ItemsViewNonSelectedItemError");
+ ErrorMessage = new TextBlock();
+ ErrorMessage.SetValue(TextBlock.TextProperty, errorMessage);
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/common/DevHomeAdaptiveCards/Parsers/DevHomeContentDialogContentParser.cs b/common/DevHomeAdaptiveCards/Parsers/DevHomeContentDialogContentParser.cs
new file mode 100644
index 0000000000..071150342c
--- /dev/null
+++ b/common/DevHomeAdaptiveCards/Parsers/DevHomeContentDialogContentParser.cs
@@ -0,0 +1,52 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Collections.Generic;
+using AdaptiveCards.ObjectModel.WinUI3;
+using DevHome.Common.DevHomeAdaptiveCards.CardModels;
+using DevHome.Common.Environments.Helpers;
+using Windows.Data.Json;
+
+namespace DevHome.Common.DevHomeAdaptiveCards.Parsers;
+
+///
+/// Represents a parser for a Dev Home content dialog card that can be rendered through an adaptive card.
+/// This parser will be used if the element type is "DevHome.ContentDialogContent".
+///
+///
+/// the JsonObject is a Windows.Data.Json.JsonObject, which has methods that can throw an exception if the type is not correct.
+///
+public class DevHomeContentDialogContentParser : IAdaptiveElementParser
+{
+ public IAdaptiveCardElement FromJson(JsonObject inputJson, AdaptiveElementParserRegistration elementParsers, AdaptiveActionParserRegistration actionParsers, IList warnings)
+ {
+ var dialog = new DevHomeContentDialogContent();
+ bool isCorrectType;
+
+ if (inputJson.TryGetValue("devHomeContentDialogTitle", out var devHomeContentDialogTitle))
+ {
+ isCorrectType = devHomeContentDialogTitle.ValueType == JsonValueType.String;
+ dialog.Title = isCorrectType ? devHomeContentDialogTitle.GetString() : StringResourceHelper.GetResource("DevHomeContentDialogDefaultTitle");
+ }
+
+ if (inputJson.TryGetValue("devHomeContentDialogBodyAdaptiveCard", out var contentDialogInternalAdaptiveCardJson))
+ {
+ isCorrectType = contentDialogInternalAdaptiveCardJson.ValueType == JsonValueType.Object;
+ dialog.ContentDialogInternalAdaptiveCardJson = isCorrectType ? contentDialogInternalAdaptiveCardJson.GetObject() : new JsonObject();
+ }
+
+ if (inputJson.TryGetValue("devHomeContentDialogPrimaryButtonText", out var devHomeContentDialogPrimaryButtonText))
+ {
+ isCorrectType = devHomeContentDialogPrimaryButtonText.ValueType == JsonValueType.String;
+ dialog.PrimaryButtonText = isCorrectType ? devHomeContentDialogPrimaryButtonText.GetString() : StringResourceHelper.GetResource("DevHomeContentDialogDefaultPrimaryButtonText");
+ }
+
+ if (inputJson.TryGetValue("devHomeContentDialogSecondaryButtonText", out var devHomeContentDialogSecondaryButtonText))
+ {
+ isCorrectType = devHomeContentDialogSecondaryButtonText.ValueType == JsonValueType.String;
+ dialog.SecondaryButtonText = isCorrectType ? devHomeContentDialogSecondaryButtonText.GetString() : StringResourceHelper.GetResource("DevHomeContentDialogDefaultSecondaryButtonText");
+ }
+
+ return dialog;
+ }
+}
diff --git a/common/DevHomeAdaptiveCards/Parsers/DevHomeLaunchContentDialogButtonParser.cs b/common/DevHomeAdaptiveCards/Parsers/DevHomeLaunchContentDialogButtonParser.cs
new file mode 100644
index 0000000000..a8ad0357f1
--- /dev/null
+++ b/common/DevHomeAdaptiveCards/Parsers/DevHomeLaunchContentDialogButtonParser.cs
@@ -0,0 +1,43 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Collections.Generic;
+using AdaptiveCards.ObjectModel.WinUI3;
+using DevHome.Common.DevHomeAdaptiveCards.CardModels;
+using DevHome.Common.Environments.Helpers;
+using Windows.Data.Json;
+
+namespace DevHome.Common.DevHomeAdaptiveCards.Parsers;
+
+///
+/// Represents a parser for a Dev Home launch content dialog button that can be rendered through an adaptive card.
+/// This parser will be used if the element type is "DevHome.LaunchContentDialogButton".
+///
+///
+/// the JsonObject is a Windows.Data.Json.JsonObject, which has methods that can throw an exception if the type is not correct.
+///
+public class DevHomeLaunchContentDialogButtonParser : IAdaptiveElementParser
+{
+ public IAdaptiveCardElement FromJson(JsonObject inputJson, AdaptiveElementParserRegistration elementParsers, AdaptiveActionParserRegistration actionParsers, IList warnings)
+ {
+ var action = new DevHomeLaunchContentDialogButton();
+ bool isCorrectType;
+
+ if (inputJson.TryGetValue("devHomeActionText", out var devHomeActionText))
+ {
+ isCorrectType = devHomeActionText.ValueType == JsonValueType.String;
+ action.ActionText = isCorrectType ? devHomeActionText.GetString() : StringResourceHelper.GetResource("DevHomeActionDefaultText");
+ }
+
+ // Parse the content dialog element and place its content into our content dialog button property.
+ if (inputJson.TryGetValue("devHomeContentDialogContent", out var devHomeContentDialogContent))
+ {
+ isCorrectType = devHomeContentDialogContent.ValueType == JsonValueType.Object;
+ var contentDialogJson = isCorrectType ? devHomeContentDialogContent.GetObject() : new JsonObject();
+ var contentDialogParser = elementParsers.Get(DevHomeContentDialogContent.AdaptiveElementType);
+ action.DialogContent = contentDialogParser?.FromJson(contentDialogJson, elementParsers, actionParsers, warnings);
+ }
+
+ return action;
+ }
+}
diff --git a/common/DevHomeAdaptiveCards/Parsers/DevHomeSettingsCardChoiceSetParser.cs b/common/DevHomeAdaptiveCards/Parsers/DevHomeSettingsCardChoiceSetParser.cs
new file mode 100644
index 0000000000..eabe0c89a2
--- /dev/null
+++ b/common/DevHomeAdaptiveCards/Parsers/DevHomeSettingsCardChoiceSetParser.cs
@@ -0,0 +1,99 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Collections.Generic;
+using AdaptiveCards.ObjectModel.WinUI3;
+using DevHome.Common.DevHomeAdaptiveCards.CardModels;
+using DevHome.Common.Environments.Helpers;
+using Windows.Data.Json;
+
+namespace DevHome.Common.DevHomeAdaptiveCards.Parsers;
+
+///
+/// Represents a parser for a Dev Home settings card choice set that can be rendered through an adaptive card.
+/// This parser will be used if the element type is "DevHome.SettingsCardChoiceSet".
+///
+///
+/// the JsonObject is a Windows.Data.Json.JsonObject, which has methods that can throw an exception if the type is not correct.
+///
+public class DevHomeSettingsCardChoiceSetParser : IAdaptiveElementParser
+{
+ public IAdaptiveCardElement FromJson(JsonObject inputJson, AdaptiveElementParserRegistration elementParsers, AdaptiveActionParserRegistration actionParsers, IList warnings)
+ {
+ var adaptiveSettingsCardChoiceSet = new DevHomeSettingsCardChoiceSet();
+ bool isCorrectType;
+
+ if (inputJson.TryGetValue("id", out var id))
+ {
+ isCorrectType = id.ValueType == JsonValueType.String;
+ adaptiveSettingsCardChoiceSet.Id = isCorrectType ? id.GetString() : string.Empty;
+ }
+
+ if (inputJson.TryGetValue("label", out var label))
+ {
+ isCorrectType = label.ValueType == JsonValueType.String;
+ adaptiveSettingsCardChoiceSet.Label = isCorrectType ? label.GetString() : StringResourceHelper.GetResource("SettingsCardChoiceSetDefaultLabel");
+ }
+
+ if (inputJson.TryGetValue("isRequired", out var isRequired))
+ {
+ isCorrectType = isRequired.ValueType == JsonValueType.Boolean;
+ adaptiveSettingsCardChoiceSet.IsRequired = isCorrectType ? isRequired.GetBoolean() : false;
+ }
+
+ if (inputJson.TryGetValue("selectedValue", out var selectedValue))
+ {
+ isCorrectType = selectedValue.ValueType == JsonValueType.Number;
+ adaptiveSettingsCardChoiceSet.SelectedValue = isCorrectType ? (int)selectedValue.GetNumber() : DevHomeSettingsCardChoiceSet.UnselectedIndex;
+ }
+
+ if (inputJson.TryGetValue("isMultiSelect", out var isMultiSelect))
+ {
+ isCorrectType = isMultiSelect.ValueType == JsonValueType.Boolean;
+ adaptiveSettingsCardChoiceSet.IsMultiSelect = isCorrectType ? isMultiSelect.GetBoolean() : false;
+ }
+
+ // If IsSelectionDisabled is true, then IsMultiSelect should be false and no item should be selected.
+ if (inputJson.TryGetValue("devHomeSettingsCardIsSelectionDisabled", out var devHomeSettingsCardIsSelectionDisabled))
+ {
+ isCorrectType = devHomeSettingsCardIsSelectionDisabled.ValueType == JsonValueType.Boolean;
+ adaptiveSettingsCardChoiceSet.IsSelectionDisabled = isCorrectType ? devHomeSettingsCardIsSelectionDisabled.GetBoolean() : false;
+
+ if (adaptiveSettingsCardChoiceSet.IsSelectionDisabled)
+ {
+ adaptiveSettingsCardChoiceSet.SelectedValue = DevHomeSettingsCardChoiceSet.UnselectedIndex;
+ adaptiveSettingsCardChoiceSet.IsMultiSelect = false;
+ }
+ }
+
+ // Parse the settings cards
+ if (inputJson.TryGetValue("devHomeSettingsCards", out var devHomeSettingsCards))
+ {
+ isCorrectType = devHomeSettingsCards.ValueType == JsonValueType.Array;
+ var elementJson = isCorrectType ? devHomeSettingsCards.GetArray() : [];
+ adaptiveSettingsCardChoiceSet.SettingsCards = GetSettingsCards(elementJson, elementParsers, actionParsers, warnings);
+ }
+
+ return adaptiveSettingsCardChoiceSet;
+ }
+
+ private List GetSettingsCards(JsonArray elementJson, AdaptiveElementParserRegistration elementParsers, AdaptiveActionParserRegistration actionParsers, IList warnings)
+ {
+ List settingsCards = new();
+ var parser = elementParsers.Get(DevHomeSettingsCard.AdaptiveElementType);
+ foreach (var element in elementJson)
+ {
+ if (element.ValueType != JsonValueType.Object)
+ {
+ continue;
+ }
+
+ if (parser.FromJson(element.GetObject(), elementParsers, actionParsers, warnings) is DevHomeSettingsCard card)
+ {
+ settingsCards.Add(card);
+ }
+ }
+
+ return settingsCards;
+ }
+}
diff --git a/common/DevHomeAdaptiveCards/Parsers/DevHomeSettingsCardParser.cs b/common/DevHomeAdaptiveCards/Parsers/DevHomeSettingsCardParser.cs
new file mode 100644
index 0000000000..c4a382997a
--- /dev/null
+++ b/common/DevHomeAdaptiveCards/Parsers/DevHomeSettingsCardParser.cs
@@ -0,0 +1,85 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using AdaptiveCards.ObjectModel.WinUI3;
+using DevHome.Common.DevHomeAdaptiveCards.CardInterfaces;
+using DevHome.Common.DevHomeAdaptiveCards.CardModels;
+using DevHome.Common.Environments.Helpers;
+using Windows.Data.Json;
+
+namespace DevHome.Common.DevHomeAdaptiveCards.Parsers;
+
+///
+/// Represents a parser for a Dev Home settings card that can be rendered through an adaptive card.
+/// This parser will be used if the element type is "DevHome.SettingsCard".
+///
+///
+/// the JsonObject is a Windows.Data.Json.JsonObject, which has methods that can throw an exception if the type is not correct.
+///
+public class DevHomeSettingsCardParser : IAdaptiveElementParser
+{
+ public IAdaptiveCardElement FromJson(JsonObject inputJson, AdaptiveElementParserRegistration elementParsers, AdaptiveActionParserRegistration actionParsers, IList warnings)
+ {
+ var adaptiveSettingsCard = new DevHomeSettingsCard();
+ bool isCorrectType;
+
+ if (inputJson.TryGetValue("id", out var id))
+ {
+ isCorrectType = id.ValueType == JsonValueType.String;
+ adaptiveSettingsCard.Id = isCorrectType ? id.GetString() : string.Empty;
+ }
+
+ if (inputJson.TryGetValue("devHomeSettingsCardDescription", out var devHomeSettingsCardDescription))
+ {
+ isCorrectType = devHomeSettingsCardDescription.ValueType == JsonValueType.String;
+ adaptiveSettingsCard.Description = isCorrectType ? devHomeSettingsCardDescription.GetString() : StringResourceHelper.GetResource("SettingsCardDescriptionError");
+ }
+
+ if (inputJson.TryGetValue("devHomeSettingsCardHeader", out var devHomeSettingsCardHeader))
+ {
+ isCorrectType = devHomeSettingsCardHeader.ValueType == JsonValueType.String;
+ adaptiveSettingsCard.Header = isCorrectType ? devHomeSettingsCardHeader.GetString() : StringResourceHelper.GetResource("SettingsCardHeaderError");
+ }
+
+ if (inputJson.TryGetValue("devHomeSettingsCardHeaderIcon", out var devHomeSettingsCardHeaderIcon))
+ {
+ isCorrectType = devHomeSettingsCardHeaderIcon.ValueType == JsonValueType.String;
+ adaptiveSettingsCard.HeaderIcon = isCorrectType ? devHomeSettingsCardHeaderIcon.GetString() : string.Empty;
+ }
+
+ if (inputJson.TryGetValue("devHomeSettingsCardActionElement", out var devHomeSettingsCardActionElement))
+ {
+ isCorrectType = devHomeSettingsCardActionElement.ValueType == JsonValueType.Object;
+ var elementJson = isCorrectType ? devHomeSettingsCardActionElement.GetObject() : new JsonObject();
+ var elementType = elementJson.GetNamedString("type", string.Empty);
+
+ // More action types can be added in the future by adding more cases here. Note this parser
+ // will only parse elements that don't submit the adaptive card. If we need to support elements
+ // that submit the adaptive card, we'll need to add a new parser action parser and add the IAdaptiveActionElement
+ // to the DevHomeSettingsCard's ActionElement property.
+ if (string.Equals(elementType, DevHomeLaunchContentDialogButton.AdaptiveElementType, StringComparison.OrdinalIgnoreCase))
+ {
+ adaptiveSettingsCard.NonSubmitActionElement = CreateLaunchContentDialogButton(elementJson, elementParsers, actionParsers, warnings);
+ }
+ else
+ {
+ // If the action isn't one of our custom actions, we'll check if there is a built-in action parser that can parse it.
+ var elementParser = elementParsers.Get(elementType);
+ if (elementParser != null)
+ {
+ adaptiveSettingsCard.NonSubmitActionElement = elementParser.FromJson(elementJson, elementParsers, actionParsers, warnings) as IDevHomeSettingsCardNonSubmitAction;
+ }
+ }
+ }
+
+ return adaptiveSettingsCard;
+ }
+
+ private DevHomeLaunchContentDialogButton? CreateLaunchContentDialogButton(JsonObject inputJson, AdaptiveElementParserRegistration elementParsers, AdaptiveActionParserRegistration actionParsers, IList warnings)
+ {
+ var parser = elementParsers.Get(DevHomeLaunchContentDialogButton.AdaptiveElementType);
+ return parser?.FromJson(inputJson, elementParsers, actionParsers, warnings) as DevHomeLaunchContentDialogButton;
+ }
+}
diff --git a/common/Helpers/AdaptiveCardHelpers.cs b/common/Helpers/AdaptiveCardHelpers.cs
new file mode 100644
index 0000000000..a662b5e7f9
--- /dev/null
+++ b/common/Helpers/AdaptiveCardHelpers.cs
@@ -0,0 +1,39 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.IO;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using DevHome.Common.Helpers;
+using DevHome.Common.Models;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Media.Imaging;
+using Serilog;
+
+namespace DevHome.Common.Helpers;
+
+public static class AdaptiveCardHelpers
+{
+ private static readonly ILogger _log = Log.ForContext("SourceContext", nameof(AdaptiveCardHelpers));
+
+ // convert base64 string to image that can be used in a imageIcon control
+ public static ImageIcon ConvertBase64StringToImageIcon(string base64String)
+ {
+ try
+ {
+ var bytes = Convert.FromBase64String(base64String);
+ using var ms = new MemoryStream(bytes);
+ var bitmapImage = new BitmapImage();
+ bitmapImage.SetSource(ms.AsRandomAccessStream());
+ var icon = new ImageIcon() { Source = bitmapImage };
+ return icon;
+ }
+ catch (Exception ex)
+ {
+ _log.Error($"Failed to load image icon", ex);
+ return new ImageIcon();
+ }
+ }
+}
diff --git a/common/Models/ExtensionAdaptiveCard.cs b/common/Models/ExtensionAdaptiveCard.cs
index 30be992375..a814cb4860 100644
--- a/common/Models/ExtensionAdaptiveCard.cs
+++ b/common/Models/ExtensionAdaptiveCard.cs
@@ -12,6 +12,10 @@ namespace DevHome.Common.Models;
public class ExtensionAdaptiveCard : IExtensionAdaptiveCard
{
+ private readonly AdaptiveElementParserRegistration? _elementParserRegistration;
+
+ private readonly AdaptiveActionParserRegistration? _actionParserRegistration;
+
public event EventHandler? UiUpdate;
public string DataJson { get; private set; }
@@ -20,11 +24,14 @@ public class ExtensionAdaptiveCard : IExtensionAdaptiveCard
public string TemplateJson { get; private set; }
- public ExtensionAdaptiveCard()
+ public ExtensionAdaptiveCard(AdaptiveElementParserRegistration? elementParserRegistration = null, AdaptiveActionParserRegistration? actionParserRegistration = null)
{
TemplateJson = new JsonObject().ToJsonString();
DataJson = new JsonObject().ToJsonString();
State = string.Empty;
+
+ _elementParserRegistration = elementParserRegistration ?? new AdaptiveElementParserRegistration();
+ _actionParserRegistration = actionParserRegistration ?? new AdaptiveActionParserRegistration();
}
public ProviderOperationResult Update(string templateJson, string dataJson, string state)
@@ -36,7 +43,7 @@ public ProviderOperationResult Update(string templateJson, string dataJson, stri
// an empty string.
var adaptiveCardString = template.Expand(Newtonsoft.Json.JsonConvert.DeserializeObject(dataJson ?? DataJson));
- var parseResult = AdaptiveCard.FromJsonString(adaptiveCardString);
+ var parseResult = AdaptiveCard.FromJsonString(adaptiveCardString, _elementParserRegistration, _actionParserRegistration);
if (parseResult.AdaptiveCard is null)
{
diff --git a/common/Models/ExtensionAdaptiveCardSession.cs b/common/Models/ExtensionAdaptiveCardSession.cs
new file mode 100644
index 0000000000..9494a1c99c
--- /dev/null
+++ b/common/Models/ExtensionAdaptiveCardSession.cs
@@ -0,0 +1,87 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using DevHome.Common.Helpers;
+
+using Microsoft.Windows.DevHome.SDK;
+using Serilog;
+using Windows.Foundation;
+
+namespace DevHome.Common.Models;
+
+///
+/// Wrapper class for the IExtensionAdaptiveCardSession and IExtensionAdaptiveCardSession2 interfaces.
+///
+public class ExtensionAdaptiveCardSession
+{
+ private readonly ILogger _log = Log.ForContext("SourceContext", nameof(ExtensionAdaptiveCardSession));
+
+ private readonly string _componentName = "ExtensionAdaptiveCardSession";
+
+ public IExtensionAdaptiveCardSession Session { get; private set; }
+
+ public event TypedEventHandler? Stopped;
+
+ public ExtensionAdaptiveCardSession(IExtensionAdaptiveCardSession cardSession)
+ {
+ Session = cardSession;
+
+ if (Session is IExtensionAdaptiveCardSession2 cardSession2)
+ {
+ cardSession2.Stopped += OnSessionStopped;
+ }
+ }
+
+ public ProviderOperationResult Initialize(IExtensionAdaptiveCard extensionUI)
+ {
+ try
+ {
+ return Session.Initialize(extensionUI);
+ }
+ catch (Exception ex)
+ {
+ _log.Error(_componentName, $"Initialize failed due to exception", ex);
+ return new ProviderOperationResult(ProviderOperationStatus.Failure, ex, ex.Message, ex.Message);
+ }
+ }
+
+ public void Dispose()
+ {
+ try
+ {
+ if (Session is IExtensionAdaptiveCardSession2 cardSession2)
+ {
+ cardSession2.Stopped -= OnSessionStopped;
+ }
+
+ Session.Dispose();
+ }
+ catch (Exception ex)
+ {
+ _log.Error(_componentName, $"Dispose failed due to exception", ex);
+ }
+ }
+
+ public async Task OnAction(string action, string inputs)
+ {
+ try
+ {
+ return await Session.OnAction(action, inputs);
+ }
+ catch (Exception ex)
+ {
+ _log.Error(_componentName, $"OnAction failed due to exception", ex);
+ return new ProviderOperationResult(ProviderOperationStatus.Failure, ex, ex.Message, ex.Message);
+ }
+ }
+
+ public void OnSessionStopped(IExtensionAdaptiveCardSession2 sender, ExtensionAdaptiveCardSessionStoppedEventArgs args)
+ {
+ Stopped?.Invoke(this, args);
+ }
+}
diff --git a/common/Renderers/DevHomeActionSet.cs b/common/Renderers/DevHomeActionSet.cs
new file mode 100644
index 0000000000..84ab1c00fc
--- /dev/null
+++ b/common/Renderers/DevHomeActionSet.cs
@@ -0,0 +1,133 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Collections.Generic;
+using AdaptiveCards.ObjectModel.WinUI3;
+using AdaptiveCards.Rendering.WinUI3;
+using DevHome.Common.Contracts;
+using Microsoft.UI.Xaml;
+
+namespace DevHome.Common.Renderers;
+
+///
+/// Represents whether the adaptive card button in an action set should be visible or hidden in Dev Home's UI.
+/// Although adaptive cards can natively hide elements, this is used on the Dev Home side to hide the action set
+/// in cases where a page in Dev Home wants its own button to invoke the action in the adaptive card.
+///
+public enum TopLevelCardActionSetVisibility
+{
+ Visible,
+ Hidden,
+}
+
+///
+/// Allows Dev Home to attach an action to a button in the Dev Home UI.
+/// Dev Home can use this to invoke an action from an adaptive card when
+/// a button within Dev Home is clicked. It allows Dev Home to link buttons from
+/// within its own UI the to top level action set in the adaptive card.
+///
+///
+/// It is expected that the adaptive card will have a top level action set with buttons that Dev Home can link to.
+///
+public class DevHomeActionSet : IDevHomeActionSetRender
+{
+ ///
+ /// Gets the visibility of the action in Dev Home's UI.
+ ///
+ private readonly TopLevelCardActionSetVisibility _actionSetVisibility;
+
+ ///
+ /// Gets the adaptive card object that will invoke an action within the action set.
+ ///
+ public AdaptiveActionInvoker? ActionButtonInvoker { get; private set; }
+
+ public AdaptiveCard? OriginalAdaptiveCard { get; private set; }
+
+ private Dictionary ActionButtonMap { get; } = new();
+
+ public DevHomeActionSet(TopLevelCardActionSetVisibility cardActionSetVisibility)
+ {
+ _actionSetVisibility = cardActionSetVisibility;
+ }
+
+ public UIElement? Render(IAdaptiveCardElement element, AdaptiveRenderContext context, AdaptiveRenderArgs renderArgs)
+ {
+ ActionButtonMap.Clear();
+
+ if (element is AdaptiveActionSet actionSet)
+ {
+ foreach (var action in actionSet.Actions)
+ {
+ if (action is AdaptiveExecuteAction executeAction)
+ {
+ context.LinkSubmitActionToCard(executeAction, renderArgs);
+ }
+ else if (action is AdaptiveSubmitAction submitAction)
+ {
+ context.LinkSubmitActionToCard(submitAction, renderArgs);
+ }
+
+ ActionButtonMap.TryAdd(action.Id, action);
+ }
+
+ ActionButtonInvoker = context.ActionInvoker;
+ OriginalAdaptiveCard = renderArgs.ParentCard;
+
+ if (_actionSetVisibility == TopLevelCardActionSetVisibility.Hidden)
+ {
+ // the page in Dev Home does not want to show the action set in the card.
+ // So we return null to prevent the adaptive card buttons from appearing.
+ // Invoking the button from Dev Home will then trigger the action in the adaptive card.
+ return null;
+ }
+ }
+
+ var renderer = new AdaptiveActionSetRenderer();
+ return renderer.Render(element, context, renderArgs);
+ }
+
+ ///
+ /// Invokes an adaptive card action from anywhere within Dev Home, like a method in a view Model for example.
+ /// A boolean is returned with the validation result of the card. We still send the action event so the adaptive
+ /// cards UI updates with error information.
+ ///
+ ///
+ /// A boolean indicating whether validation for the card passed or failed.
+ ///
+ public bool TryValidateAndInitiateAction(string buttonId, AdaptiveInputs userInputs)
+ {
+ ActionButtonMap.TryGetValue(buttonId, out var actionElement);
+
+ if ((actionElement == null) || (userInputs == null))
+ {
+ return false;
+ }
+
+ var result = userInputs.ValidateInputs(actionElement);
+
+ ActionButtonInvoker?.SendActionEvent(actionElement);
+ return result;
+ }
+
+ public void InitiateAction(string buttonId)
+ {
+ ActionButtonMap.TryGetValue(buttonId, out var actionElement);
+
+ if (actionElement == null)
+ {
+ return;
+ }
+
+ ActionButtonInvoker?.SendActionEvent(actionElement);
+ }
+
+ public string GetActionTitle(string buttonId)
+ {
+ if (!ActionButtonMap.TryGetValue(buttonId, out var action))
+ {
+ return string.Empty;
+ }
+
+ return action.Title;
+ }
+}
diff --git a/common/Renderers/ItemsViewChoiceSet.cs b/common/Renderers/ItemsViewChoiceSet.cs
new file mode 100644
index 0000000000..546543f1b0
--- /dev/null
+++ b/common/Renderers/ItemsViewChoiceSet.cs
@@ -0,0 +1,93 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using AdaptiveCards.ObjectModel.WinUI3;
+using AdaptiveCards.Rendering.WinUI3;
+using DevHome.Common.DevHomeAdaptiveCards.CardModels;
+using DevHome.Common.DevHomeAdaptiveCards.InputValues;
+using DevHome.Common.Helpers;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Automation;
+using Microsoft.UI.Xaml.Controls;
+
+namespace DevHome.Common.Renderers;
+
+///
+/// Renders a known Dev Home choice set as an ItemsView.
+///
+public class ItemsViewChoiceSet : IAdaptiveElementRenderer
+{
+ private readonly double _defaultSpacing = 5;
+
+ private readonly string? _choiceSetItemsTemplateName;
+
+ public ItemsViewChoiceSet(string itemsTemplateResourceName)
+ {
+ // set the template for the items view.
+ _choiceSetItemsTemplateName = itemsTemplateResourceName;
+ }
+
+ // Default template for the ItemsView will be used
+ public ItemsViewChoiceSet()
+ {
+ }
+
+ public UIElement Render(IAdaptiveCardElement element, AdaptiveRenderContext context, AdaptiveRenderArgs renderArgs)
+ {
+ // As we add more types of choice sets, we can add more cases here.
+ if (element is DevHomeSettingsCardChoiceSet settingsCardChoiceSet)
+ {
+ return GetItemsViewElement(settingsCardChoiceSet, context, renderArgs);
+ }
+
+ // Use default render for all other cases.
+ var renderer = new AdaptiveChoiceSetInputRenderer();
+ return renderer.Render(element, context, renderArgs);
+ }
+
+ private ItemsView GetItemsViewElement(DevHomeSettingsCardChoiceSet settingsCardChoiceSet, AdaptiveRenderContext context, AdaptiveRenderArgs renderArgs)
+ {
+ // Set default spacing for the ItemsView.
+ var choiceSetItemsView = new ItemsView();
+ var defaultLayout = new StackLayout();
+ defaultLayout.Spacing = _defaultSpacing;
+ choiceSetItemsView.Layout = defaultLayout;
+
+ // If there is a template for the items view, set it.
+ if (!string.IsNullOrEmpty(_choiceSetItemsTemplateName))
+ {
+ choiceSetItemsView.ItemTemplate = Application.Current.Resources[_choiceSetItemsTemplateName] as DataTemplate;
+ }
+
+ // Check if the choice set is multi-select, and if it is make sure the ItemsView is set to allow multiple selection.
+ if (settingsCardChoiceSet.IsMultiSelect)
+ {
+ choiceSetItemsView.SelectionMode = ItemsViewSelectionMode.Multiple;
+ }
+
+ // If selection is disabled, set the ItemsView to not allow selection of items in the items view.
+ if (settingsCardChoiceSet.IsSelectionDisabled)
+ {
+ choiceSetItemsView.SelectionMode = ItemsViewSelectionMode.None;
+ }
+
+ // Go through all the items in the choice set and make an item for each one.
+ for (var i = 0; i < settingsCardChoiceSet.SettingsCards.Count; i++)
+ {
+ var curCard = settingsCardChoiceSet.SettingsCards[i];
+ curCard.HeaderIconImage = AdaptiveCardHelpers.ConvertBase64StringToImageIcon(curCard.HeaderIcon);
+ }
+
+ // Set up the ItemsSource for the ItemsView and add the input value to the context.
+ // the input value is used to get the current index of the items view in relation
+ // to the item in the choice set.
+ choiceSetItemsView.ItemsSource = settingsCardChoiceSet.SettingsCards;
+
+ // Set the automation name of the list to be the label of the choice set.
+ context.AddInputValue(new ItemsViewInputValue(settingsCardChoiceSet, choiceSetItemsView), renderArgs);
+ AutomationProperties.SetName(choiceSetItemsView, settingsCardChoiceSet.Label);
+
+ // Return the ItemsView.
+ return choiceSetItemsView;
+ }
+}
diff --git a/common/Strings/en-us/Resources.resw b/common/Strings/en-us/Resources.resw
index 1873f71307..3cf7a21ab0 100644
--- a/common/Strings/en-us/Resources.resw
+++ b/common/Strings/en-us/Resources.resw
@@ -233,4 +233,48 @@
The current user is not a {0} administrators. {0} virtual machines will not load. Please add the user to the {0} administrators group and reboot.Locked="{0}" Text explaining that the user is not in the {0} administrators group and that we need to add them.
+
+ Open
+ Default text for a button that launches a content dialog in the UI
+
+
+ Ok
+ Default text for a content dialogs primary action button
+
+
+ Close
+ Default text for a content dialogs secondary action button
+
+
+ No title was provide by the provider
+ Default text when a provider does not provide Dev Home with a title for the dialog
+
+
+ Settings card items
+ Default text that will be displayed and read out to the reader if an extension does not provide us with label text for their list of values
+
+
+ Provider failed to provide a description for this item
+ Error text to be displayed when a provider does not provide a description for an item in a list of UI cards
+
+
+ Provider failed to provide a Header title for this item
+ Error text to be displayed when an provider does not provide header text for an item in a list of UI cards
+
+
+ Ok
+ Primary button text that will be displayed to the user as the main action when they are presented with a content dialog with two buttons
+
+
+ Cancel
+ Secondary button text that will be displayed to the user as a secondary action when they are presented with a content dialog with two buttons
+
+
+ Couldn't get title from the extension
+ Error text to be displayed to the user when a dev home extension that wants a content dialog to be displayed does not provide Dev Home with a title for the content dialog
+
+
+ An item must be selected
+ Error text advising the user that they must select an item in the list before proceeding
+
\ No newline at end of file
diff --git a/common/Views/AdaptiveCardViews/AdaptiveCardResourceTemplates.xaml b/common/Views/AdaptiveCardViews/AdaptiveCardResourceTemplates.xaml
new file mode 100644
index 0000000000..c5cc1f1a63
--- /dev/null
+++ b/common/Views/AdaptiveCardViews/AdaptiveCardResourceTemplates.xaml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/common/Views/AdaptiveCardViews/AdaptiveCardResourceTemplates.xaml.cs b/common/Views/AdaptiveCardViews/AdaptiveCardResourceTemplates.xaml.cs
new file mode 100644
index 0000000000..6b37b2171b
--- /dev/null
+++ b/common/Views/AdaptiveCardViews/AdaptiveCardResourceTemplates.xaml.cs
@@ -0,0 +1,14 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using Microsoft.UI.Xaml;
+
+namespace DevHome.Common.Views.AdaptiveCardViews;
+
+public partial class AdaptiveCardResourceTemplates : ResourceDictionary
+{
+ public AdaptiveCardResourceTemplates()
+ {
+ this.InitializeComponent();
+ }
+}
diff --git a/common/Views/AdaptiveCardViews/ContentDialogWithNonInteractiveContent.xaml b/common/Views/AdaptiveCardViews/ContentDialogWithNonInteractiveContent.xaml
new file mode 100644
index 0000000000..8f95bc8e41
--- /dev/null
+++ b/common/Views/AdaptiveCardViews/ContentDialogWithNonInteractiveContent.xaml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
diff --git a/common/Views/AdaptiveCardViews/ContentDialogWithNonInteractiveContent.xaml.cs b/common/Views/AdaptiveCardViews/ContentDialogWithNonInteractiveContent.xaml.cs
new file mode 100644
index 0000000000..23f78ba236
--- /dev/null
+++ b/common/Views/AdaptiveCardViews/ContentDialogWithNonInteractiveContent.xaml.cs
@@ -0,0 +1,38 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Threading.Tasks;
+using AdaptiveCards.Rendering.WinUI3;
+using DevHome.Common.DevHomeAdaptiveCards.CardModels;
+using DevHome.Common.Extensions;
+using DevHome.Common.Services;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using WinUIEx;
+
+namespace DevHome.Common.Views.AdaptiveCardViews;
+
+///
+/// Content dialog with non-interactive content within an adaptive card
+///
+public sealed partial class ContentDialogWithNonInteractiveContent : ContentDialog
+{
+ public ContentDialogWithNonInteractiveContent(DevHomeContentDialogContent content)
+ {
+ this.InitializeComponent();
+
+ // Since we use the renderer service to allow the card to receive theming updates, we need to ensure the UI thread is used.
+ var dispatcherQueue = Application.Current.GetService().DispatcherQueue;
+ dispatcherQueue.TryEnqueue(async () =>
+ {
+ Title = content.Title;
+ PrimaryButtonText = content.PrimaryButtonText;
+ var rendererService = Application.Current.GetService();
+ var renderer = await rendererService.GetRendererAsync();
+ renderer.HostConfig.ContainerStyles.Default.BackgroundColor = Microsoft.UI.Colors.Transparent;
+ var card = renderer.RenderAdaptiveCardFromJsonString(content.ContentDialogInternalAdaptiveCardJson?.Stringify() ?? string.Empty);
+ Content = card.FrameworkElement;
+ SecondaryButtonText = content.SecondaryButtonText;
+ });
+ }
+}