npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@napacitor/maui

v0.1.2-beta.9

Published

Napacitor .NET MAUI 运行时(对应 @capacitor/android 的原生库角色,以 C# 实现)

Readme

@napacitor/maui

Napacitor 的 .NET MAUI 原生运行时,对应 Capacitor 仓库中 android/(@capacitor/android)的角色 —— 区别在于原生抽象层是 .NET MAUI Blazor(BlazorWebView + JSInterop),同一份 C# 代码覆盖 Android / iOS / Mac Catalyst / Windows 全部平台。

结构

maui/
├── src/
│   ├── Napacitor.csproj          # net10.0 类库;程序集名固定为 Napacitor
│   ├── NapacitorRuntime.cs       # 运行时:插件发现、原生桥注入、入口导入、事件发送
│   ├── NapacitorBridge.cs        # 静态 [JSInvokable] 入口(JS → C#)
│   ├── PluginRegistry.cs         # 反射自动发现 [NapacitorPlugin](零手动注册)
│   ├── ListenerRegistry.cs       # callbackId → (插件, 事件) 注册表
│   ├── NapacitorException.cs     # 带错误码的异常
│   ├── NapacitorJson.cs          # camelCase / 大小写不敏感的共享 JsonSerializerOptions
│   ├── Plugins/                  # 插件基础设施(特性、接口、基类、NapacitorCall)
│   └── JSBridge/                 # 内嵌原生桥脚本(由 core/dist 同步)
└── package.json                  # npm 包形态,files 仅含 src/

宿主如何接入

MAUI Blazor 宿主(由 nap add maui 生成的模板)只需两处:

  1. csproj 引用本运行时(nap sync 生成的 napacitor.build.props 完成);
  2. Blazor 根组件 Main.razor:
@inject IJSRuntime JS

@code {
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await NapacitorRuntime.StartAsync(JS);
        }
    }
}

启动顺序:发现插件 → 注入 window.NapacitorNative 与原生桥(eval)→ 读取 napacitor.config.json → 按 napacitor.entries.json 顺序 import() Web 应用入口脚本。

桥协议

| 方向 | 通道 | |---|---| | JS → C# | window.DotNet.invokeMethodAsync('Napacitor', 'NAP_invoke', pluginId, method, argsJson) | | C# → JS | js.InvokeVoidAsync('NapacitorNative.__emit', callbackId, dataJson) | | C# → JS(事件) | js.InvokeVoidAsync('eval', …) 派发 CustomEvent(见下 TriggerJSEventAsync) |

调用结果为信封:{"ok":true,"data":…} / {"ok":false,"error":{"code":"…","message":"…"}}。

Core APIs(MAUI ⇄ Web 交互)

与 Capacitor 的 Core APIs(reference/core-apis)对应 —— 插件调用之外的桥层基础交互能力:

