Skip to content

Commit 22db6b8

Browse files
committed
ci: add workflows and copilot instructions
1 parent 5283434 commit 22db6b8

File tree

9 files changed

+478
-0
lines changed

9 files changed

+478
-0
lines changed

.github/CODEOWNERS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# A CODEOWNERS file uses a pattern that follows the same rules used in gitignore files.
2+
# The pattern is followed by one or more GitHub usernames or team names using the
3+
# standard @username or @org/team-name format. You can also refer to a user by an
4+
# email address that has been added to their GitHub account, for example [email protected]
5+
6+
* @DevTKSS

.github/copilot-instructions.md

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
# Uno Platform MVUX Sample Apps - AI Coding Guide
2+
3+
## Project Context
4+
5+
This is a **German-localized** learning repository for **Uno Platform 6.3.28+** showcasing MVUX (Model-View-Update-eXtended) patterns, navigation, and Uno.Extensions. All apps use `.NET 9.0` with the `Uno.Sdk` (see `src/global.json`). Project context defaults to **Uno Platform, not MAUI**.
6+
7+
## Architecture & Patterns
8+
9+
### MVUX Models Convention
10+
- **All models are `partial record` types** named `*Model` (e.g., `DashboardModel`, `MainModel`). These are not the ViewModels themselves.
11+
- The bindable **ViewModel is auto-generated** from each `*Model` by the MVUX source generators at build time. You should never edit or depend on the generated files directly.
12+
- Models use constructor injection for services (DI via Uno.Extensions.Hosting)
13+
- **No `INotifyPropertyChanged`** - MVUX generates reactive bindings automatically
14+
- Expose state via `IFeed<T>`, `IListFeed<T>`, `IState<T>`, or `IListState<T>` properties
15+
- Models are **stateless** - focus on presentation logic, not state management
16+
17+
Generated ViewModels and analyzer notes:
18+
- During test builds or when stepping through in the debugger, you may see messages about a missing `BindableAttribute` on models; these are expected with MVUX source generation and can be ignored.
19+
- Do not add attributes or change patterns to “fix” these messages; the source generator handles the bindable surface. Never modify generated code under `obj/`.
20+
21+
Example pattern:
22+
```csharp
23+
public partial record DashboardModel
24+
{
25+
public IListFeed<GalleryImage> GalleryImages => ListFeed.Async(_service.GetDataAsync);
26+
public IState<string> SelectedItem => State<string>.Value(this, () => "defaultValue")
27+
.ForEach(SelectionChanged);
28+
}
29+
```
30+
31+
#### Feed vs State
32+
- **Feeds (`IFeed<T>`, `IListFeed<T>`)**: Read-only async data streams from services
33+
- Stateless, reactive sequences similar to `IObservable<T>`
34+
- Use for data you won't edit (e.g., server responses)
35+
- Example: `IListFeed<Person> People => ListFeed.Async(_service.GetPeopleAsync);`
36+
37+
- **States (`IState<T>`, `IListState<T>`)**: Stateful feeds with update capabilities
38+
- Replay current value + allow modifications
39+
- Use for editable data with two-way binding
40+
- Example: `IState<int> Counter => State.Value(this, () => 0);`
41+
- Update via: `await CounterState.UpdateAsync(v => v + 1, ct);`
42+
43+
### Navigation Architecture
44+
- **Routes defined in `App.xaml.cs`** via `RegisterRoutes()` using `ViewRegistry` and `RouteRegistry`
45+
- Navigation uses **`INavigator` service** (dependency-injected), **not `Frame.Navigate()`**
46+
- Region-based navigation: `Frame`, `ContentControl`, `NavigationView`, `ContentDialog`, `Flyout`, `Popup`
47+
- ViewMap associates Views with ViewModels: `new ViewMap<PageType, ModelType>()`
48+
- DataViewMap for data-driven routes: `new DataViewMap<Page, Model, DataType>()`
49+
- Nested routes: `new RouteMap("path", View: ..., Nested: [...], IsDefault: true, DependsOn: "parent")`
50+
- Use `IRouteNotifier` in models to observe route changes
51+
52+
Navigation patterns:
53+
```csharp
54+
// In App.xaml.cs RegisterRoutes
55+
views.Register(
56+
new ViewMap<MainPage, MainModel>(),
57+
new DataViewMap<DetailsPage, DetailsModel, Widget>()
58+
);
59+
60+
routes.Register(
61+
new RouteMap("", View: views.FindByViewModel<ShellModel>(),
62+
Nested: [
63+
new ("Main", View: views.FindByViewModel<MainModel>(), IsDefault: true),
64+
new ("Details", View: views.FindByViewModel<DetailsModel>(), DependsOn: "Main")
65+
]
66+
)
67+
);
68+
69+
// In Model - inject INavigator
70+
public partial record MainModel(INavigator Navigator)
71+
{
72+
public async Task NavigateToDetails(Widget widget, CancellationToken ct)
73+
=> await Navigator.NavigateDataAsync(this, widget, cancellation: ct);
74+
}
75+
```
76+
77+
### Project Structure
78+
79+
> **Default structure:** Place all Views and Models in the `/Presentation` folder. Only if the app grows larger, add further subfolders (as seen in MvuxGallery) within `/Presentation` to keep the structure organized and concise.
80+
81+
```
82+
src/
83+
├── DevTKSS.Uno.Samples.MvuxGallery/ # Main gallery app
84+
│ ├── Presentation/
85+
│ │ ├── ViewModels/*Model.cs # MVUX partial records
86+
│ │ ├── Views/*Page.xaml # Pages (not Views/)
87+
│ │ ├── Shell.xaml # Main navigation shell
88+
│ ├── Models/ # Domain models & services
89+
│ ├── appsettings.json # Config sections (AppConfig, Localization)
90+
│ ├── appsettings.sampledata.json # Sample data for code examples
91+
├── DevTKSS.Extensions.Uno.Storage/ # Custom storage extensions
92+
├── global.json # Uno.Sdk version (6.3.28)
93+
├── Directory.Packages.props # Central package management
94+
```
95+
96+
## Key Conventions
97+
98+
### UnoFeatures in .csproj
99+
Apps declare capabilities via `<UnoFeatures>`: Material, MVUX, Navigation, Hosting, Configuration, Localization, Serialization, Storage, ThemeService. **Do not manually add implicit package references** - they're managed by Uno.Sdk.
100+
101+
### Configuration Pattern
102+
- Load sections from `appsettings.json` using `.EmbeddedSource<App>().Section<TConfig>()`
103+
- Keyed services for multiple code sample collections: `.AddKeyedSingletonCodeService("SampleName")`
104+
- Inject via `[FromKeyedServices("key")]` attribute
105+
106+
### XAML Binding
107+
108+
### Critical Troubleshooting: Never Remove `this.InitializeComponent()`
109+
### Code Editing Rule: Never Remove or Replace Required Lines with Placeholders
110+
111+
> **🚨 DO NOT use `// ...existing code...` or any similar placeholder to remove or replace required framework or initialization lines (especially `this.InitializeComponent()`).**
112+
113+
- When editing, always preserve all required initialization and framework lines; do not replace them with comments, ellipses, or placeholders.
114+
- If you are unsure about a line's necessity, **leave it unchanged** and investigate the real cause of the error.
115+
- Never remove or replace `this.InitializeComponent()` or similar required lines, even if other inserted code would otherwise be correct.
116+
117+
> **🚨 ABSOLUTE RULE: Never remove `this.InitializeComponent()` from `App.xaml.cs` or any `*Page.xaml.cs` file.**
118+
119+
- If you see errors or build failures related to `this.InitializeComponent()`, **do NOT delete or comment out this line**. Removing it will break the app and prevent any XAML from loading.
120+
- Instead, always check that:
121+
- The namespaces in your `*Page.xaml`, `*Page.xaml.cs`, and corresponding `*Model.cs` files are in sync and correct.
122+
- All Views and Models are properly registered in `App.xaml.cs` using the navigation/DI system.
123+
- There are no typos or mismatches in file/class names or XAML root element names.
124+
- If you are troubleshooting DI/HostBuilder/service registration issues, **never fix by removing or altering `this.InitializeComponent()`**. The problem is almost always a registration, namespace, or XAML mismatch elsewhere.
125+
126+
> **If Copilot suggests removing or altering `this.InitializeComponent()`, this is always incorrect.**
127+
- **Always use `{Binding}` (not `{x:Bind}`) when binding anything exposed by a `*Model`** (Feeds and States). The MVUX ViewModel is generated and provided at runtime as the `DataContext`; using `{x:Bind}` here commonly leads to NullReferenceExceptions.
128+
- `FeedView` wraps async data: `<mvux:FeedView Source="{Binding GalleryImages}">` with `ValueTemplate`
129+
- Access parent model in templates: `{Binding Parent.PropertyName}`
130+
- Refresh commands: `{utu:AncestorBinding AncestorType=mvux:FeedView, Path=Refresh}`
131+
132+
133+
#### Views and code-behind (no ViewModel ctor/fields, no Page constructor arguments)
134+
- **Page constructors must have NO arguments** when the project uses `<UnoFeatures>...Navigation</UnoFeatures>` and navigation is registered in `App.xaml.cs` via Uno.Extensions. The navigation and DI system will only instantiate Pages using the default parameterless constructor. If you add any arguments (e.g., `MainPage(MainViewModel vm)` or `MainPage(IService svc)`), navigation will fail and the Page will not be created.
135+
- Do not inject or expect the MVUX-generated `*ViewModel` in a Page constructor, and do not rely on `DataContextChanged` to grab it early. The `INavigator` sets the `DataContext` after the view initializes; trying to access it early (or via TwoWay `{x:Bind}` with backing fields) will cause `NullReferenceException` and crash.
136+
- Avoid creating backing properties/fields in code-behind that expect the ViewModel to exist during `InitializeComponent`. Prefer pure XAML `{Binding}` to MVUX feeds/states exposed by the corresponding `*Model`.
137+
138+
#### Selection with IListState (ListView/GridView)
139+
- When binding a `ListView` or `GridView` to an `IListState<T>` that uses the `.Selection(...)` operator, do **not** attach `Command`, `ItemClickCommand`, or `SelectionChanged` handlers on the control at the same time. Doing so prevents the MVUX selection pipeline from invoking the `.Selection(...)` operator.
140+
- Correct pattern:
141+
- Model:
142+
```csharp
143+
public partial record PeopleModel(IPeopleService Service)
144+
{
145+
public IListFeed<Person> People => ListFeed.Async(Service.GetPeopleAsync)
146+
.Selection(SelectedPerson);
147+
public IState<Person?> SelectedPerson => State.Value(this, () => default(Person?));
148+
}
149+
```
150+
- XAML:
151+
```xml
152+
<mvux:FeedView Source="{Binding People}">
153+
<DataTemplate>
154+
<ListView ItemsSource="{Binding Data}" SelectionMode="Single"/>
155+
</DataTemplate>
156+
</mvux:FeedView>
157+
```
158+
- Note: Avoid setting `ItemClick`, `IsItemClickEnabled`, `ItemClickCommand`, or `SelectionChanged` command bindings on the list control when using `.Selection(...)` on the bound `IListFeed/IListState`.
159+
160+
### Localization
161+
- Supported cultures in `appsettings.json`: `LocalizationConfiguration.Cultures`
162+
- Inject `IStringLocalizer` for translated strings
163+
- Documentation exists in `docs/articles/en/` and `docs/articles/de/`
164+
165+
## Build & Development
166+
167+
### Commands
168+
```powershell
169+
# Build with solution filters for specific apps
170+
dotnet build src/DevTKSS.Uno.SampleApps-GalleryOnly.slnf
171+
dotnet build src/DevTKSS.Uno.SampleApps-Tutorials.slnf
172+
173+
# Documentation (DocFX)
174+
./docs/Build-Docs.ps1 # Build docs to _site
175+
./docs/Clean-ApiDocs.ps1 # Clean generated API docs
176+
```
177+
178+
### VS Code and Visual Studio notes
179+
- In VS Code, keep `.vscode/tasks.json` in sync with solution changes (added/removed projects), or build tasks may fail. If projects change and tasks arent updated, update the tasks to point to the correct `.sln`/`.slnf` or project.
180+
- In Visual Studio 2022+, verify `src/[ProjectName]/Properties/launchSettings.json` when adding/removing targets or tweaking profiles so F5/run profiles match current TFMs.
181+
182+
### Known Issues
183+
1. **Windows target disabled** in MvuxGallery Issue [#15](https://github.com/DevTKSS/DevTKSS.Uno.SampleApps/issues/15): ResourcesDictionary import bug prevents building
184+
2. **Theme changes** not reactive for ThemeResource styles Issue [#13](https://github.com/DevTKSS/DevTKSS.Uno.SampleApps/issues/13)
185+
3. **DocFX source links** fail for `[!INCLUDE]` markup - uses workaround includes instead of redirects
186+
187+
#### Windows target and ResourceDictionaries
188+
- Current limitation: the MvuxGallery app cannot build with the Windows target when using external `Styles/*.xaml` ResourceDictionary files (see repository issue about this limitation). If you need the Windows target and centralized DataTemplates, define them directly inside `App.xaml` instead of separate dictionary files.
189+
190+
### Warnings Suppressed
191+
- `NU1507`: Multiple package sources with CPM
192+
- `NETSDK1201`: RID won't create self-contained app
193+
- `PRI257`: Default language (en) vs resources (en-us)
194+
195+
## Sample App Specifics
196+
197+
### MvuxGallery Features
198+
- **FeedView + GridView/ListView** patterns with ItemOverlayTemplate
199+
- Centralized DataTemplates in `Styles/GalleryTemplates.xaml`
200+
- Code sample viewer using `IStorage.ReadPackageFileAsync()` from Assets
201+
- TabBar navigation, NavigationView structure
202+
- Custom extensions: `DevTKSS.Extensions.Uno.Storage` for line-range file reading
203+
204+
### XamlNavigationApp
205+
- Tutorial-focused app for XAML markup navigation
206+
- Demonstrates MVUX + Navigation combined patterns
207+
- Bilingual README files: `ReadMe.en.md`, `ReadMe.de.md`
208+
209+
## Contributing Context
210+
- Primary language: German (documentation available in EN/DE)
211+
- Video tutorials on YouTube (German with English subtitles)
212+
- Apache License 2.0
213+
- Use GitHub Discussions for questions, Issues for bugs
214+
215+
## Uno Platform Context
216+
217+
### Important Notes
218+
- This uses **Uno.Sdk** (not WinAppSDK/WinUI directly)
219+
- **Not MAUI** - uses .NET mobile bindings directly
220+
- Targets: iOS/iPadOS, Android, macOS, Windows, Linux, WebAssembly
221+
- Skia (canvas) and Native (native elements) renderers available
222+
- Free C# and XAML Hot Reload support
223+
224+
### MVUX Specifics
225+
- **FeedView control** wraps async data with loading/error states
226+
- `Source="{Binding Feed}"` binds to IFeed/IState
227+
- `ValueTemplate` for successful data display
228+
- `ErrorTemplate` and `ProgressTemplate` for states
229+
- `{Binding Data}` accesses feed value in template
230+
- `{Binding Refresh}` command triggers feed refresh
231+
- Feeds are **awaitable**: `var data = await this.MyFeed;`
232+
- BindableViewModel auto-generated with naming pattern `*Model` → `*ViewModel`
233+
- Use `[ReactiveBindable]` attribute to customize code generation
234+
235+
### XAML Best Practices
236+
- Prefer `{Binding}` over `{x:Bind}` for MVUX feeds (runtime-reactive)
237+
- Use `{utu:AncestorBinding}` from Uno.Toolkit for parent access
238+
- Centralize DataTemplates in ResourceDictionaries (see `Styles/GalleryTemplates.xaml`)
239+
- FeedView `State` property auto-set as DataContext for templates

.github/dependabot.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: "nuget"
4+
directory: "/"
5+
schedule:
6+
interval: weekly
7+
target-branch: master
8+
open-pull-requests-limit: 10
9+
labels: ["dependencies", "nuget"]
10+
commit-message:
11+
prefix: "chore"
12+
include: scope
13+
pull-request-branch-name:
14+
separator: "-"
15+
groups:
16+
minor-and-patch:
17+
patterns:
18+
- "*"
19+
update-types:
20+
- minor
21+
- major
22+
23+
- package-ecosystem: "github-actions"
24+
directory: "/"
25+
schedule:
26+
interval: weekly
27+
target-branch: master
28+
labels: ["dependencies", "github-actions"]
29+
commit-message:
30+
prefix: "chore"
31+
include: scope

.github/labeler.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Documentation
2+
documentation:
3+
- changed-files:
4+
- any-glob-to-any-file: 'docs/**/*'
5+
- any-glob-to-any-file: '**/*.md'
6+
7+
# Source Code
8+
source:
9+
- changed-files:
10+
- any-glob-to-any-file: 'src/**/*.cs'
11+
- any-glob-to-any-file: 'src/**/*.xaml'
12+
13+
# Extensions
14+
extensions:
15+
- changed-files:
16+
- any-glob-to-any-file: 'src/DevTKSS.Extensions.Uno/**/*'
17+
- any-glob-to-any-file: 'src/DevTKSS.Extensions.Uno.Storage/**/*'
18+
19+
# Sample Apps
20+
samples:
21+
- changed-files:
22+
- any-glob-to-any-file: 'src/DevTKSS.Uno.MvuxListApp/**/*'
23+
- any-glob-to-any-file: 'src/DevTKSS.Uno.Samples.MvuxGallery/**/*'
24+
- any-glob-to-any-file: 'src/DevTKSS.Uno.XamlNavigationApp-1/**/*'
25+
26+
# CI/CD
27+
ci-cd:
28+
- changed-files:
29+
- any-glob-to-any-file: '.github/**/*'
30+
31+
# Dependencies
32+
dependencies:
33+
- changed-files:
34+
- any-glob-to-any-file: 'src/Directory.Packages.props'
35+
- any-glob-to-any-file: 'src/global.json'
36+
37+
# Tests
38+
tests:
39+
- changed-files:
40+
- any-glob-to-any-file: 'src/Tests/**/*'

0 commit comments

Comments
 (0)