-
Notifications
You must be signed in to change notification settings - Fork 378
Add maui-coding-guardrails and maui-current-apis skills #379
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
jfversluis
wants to merge
10
commits into
dotnet:main
from
jfversluis:dev/jfversluis/maui-guardrails-skills
Closed
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
109f623
Add maui-coding-guardrails and maui-current-apis skills
jfversluis 19b1b14
Address review feedback: tighten eval assertions and remove forward r…
jfversluis 9ee88d9
Rewrite guardrail skills based on multi-model review
jfversluis 651fc87
Restore inline API tables in maui-current-apis
jfversluis 0bfb28f
Address jonathanpeppers feedback: shorten descriptions, emphasize Fra…
jfversluis f35dc15
Remove unused deprecated-apis reference file
jfversluis bbadc2d
Reduce skill token count ~40% while preserving all meaning
jfversluis b6eb810
Rework eval scenarios to maximize verdict pass rates
jfversluis 868e908
Compress skills and delete reference file to reduce token cost
jfversluis b5deaaf
Merge branch 'main' into dev/jfversluis/maui-guardrails-skills
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| { | ||
| "name": "dotnet-maui", | ||
| "version": "0.1.0", | ||
| "description": "Skills for .NET MAUI development: environment setup, diagnostics, and troubleshooting.", | ||
| "description": "Skills for .NET MAUI development: environment setup, diagnostics, coding guardrails, and API currency.", | ||
| "skills": ["./skills/"] | ||
| } |
101 changes: 101 additions & 0 deletions
101
plugins/dotnet-maui/skills/maui-coding-guardrails/SKILL.md
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| --- | ||
| name: maui-coding-guardrails | ||
| description: >- | ||
| Guardrails for .NET MAUI layouts, controls, and handlers. USE FOR: any MAUI | ||
| code generation/review. NOT FOR: API deprecations (maui-current-apis) or | ||
| environment setup (dotnet-maui-doctor). | ||
| --- | ||
|
|
||
| # .NET MAUI Coding Guardrails | ||
|
|
||
| Apply to all MAUI code generation and editing. For API replacements, use `maui-current-apis`. | ||
|
|
||
| ## Layout Rules | ||
|
|
||
| **Don't put ScrollView/CollectionView inside StackLayout.** | ||
| StackLayout gives children infinite height — ScrollView won't scroll, CollectionView loses virtualization. Use Grid. | ||
|
|
||
| ```xml | ||
| <!-- ❌ StackLayout gives infinite height --> | ||
| <StackLayout> | ||
| <ScrollView>...</ScrollView> | ||
| </StackLayout> | ||
|
|
||
| <!-- ✅ Grid constrains height --> | ||
| <Grid RowDefinitions="Auto,*,Auto"> | ||
| <Label Text="Header" /> | ||
| <ScrollView Grid.Row="1">...</ScrollView> | ||
| <Button Grid.Row="2" Text="Submit" /> | ||
| </Grid> | ||
| ``` | ||
|
|
||
| **Prefer `VerticalStackLayout`/`HorizontalStackLayout` over `StackLayout`** — avoids legacy Orientation check each measure pass. | ||
|
|
||
| **Don't use `AndExpand` options.** No-ops in MAUI; use Grid row/column sizing. | ||
|
|
||
| **Flatten nested layouts.** Each nesting level adds measure/arrange cost; prefer flat Grids. | ||
|
|
||
| ## Control Rules | ||
|
|
||
| ### ⚠️ DO NOT USE `Frame` — Use `Border` Instead | ||
|
|
||
| `Frame` is Xamarin.Forms legacy. `Border` supports `StrokeShape`, custom strokes. Keep `Frame` only for `HasShadow`. | ||
|
|
||
| ```xml | ||
| <Border StrokeShape="RoundRectangle 10" Stroke="Gray" StrokeThickness="1" Padding="12"> | ||
| <Label Text="Content" /> | ||
| </Border> | ||
| ``` | ||
|
|
||
| **Use `CollectionView` over `ListView`.** ListView and all cell types deprecated in .NET 10. For ≤20 items, use `BindableLayout`. | ||
|
|
||
| **Use `Background` over `BackgroundColor`.** Accepts Color and Brush (gradients, images). | ||
|
|
||
| **Reference images as `.png`, not `.svg`.** SVGs compile to PNG; `.svg` fails at runtime. | ||
|
|
||
| ## Navigation Rules | ||
|
|
||
| **Don't mix Shell with NavigationPage/TabbedPage/FlyoutPage.** Shell has its own stack; wrapping in NavigationPage creates competing stacks, corruption, double headers. Pick one paradigm. | ||
|
|
||
| **Set `App.MainPage` once.** Use Shell routing or `NavigationPage.PushAsync` after. Changing MainPage leaks pages/handlers. | ||
|
|
||
| ## Handler Architecture | ||
|
|
||
| Use **handlers** and Mapper methods, not renderers. Renderers are Xamarin.Forms-only. | ||
|
|
||
| ```csharp | ||
| EntryHandler.Mapper.AppendToMapping("NoBorder", (handler, view) => | ||
| { | ||
| #if ANDROID | ||
| handler.PlatformView.SetBackgroundColor(Android.Graphics.Color.Transparent); | ||
| #elif IOS | ||
| handler.PlatformView.BorderStyle = UIKit.UITextBorderStyle.None; | ||
| #endif | ||
| }); | ||
| ``` | ||
|
|
||
| `AppendToMapping` runs after defaults, `PrependToMapping` before, `ModifyMapping` wraps one property. | ||
|
|
||
| ## Compiled Bindings | ||
|
|
||
| Declare `x:DataType` on pages and DataTemplates — without it, bindings use slow reflection (8–20×) and typos fail silently. | ||
|
|
||
| ```xml | ||
| <ContentPage x:DataType="viewmodels:MainViewModel"> | ||
| <CollectionView ItemsSource="{Binding Items}"> | ||
| <CollectionView.ItemTemplate> | ||
| <DataTemplate x:DataType="viewmodels:ItemViewModel"> | ||
| <Label Text="{Binding Name}" /> | ||
| </DataTemplate> | ||
| </CollectionView.ItemTemplate> | ||
| </CollectionView> | ||
| </ContentPage> | ||
| ``` | ||
|
|
||
| ## Common Pitfalls | ||
|
|
||
| | Pitfall | Fix | | ||
| |---------|-----| | ||
| | Gesture on parent+child — parent intercepts | `InputTransparent="True"` on overlay or restructure ownership | | ||
| | Unsubscribed events — pages leak | Unsubscribe in `OnDisappearing` or use `WeakReferenceMessenger` | | ||
| | Only testing on emulators | Test on physical devices — emulators hide perf/gesture issues | | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| --- | ||
| name: maui-current-apis | ||
| description: >- | ||
| Tracks deprecated/removed APIs in .NET MAUI 8/9/10, MauiReactor, Blazor | ||
| Hybrid. USE FOR: generating/reviewing MAUI code, fixing deprecation warnings. | ||
| NOT FOR: layout patterns (maui-coding-guardrails) or setup (dotnet-maui-doctor). | ||
| --- | ||
|
jfversluis marked this conversation as resolved.
|
||
|
|
||
| # .NET MAUI Current APIs | ||
|
|
||
| Prevents generating code with deprecated or removed APIs. | ||
|
|
||
| **Before generating:** Read `.csproj` TFM and package versions. API availability varies by .NET version; don't suggest .NET 10 APIs for `net8.0`. | ||
|
|
||
| ## Key Rules | ||
|
|
||
| 1. **Read `.csproj` first**; don't assume target version. | ||
| 2. **Prefer newer APIs** available for the detected version. | ||
| 3. **`Xamarin.*` don't exist in MAUI** — won't compile. | ||
| 4. **Avoid `Compatibility.*`** — Xamarin.Forms layout logic, migration aid only. | ||
| 5. **`Device` class deprecated** — split into services (see table). | ||
| 6. **Use `*Async` in .NET 10+** — animation/dialog methods renamed. | ||
| 7. **Check package versions** — CommunityToolkit/MauiReactor break between majors. | ||
|
|
||
| --- | ||
|
|
||
| ## Deprecated APIs — .NET MAUI 10 | ||
|
|
||
| ### Controls | ||
|
|
||
|
jfversluis marked this conversation as resolved.
|
||
| | ❌ Deprecated | ✅ Replacement | Why | | ||
| |---------------|----------------|-----| | ||
| | `ListView` | `CollectionView` | Deprecated in .NET 10 with all cell types | | ||
|
Comment on lines
+31
to
+33
|
||
| | `TableView` | `CollectionView` or custom layout | Deprecated in .NET 10 | | ||
| | `Frame` | `Border` | Legacy; Border supports StrokeShape | | ||
| | `Compatibility.RelativeLayout` | `Grid` | Migration-only | | ||
| | `Compatibility.StackLayout` | `VerticalStackLayout`/`HorizontalStackLayout` | Xamarin layout logic | | ||
|
|
||
| ### Gestures | ||
|
|
||
| | ❌ Deprecated | ✅ Replacement | | ||
| |---------------|----------------| | ||
| | `ClickGestureRecognizer` | `TapGestureRecognizer` | | ||
| | `Accelerator` | `KeyboardAccelerator` | | ||
|
|
||
| ### Navigation | ||
|
|
||
| | ❌ Deprecated | ✅ Replacement | Why | | ||
| |---------------|----------------|-----| | ||
| | `Page.IsBusy` | `ActivityIndicator` | Obsolete .NET 10 | | ||
| | `DisplayAlert()` | `DisplayAlertAsync()` | Async rename | | ||
| | `DisplayActionSheet()` | `DisplayActionSheetAsync()` | Same | | ||
| | `MessagingCenter` | `WeakReferenceMessenger` (CommunityToolkit.Mvvm) | Internal .NET 10; leaked subscriptions | | ||
|
|
||
| ### Animation (*Async renames in .NET 10) | ||
|
|
||
| | ❌ Old | ✅ New | | ||
| |--------|--------| | ||
| | `FadeTo()`, `RotateTo()`, `ScaleTo()`, `TranslateTo()` | `FadeToAsync()`, `RotateToAsync()`, `ScaleToAsync()`, `TranslateToAsync()` | | ||
| | `RelRotateTo()`, `RelScaleTo()`, `LayoutTo()` | `RelRotateToAsync()`, `RelScaleToAsync()`, `LayoutToAsync()` | | ||
|
|
||
| ### Device APIs (class split into focused services) | ||
|
|
||
| | ❌ Deprecated | ✅ Replacement | | ||
| |---------------|----------------| | ||
| | `Device.RuntimePlatform` | `DeviceInfo.Platform` | | ||
| | `Device.BeginInvokeOnMainThread()` | `MainThread.BeginInvokeOnMainThread()` | | ||
| | `Device.OpenUri()` | `Launcher.OpenAsync()` | | ||
| | `Device.StartTimer()` | `Dispatcher.StartTimer()` or `PeriodicTimer` | | ||
| | `DependencyService` | Constructor injection via `builder.Services` | | ||
|
|
||
| ### Other | ||
|
|
||
| | ❌ Deprecated | ✅ Replacement | | ||
| |---------------|----------------| | ||
| | `Color.FromHex()` | `Color.FromArgb()` | | ||
| | `Page.UseSafeArea` / `Layout.IgnoreSafeArea` | `SafeAreaEdges` property (.NET 10) | | ||
| | `AutomationProperties.Name`/`.HelpText` | `SemanticProperties.Description`/`.Hint` | | ||
|
|
||
| ### NuGet Packages | ||
|
|
||
| | ❌ Old | ✅ New | | ||
| |--------|--------| | ||
| | `Xamarin.Forms` | `Microsoft.Maui.Controls` | | ||
| | `Xamarin.Essentials` | Built-in MAUI APIs | | ||
| | `Xamarin.CommunityToolkit` | `CommunityToolkit.Maui` | | ||
| | `Microsoft.Toolkit.Mvvm` | `CommunityToolkit.Mvvm` | | ||
|
|
||
| --- | ||
|
|
||
| ## MauiReactor v3+ (.NET MAUI 9/10) | ||
|
|
||
| - **Hot reload**: feature switch in `.csproj`, not v2 `EnableMauiReactorHotReload()`. | ||
| - **State**: `State<T>`/`Props<T>`, not `RxComponent`. | ||
| - **Navigation**: use MauiReactor nav; avoid mixing Shell `GoToAsync`. | ||
|
|
||
| ## Blazor Hybrid | ||
|
|
||
| - Prefer `BlazorWebView`, not `WebView`. | ||
| - JS interop: `IJSRuntime.InvokeAsync<T>()` — sync deadlocks on mobile. | ||
| - Safe areas: CSS `env(safe-area-inset-*)`; don't combine with XAML `SafeAreaEdges`. | ||
|
|
||
| ## Version Detection | ||
|
|
||
| | TFM | .NET | MAUI | CommunityToolkit.Maui | | ||
| |-----|------|------|-----------------------| | ||
| | `net10.0-*` | 10 | 10 | v11+ | | ||
| | `net9.0-*` | 9 | 9 | v9-10 | | ||
| | `net8.0-*` | 8 (LTS) | 8 | v5-7 | | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| scenarios: | ||
| - name: "Prevent ListView usage in new MAUI app" | ||
| prompt: | | ||
| I'm building a new .NET MAUI app and I need to display a list of products. | ||
| Each product has a name, price, and image. I want to show them in a scrollable | ||
| list with a search bar at the top. Can you give me the XAML for this? | ||
| assertions: | ||
| - type: "output_contains" | ||
| value: "CollectionView" | ||
| - type: "output_not_contains" | ||
| value: "<ListView" | ||
|
jfversluis marked this conversation as resolved.
|
||
| - type: "output_matches" | ||
| pattern: "x:DataType" | ||
| - type: "output_not_contains" | ||
| value: "<Frame" | ||
| - type: "output_not_contains" | ||
| value: "<TableView" | ||
| rubric: | ||
| - "Uses CollectionView instead of ListView for the product list" | ||
| - "Declares x:DataType for compiled bindings on DataTemplate or page" | ||
| - "Does not use any deprecated controls (ListView, TableView, Frame)" | ||
| - "Uses Grid or VerticalStackLayout as the parent container, not StackLayout wrapping a ScrollView" | ||
| timeout: 120 | ||
|
|
||
| - name: "Fix AndExpand layout that does nothing" | ||
| prompt: | | ||
| My .NET MAUI button isn't expanding to fill the remaining space at the | ||
| bottom of the screen. I tried this but it doesn't work: | ||
|
|
||
| ```xml | ||
| <VerticalStackLayout> | ||
| <Label Text="Title" FontSize="24" /> | ||
| <Entry Placeholder="Enter text" /> | ||
| <Button Text="Submit" VerticalOptions="FillAndExpand" /> | ||
| </VerticalStackLayout> | ||
| ``` | ||
|
|
||
| Why doesn't the button fill the rest of the screen? | ||
| assertions: | ||
| - type: "output_contains" | ||
| value: "Grid" | ||
| - type: "output_matches" | ||
| pattern: "(?i)no.op|does nothing|ignored|no effect|doesn.t work|not supported|undefined" | ||
| rubric: | ||
| - "Explains that AndExpand/FillAndExpand is a no-op in MAUI (silently does nothing)" | ||
| - "Recommends Grid with star-sized rows as the correct approach" | ||
| - "Shows a working Grid example with RowDefinitions" | ||
| - "Does not suggest VerticalOptions alone will solve the problem" | ||
| timeout: 120 | ||
|
|
||
| - name: "Replace Frame with Border in card layout" | ||
| prompt: | | ||
| I need a card-style UI component for my .NET MAUI app with rounded corners, | ||
| a border, and some padding. Show me the XAML. | ||
| assertions: | ||
| - type: "output_contains" | ||
| value: "Border" | ||
| - type: "output_matches" | ||
| pattern: "(?i)StrokeShape|RoundRectangle" | ||
| rubric: | ||
| - "Uses Border instead of Frame for the card component" | ||
| - "Uses StrokeShape with RoundRectangle for rounded corners" | ||
| - "If Frame is mentioned at all, it is explicitly called out as legacy" | ||
| - "Uses Background property instead of BackgroundColor" | ||
| timeout: 120 | ||
|
|
||
| - name: "Handler customization instead of renderer" | ||
| prompt: | | ||
| I need to customize the Picker control in my .NET MAUI app to change the | ||
| dropdown arrow color on Android and remove the underline on iOS. My colleague | ||
| said I need a custom renderer with [assembly: ExportRenderer]. Show me how. | ||
| assertions: | ||
| - type: "output_matches" | ||
| pattern: "(?i)handler|mapper" | ||
| - type: "output_not_contains" | ||
| value: "ExportRenderer" | ||
| - type: "output_matches" | ||
| pattern: "AppendToMapping|PrependToMapping|ModifyMapping" | ||
| rubric: | ||
| - "Redirects from custom renderer to handler/mapper approach" | ||
| - "Explains that ExportRenderer/renderers don't exist in MAUI" | ||
| - "Shows platform-specific code using #if ANDROID / #elif IOS preprocessor directives" | ||
| - "Uses AppendToMapping, PrependToMapping, or ModifyMapping method" | ||
| timeout: 120 | ||
|
|
||
| - name: "Image reference and Background property guidance" | ||
| prompt: | | ||
| I'm adding a logo to my .NET MAUI app. I put logo.svg in Resources/Images/. | ||
| Also, I want the containing layout to have a light blue background: | ||
|
|
||
| ```xml | ||
| <VerticalStackLayout BackgroundColor="LightBlue"> | ||
| <Image Source="logo.svg" /> | ||
| </VerticalStackLayout> | ||
| ``` | ||
|
|
||
| Is this correct? | ||
| assertions: | ||
| - type: "output_contains" | ||
| value: ".png" | ||
| - type: "output_matches" | ||
| pattern: "(?i)Background(?!Color)" | ||
| rubric: | ||
| - "Instructs to reference the image as .png, not .svg" | ||
| - "Explains that SVGs are converted to PNGs at build time" | ||
| - "Recommends Background property over BackgroundColor" | ||
| - "Explains Background accepts both Color and Brush (gradients, images)" | ||
| timeout: 120 | ||
Oops, something went wrong.
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This table uses
||at the start of each row, which adds an unintended empty first column in Markdown. Use a single leading|instead (and update the other tables in this file that follow the same pattern).