| Capacitor | Napacitor(C#) | 说明 | |---|---|---| | bridge.getConfig() | NapacitorRuntime.AppConfig / 插件基类 AppConfig | napacitor.config.json 根配置(JsonElement;未加载时 ValueKind 为 Undefined) | | bridge.triggerJSEvent(name, target, data?) | TriggerJSEventAsync(name, target = "window", dataJson?) | 在 window / document 上派发 CustomEvent;另有 (name, object? data) 便捷重载 | | iOS localURL(fromWebURL:) | ReadAssetTextAsync(url) | C# 侧按 Web 的 URL 规则读取 wwwroot 静态文本(经 WebView 同源通道,Android 的 apk 内资产同样可读) | | iOS viewController / DispatchQueue.main | 插件工程直接用 MAUI Essentials | Application.Current.MainPage、MainThread.InvokeOnMainThreadAsync(…) —— 运行时库刻意零 MAUI 框架依赖(仅引 Microsoft.JSInterop),UI 能力留在插件/宿主层 | | saveCall / keepAlive | 无需(架构差异) | Task 异步模型天然承载单次异步完成;多次回调即事件管线(NotifyListeners + JS addListener) |

TriggerJSEventAsync 用例 —— 原生主动通知前端(插件事件管线之外的广播通道):

// 插件内(基类方法;宿主代码可用 NapacitorRuntime.Current?.TriggerJSEventAsync(…))
await TriggerJSEventAsync("napacitor:download", "window",
    """{"progress":100,"file":"report.pdf"}""");

// 便捷重载:对象直接序列化为事件 detail
await TriggerJSEventAsync("napacitor:download", new { Progress = 100, File = "report.pdf" });
// Web 侧:普通 DOM 事件接收
window.addEventListener('napacitor:download', (e) => {
  console.log('下载完成', (e as CustomEvent).detail);
});

数据须为序列化 JSON 字符串(与 Capacitor 同约定),JS 侧自动 JSON.parse 为事件 detail,解析失败时以原始字符串传入;事件名与数据均经 JSON 字面量嵌入脚本,无注入风险。

宿主原生按钮验证(完整示例)

宿主工程由 nap add maui 生成、不入仓库,这里的示例代码供宿主按需复制。若想在原生层验证 C# → Web 通道,可在承载 BlazorWebView 的 MainPage.xaml 顶部加一条原生按钮栏,点击时经 TriggerJSEventAsync / ReadAssetTextAsync 与 Web 交互(Plugins Gallery 即此做法):

<Grid RowDefinitions="Auto,*">
    <!-- 原生按钮栏:演示 C# → Web 交互(TriggerJSEventAsync / ReadAssetTextAsync) -->
    <HorizontalStackLayout Grid.Row="0" Padding="10,8" Spacing="8">
        <Button Text="派发原生事件(napacitor:tick)"
                Clicked="OnDispatchEvent" />
        <Button Text="读取 Web 资产(napacitor.config.json)"
                Clicked="OnReadAsset" />
    </HorizontalStackLayout>

    <BlazorWebView Grid.Row="1" HostPage="wwwroot/index.html">
        <BlazorWebView.RootComponents>
            <RootComponent Selector="#napacitor-root"
                             ComponentType="{x:Type local:Main}" />
        </BlazorWebView.RootComponents>
    </BlazorWebView>
</Grid>

后台代码(注意 using Napacitor;):

using Napacitor;

namespace NapacitorHost;

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
    }

    /// <summary>按钮:经 TriggerJSEventAsync 向 Web 派发 DOM 事件(对应 Capacitor triggerJSEvent)。</summary>
    private async void OnDispatchEvent(object sender, EventArgs e)
    {
        var runtime = NapacitorRuntime.Current;
        if (runtime is null)
        {
            return;
        }

        await runtime.TriggerJSEventAsync("napacitor:tick", new
        {
            time = DateTime.Now.ToString("O"),
            message = "来自 MainPage.xaml 的原生按钮",
        });
    }

    /// <summary>按钮:经 JS 同源通道读取 Web 资产文本,再以事件形式回传页面(对应 iOS localURL 的读取场景)。</summary>
    private async void OnReadAsset(object sender, EventArgs e)
    {
        var runtime = NapacitorRuntime.Current;
        if (runtime is null)
        {
            return;
        }

        var text = await runtime.ReadAssetTextAsync("napacitor.config.json");
        await runtime.TriggerJSEventAsync("napacitor:asset", new
        {
            url = "napacitor.config.json",
            length = text?.Length ?? 0,
            preview = text is null ? null : (text.Length > 120 ? text[..120] + "…" : text),
        });
    }
}

Web 侧以普通 DOM 监听接收:window.addEventListener('napacitor:tick', …) 与 window.addEventListener('napacitor:asset', …),事件数据在 e.detail。

Web 侧工具(@napacitor/core 的 Napacitor 对象,对应 Capacitor 的 Web API)已内置:getPlatform()、isNativePlatform()、isPluginAvailable(name)、convertFileUri(uri)。其中 convertFileUri 直接返回原 URI:MAUI Blazor 的 WebView 与静态资产同源,不需要 Capacitor 那种 file:// → http://localhost 的协议改写;iOS 的 portablePath(fromLocalURL:) 同理 —— 相对路径在 WebView 直接可用,反向(C# 读 wwwroot)用 ReadAssetTextAsync 借道。