Skip to main content

shiny-maui-hosting

Generate and configure Shiny MAUI Hosting for .NET - modular MAUI app configuration with IMauiModule, static ShinyHost.Services access, IAppSupport (device info + orientation/culture/timezone change events + programmatic orientation lock), IAppStore (cross-platform store version lookups and deep links for the Apple/Mac App Store, Google Play, Microsoft Store and Linux Flatpak/Snap), and IStartupService (install the app into the desktop OS launch-at-login list on Windows, macOS, and Linux - on Linux either as a GUI app via XDG autostart / a graphical-session systemd user unit, or as a headless systemd user/system service). Covers the dotnet/maui-labs desktop backends - macOS AppKit (net10.0-macos) and Linux GTK4 via the companion Shiny.Extensions.MauiHosting.Linux package

インストールへ移動

ソース情報

リポジトリ
shinyorg/extensions
ソースの最終更新活動
2026年9月19日 19:51
検出された SKILL.md の言語
英語
スター
7
フォーク
1

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
shiny-maui-hosting
description
Generate and configure Shiny MAUI Hosting for .NET - modular MAUI app configuration with IMauiModule, static ShinyHost.Services access, IAppSupport (device info + orientation/culture/timezone change events + programmatic orientation lock), IAppStore (cross-platform store version lookups and deep links for the Apple/Mac App Store, Google Play, Microsoft Store and Linux Flatpak/Snap), and IStartupService (install the app into the desktop OS launch-at-login list on Windows, macOS, and Linux - on Linux either as a GUI app via XDG autostart / a graphical-session systemd user unit, or as a headless systemd user/system service). Covers the dotnet/maui-labs desktop backends - macOS AppKit (net10.0-macos) and Linux GTK4 via the companion Shiny.Extensions.MauiHosting.Linux package
auto_invoke
true
triggers
["IMauiModule","Shiny.Extensions.MauiHosting","ShinyHost.Services","IAppSupport","IAppStore","AppStoreOptions","AppStoreResult","AddAppSupport","AddAppStore","AddInfrastructureModules","OrientationChanged","CultureChanged","TimeZoneChanged","SetOrientation","ResetOrientation","IStartupService","AddStartupService","StartupServiceOptions","StartupServiceState","run at startup","launch at login","login item","autostart","systemd","systemd service","LinuxStartupMode","run as a service on Linux","AppKit","net10.0-macos","UseMauiAppMacOS","AddMacOSEssentials","Shiny.Extensions.MauiHosting.Linux","[Truncated]"]
# Shiny MAUI Hosting Skill You are an expert in Shiny Extensions MAUI Hosting, a .NET library providing modular MAUI app configuration via `IMauiModule`, a static service provider accessor, an `IAppSupport` service for device info and orientation/culture/timezone change detection, an `IAppStore` service for cross-platform store info and deep links, and an `IStartupService` for desktop launch-at-login registration. Platform lifecycle hooks (`IIosLifecycle.*`, `IAndroidLifecycle.*`, `IMacLifecycle.*`) are wired automatically by `UseShiny()` from `Shiny.Hosting.Maui` — they are not handled by this library. ## When to Use This Skill Invoke this skill when the user wants to: - Create MAUI hosting modules with `IMauiModule` - Access the service provider via `ShinyHost.Services` - React to orientation, culture, or time-zone changes via `IAppSupport` - Programmatically lock or reset device orientation - Check store version / deep-link to store / launch a review page via `IAppStore` - Install or remove the app from the desktop OS startup (launch at login) list via `IStartupService` ## Library Overview **Documentation**: https://shinylib.net/mauihost/ **Repository**: https://github.com/shinyorg/extensions **Package**: `Shiny.Extensions.MauiHosting` **Namespace**: `Shiny` ## Registration Starting in v4, each capability ships as its own extension method. `AddInfrastructureModules` only wires modules — opt into the rest: ```csharp using Shiny; var builder = MauiApp.CreateBuilder(); builder .UseMauiApp<App>() .AddInfrastructureModules(new MyModule(), new AnotherModule()) .AddAppSupport() // IAppSupport .AddStartupService() // IStartupService + IOptions<StartupServiceOptions> .AddAppStore(opts => // IAppStore + IOptions<AppStoreOptions> { opts.AppleAppId = "1234567890"; opts.WindowsProductId = "9NBLGGH4NNS1"; opts.CountryCode = "us"; }); return builder.Build(); ``` Each extension is idempotent (uses `TryAddSingleton` / `HasImplementation` guards) so it's safe to call from libraries. `AddAppStore` has a convenience overload: ```csharp builder.AddAppStore(appleAppId: "1234567890", windowsProductId: "9NBLGGH4NNS1"); ``` ## IMauiModule Interface ```csharp public interface IMauiModule { void Add(MauiAppBuilder builder); // Register services void Use(IPlatformApplication app); // Post-build initialization (do NOT block) } ``` Each module implements two methods: - **`Add(MauiAppBuilder builder)`** — register services, configure the builder. Runs before the app is built. - **`Use(IPlatformApplication app)`** — post-build initialization. `ShinyHost.Services` is available here. **Do NOT block** — runs on the main thread. ```csharp public class AnalyticsModule : IMauiModule { public void Add(MauiAppBuilder builder) { builder.Services.AddSingleton<IAnalytics, AppCenterAnalytics>(); } public void Use(IPlatformApplication app) { var analytics = ShinyHost.Services.GetRequiredService<IAnalytics>(); analytics.TrackEvent("AppStarted"); } } ``` ## Static ShinyHost Access After initialization, `ShinyHost.Services` provides access to the service provider from anywhere: ```csharp var service = ShinyHost.Services.GetRequiredService<IMyService>(); ``` :::caution `ShinyHost.Services` throws `InvalidOperationException` if accessed before initialization. ::: ## IAppSupport `IAppSupport` exposes device info, browser/map launch, programmatic orientation lock, and change-detection events for orientation, culture, and time zone. ```csharp public interface IAppSupport { Version AppVersion { get; } string DeviceManufacturer { get; } string DeviceModel { get; } Version? PlatformVersion { get; } string Platform { get; } // DeviceInfo.Platform.ToString() — "Android", "iOS", "WinUI", "macOS" DeviceIdiom DeviceIdiom { get; } // DeviceInfo.Idiom — Phone / Tablet / Desktop / TV / Watch DisplayOrientation CurrentOrientation { get; } event EventHandler<DisplayOrientation>? OrientationChanged; CultureInfo CurrentCulture { get; } event EventHandler<CultureInfo>? CultureChanged; TimeZoneInfo CurrentTimeZone { get; } event EventHandler<TimeZoneInfo>? TimeZoneChanged; Task<bool> SetOrientation(DisplayOrientation orientation); Task<bool> ResetOrientation(); Task<bool> OpenBrowser(string uri, /* … */); Task<bool> OpenMap(double latitude, double longitude, /* … */); } ``` ### Change-detection events Each event has its own lazy subscription — the native listener spins up when the first handler attaches and tears down when the last detaches. | Capability | iOS / macCatalyst / macOS | Android | Windows | Bare TFM | |------------|---------------------------|---------|---------|----------| | Orientation | `DeviceDisplay.MainDisplayInfoChanged` (MAUI) | `DeviceDisplay.MainDisplayInfoChanged` (MAUI) | `DeviceDisplay.MainDisplayInfoChanged` (MAUI) | 2s poll | | Culture | `NSLocale.CurrentLocaleDidChangeNotification` | `BroadcastReceiver` on `Intent.ActionLocaleChanged` | `SystemEvents.UserPreferenceChanged` (Locale category) | 30s poll | | Time zone | `NSSystemTimeZoneDidChangeNotification` | `BroadcastReceiver` on `Intent.ActionTimezoneChanged` | `SystemEvents.TimeChanged` | 30s poll | The Linux GTK4 head uses `LinuxAppSupport` from `Shiny.Extensions.MauiHosting.Linux` instead — it watches `/etc/localtime` via `FileSystemWatcher` for time-zone changes and polls for culture and orientation. ```csharp public class SettingsViewModel(IAppSupport app) { public void Init() { app.OrientationChanged += (s, o) => { /* new DisplayOrientation */ }; app.CultureChanged += (s, c) => { /* new CultureInfo */ }; app.TimeZoneChanged += (s, tz) => { /* new TimeZoneInfo */ }; } } ``` ### Orientation lock ```csharp await app.SetOrientation(DisplayOrientation.Landscape); await app.ResetOrientation(); // restore system default ``` | Platform | Mechanism | Notes | |----------|-----------|-------| | Android | `Activity.RequestedOrientation` | Uses `SensorPortrait`/`SensorLandscape` so the device can still flip left↔right within the chosen orientation. Returns `false` if no current Activity | | iOS 16+ | `UIWindowScene.RequestGeometryUpdate` | The active view controller must permit the requested mask via `supportedInterfaceOrientations` or the request is silently dropped | | iOS 15 and earlier | Not supported | Returns `false` | | macCatalyst / macOS (AppKit) / Linux (GTK4) | Not supported (desktop windows don't rotate) | Returns `false` | | Windows | `DisplayInformation.AutoRotationPreferences` | `None` restores system default | ## IAppStore `IAppStore` looks up the latest published version from the relevant platform store, exposes deep links, and launches the review page. ```csharp public interface IAppStore { Task<AppStoreResult?> GetCurrent(CancellationToken cancellationToken = default); Task<bool> OpenStore(); Task<bool> OpenReviewPage(); Task<bool> RequestReview(); // native in-app prompt where the OS has one } public record AppStoreResult( Version StoreVersion, Version CurrentVersion, bool NeedsUpdate, string StoreUrl, string? ReleaseNotes = null, DateTimeOffset? ReleasedAt = null, double? AverageRating = null, long? RatingCount = null, string? MinimumOsVersion = null ); public class AppStoreOptions { public string? AppleAppId { get; set; } // numeric App Store ID (iOS + Mac App Store deep links) public string? AppleBundleId { get; set; } // defaults to AppInfo.PackageName public string? AndroidPackageName { get; set; } // defaults to AppInfo.PackageName public string? WindowsProductId { get; set; } // required on Windows public string? LinuxAppId { get; set; } // AppStream / Flatpak ID; auto-detected inside a Flatpak or Snap public string CountryCode { get; set; } = "us"; } ``` ### Lookup behaviour | Platform | API | Fields populated | |----------|-----|------------------| | iOS / macCatalyst | iTunes Search API (`itunes.apple.com/lookup?bundleId=…`) | All fields — version, release notes, ratings, release date, min OS. Auto-caches `trackId` back into `AppleAppId` for subsequent deep links | | macOS (AppKit) | Same, plus `&entity=macSoftware` so iTunes answers with the Mac app rather than an iOS app sharing the bundle ID | Same as iOS | | Linux (`Shiny.Extensions.MauiHosting.Linux`) | `flatpak info` for the installed version + origin, then `flatpak remote-info <origin> <id>`; or `snap list` + `snap info` for the tracked channel | Version, `NeedsUpdate`, store URL, `ReleasedAt`, and the Flatpak commit subject as `ReleaseNotes` | | Android | Play Store HTML scrape (`play.google.com/store/apps/details?id=…`) with two `GeneratedRegex` strategies (JSON-LD `softwareVersion` and legacy `[[["x.y.z"]]]` AF_initDataCallback) | Version + `NeedsUpdate` only — Play HTML doesn't reliably expose other fields | | Windows | Microsoft Store DisplayCatalog (`displaycatalog.mp.microsoft.com/v7.0/products?bigIds=…`) | Version, release notes (from `ProductDescription`), `ReleasedAt` where available | | Other TFMs | Not supported | Returns `null` | ### Deep links | Platform | OpenStore | OpenReviewPage | |----------|-----------|----------------| | iOS / macCatalyst | `itms-apps://itunes.apple.com/app/id{AppleAppId}` | `itms-apps://…/app/id{AppleAppId}?action=write-review` | | macOS (AppKit) | `macappstore://apps.apple.com/app/id{AppleAppId}` | `macappstore://…/app/id{AppleAppId}?action=write-review` | | Linux | `appstream://{LinuxAppId}` via `xdg-open` (GNOME Software / Plasma Discover / Snap Store) | Same as OpenStore — software centres show reviews on the app page | | Android | `market://details?id={packageName}` | Same as OpenStore (Play Store has no separate review URL) | | Windows | `ms-windows-store://pdp/?ProductId={WindowsProductId}` | `ms-windows-store://review/?ProductId={WindowsProductId}` | `RequestReview` shows the OS's own in-app prompt: `StoreKit.AppStore.RequestReview` (a `UIWindowScene` on iOS / Mac Catalyst 16+, the key window's `NSViewController` on macOS 14+) and `StoreContext.RequestRateAndReviewAppAsync` on Windows. Android and Linux have no dependency-free in-app prompt, so both fall back to `OpenReviewPage`. ### Usage ```csharp public class UpdateChecker(IAppStore store) { public async Task CheckForUpdates(CancellationToken ct = default) { var result = await store.GetCurrent(ct); if (result?.NeedsUpdate == true) { // result.StoreVersion, result.CurrentVersion, result.ReleaseNotes await store.OpenStore(); } } public Task PromptForReview() => store.OpenReviewPage(); } ``` :::caution Android version detection relies on scraping the Play Store HTML. Google changes the page structure periodically — if `GetCurrent` returns `null` on Android even when the app exists, the regex likely needs updating. ::: ## IStartupService `IStartupService` installs the running app into the desktop operating system's startup ("launch at login") list. It is safe to call from cross-platform code — mobile reports `NotSupported` rather than throwing. ```csharp public interface IStartupService { bool IsSupported { get; } Task<StartupServiceState> GetState(CancellationToken cancellationToken = default); Task<StartupServiceState> Register(CancellationToken cancellationToken = default); Task<StartupServiceState> Unregister(CancellationToken cancellationToken = default); Task<bool> OpenSettings(); // OS startup-apps / login-items UI } public enum StartupServiceState { NotSupported, NotRegistered, Enabled, DisabledByUser, // registered, but switched off in Task Manager / Login Items / the .desktop file DisabledByPolicy, // group policy / MDM / masked systemd unit — the app cannot override this RequiresApproval // macOS only — submitted, waiting for the user to approve in System Settings } public class StartupServiceOptions { public string? Identifier { get; set; } // Windows Run value name / Linux .desktop file or {Identifier}.service unit name; defaults to the entry assembly name public string? DisplayName { get; set; } // Linux desktop entry Name / systemd Description; defaults to Identifier public string? ExecutablePath { get; set; } // defaults to Environment.ProcessPath (must be absolute for systemd) public IList<string> Arguments { get; set; } // Windows + Linux only public LinuxStartupMode LinuxMode { get; set; } = LinuxStartupMode.XdgAutostart; public bool RestartOnFailure { get; set; } = true; // systemd: Restart=on-failure public string? WorkingDirectory { get; set; } // systemd: defaults to AppContext.BaseDirectory public string? LinuxServiceUser { get; set; } // SystemdSystem: User= (root when unset) } public enum LinuxStartupMode { XdgAutostart, // GUI — ~/.config/autostart/{id}.desktop (default) SystemdGraphicalSession, // GUI — ~/.config/systemd/user/{id}.service, WantedBy=graphical-session.target SystemdUser, // service — ~/.config/systemd/user/{id}.service, WantedBy=default.target SystemdSystem // service — /etc/systemd/system/{id}.service, WantedBy=multi-user.target (root) } ``` ### Platform behaviour | Platform | Mechanism | Notes | |----------|-----------|-------| | Windows (unpackaged, `WindowsPackageType=None`) | `HKCU\Software\Microsoft\Windows\CurrentVersion\Run` | Honours `ExecutablePath`/`Arguments`. `StartupApproved\Run` is read so a user switching the entry off in Task Manager surfaces as `DisabledByUser`. `OpenSettings` launches `ms-settings:startupapps` | | Windows (MSIX packaged) | Not supported | MSIX virtualizes `HKCU` writes into a per-package hive, so a `Run` entry never reaches the shell. Packaged apps need a `windows.startupTask` manifest declaration driven through WinRT, which this package doesn't implement. `IsSupported` is false |
GitHubで見る
この SKILL.md は非常に大きいため、SkillsMP では最初のセクションだけを表示しています。 GitHubで見る