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

@rainbow-robotics/plugin-ui

v1.2.1

Published

Muscat plugin UI contract/runtime: author bindings, robot SDK, and direct-mount host runtime

Readme

@rainbow-robotics/plugin-ui

Muscat plugin UI contract and host mounting architecture

A Muscat plugin is a plain React component exported as export default App.

Plugin mounting works through two paths, both handled by the ./host package.

Dev environment: muscat-pk dev

In dev, the plugin's own Vite dev server (using the plugin's vite.config.ts) serves a bootstrap module.

The host dynamically imports that bootstrap module to run the plugin.

Characteristics of this approach:

  • Full HMR support
  • The plugin runs as its own independent React root
  • The plugin uses its own React instance
  • The host's React tree and the plugin's React tree are separate

In other words, in dev the plugin runs inside the host page, but it does not share the React runtime with the host.

Build & robot environment: muscat-pk build

muscat-pk build is not its own bundler — it's a thin wrapper that runs vite build using the plugin project's own vite.config.ts (no --config is passed; it just sets cwd to the plugin root and runs vite build — Vite reads that directory's vite.config.ts on its own).

The scaffolded vite.config.ts looks like this:

import { defineConfig } from "vite";
import tailwindcss from "@tailwindcss/vite";
import { muscat } from "@rainbow-robotics/muscat-plugin-kit/vite";
export default defineConfig({ plugins: [tailwindcss(), muscat()] });

The external list and rollup config live in @rainbow-robotics/muscat-plugin-kit's muscat() preset (cli/vite.mjs), not in this package (@rainbow-robotics/plugin-ui). The roles split as follows:

| Package | Responsibility | |---|---| | @rainbow-robotics/muscat-plugin-kit | Build time — the muscat() preset (external list, entry discovery, rollup config) | | @rainbow-robotics/plugin-ui (this package) | Runtime — ./host (mounting), ./vite-host (import-map injection on the nexus side) |

The muscat() preset leaves the following dependencies out of the plugin bundle as external bare specifiers:

react
react-dom
@rainbow-robotics/plugin-ui/*
@repo/rb-components

The host page includes an import map set up by ./vite-host.

This import map resolves those bare specifiers to the actual module instances the host has already loaded.

So a plugin loaded in production behaves as follows:

  • Uses the same React instance as the host
  • Mounts as a real nested React tree inside the host's React tree
  • Shares RbContext
  • Shares the host's UI context
  • Minimizes the plugin bundle size

In dev the plugin runs as its own independent React root; in build/robot environments it runs nested inside the host's React tree.

Plugins with no UI skip the vite build entirely

muscat-pk build collects contributes[].ui from manifest.json and verifies that a matching ui/{name}.tsx (or .ts/.jsx/.js) file actually exists.

  • If contributes declares a ui but the file doesn't exist → build fails (clear error)
  • If contributes declares no ui and the ui/ directory has no files either (a backend-only plugin) → the vite build is skipped entirely. dist/ only contains manifest.json/assets/backend files.

This logic lives in buildPlugin() in libraries/muscat-plugin-kit/cli/src/build.mjs.

When Node.js isn't available

muscat-pk build isn't its own bundler — it spawns vite build as a child process, so a JS runtime must be available to run that child process.

  • muscat-pk installed via npm → already running under Node, so this issue never occurs.

  • muscat-pk installed via the S3 self-contained binary (compiled with Bun) → looks on PATH in order: bunnodenodejs. If none are found:

    Node.js 20+ (or Bun) is required to build the plugin UI.
      Install Node.js: https://nodejs.org
      Then run: npm install  (inside the plugin directory)

    is printed and the process exits with exitCode 1 quietly (no stack trace). This logic lives in findJsRuntime() / runViteBuild() in build.mjs.

Entry points

./react

Used by:

plugin

Re-exports every new-components export from @repo/rb-components, plus the useRb, useRbFormData, and useRbSave hooks from ./sdk.

Plugins should use UI components through this entry point rather than importing @repo/rb-components directly.

muscat-pk mcp parses this package's dist/react.d.ts (a self-contained bundle of rb-components' types) to give AI coding agents component lists and prop info.

./sdk

Used by:

plugin

Provides useRb().

useRb() gives access to:

  • Zenoh-WebSocket-based robot publish
  • subscribe
  • action calls
  • serve
  • toast
  • alert
  • confirm
  • prompt

Instead of implementing dialog/toast UI itself, a plugin delegates these requests to the host's real dialog and toast implementation.

See ./host-ui for the actual host UI service implementation.

useRbFormData() / useRbSave() are also provided by ./sdk. The host passes previously-saved data to the plugin, and the plugin registers a payload getter via onSave — form save/restore is owned by the host, not the plugin.

./ws

Used by:

plugin dev bootstrap only

Provides RbWs.

RbWs is the WebSocket client useRb() uses internally.

This entry point isn't used by a plugin's normal production code — it's only used by the dev-environment bootstrap module.

./manifest

Used by:

nexus
muscat-pk

Provides the plugin manifest schema.

Acts as the single source of truth for the manifest definition.

contributes[].point (CONTRIBUTION_POINTS) has 4 possible values:

| point | Description | |---|---| | toolbar | A live UI panel exposed in the top toolbar | | lnb | A live UI panel exposed in the left navigation bar (LNB) | | move.panel | A live UI panel exposed in the Move panel | | program.panel | A custom command the plugin defines for the Program page's rule-based sequence builder |

toolbar/lnb/move.panel back ends use RBManipulateSDK/RBAmrSDK or rb.action() directly. program.panel declares commandInfo (funcName: "rb_program_sdk.plugin_execute") and its backend runs through PluginProgramContext (on_execute, check_stop, get_variable, set_variable, done).

./plugin-data

Used by:

nexus only

Provides:

PluginDataProvider
usePluginData()

Decides where and how a plugin is loaded from.

./host

Used by:

nexus only

Provides PluginHost.

PluginHost handles both mounting paths described above:

  • The dev environment's dynamic Vite bootstrap import
  • The build/robot environment's ESM module mount

./vite-host

Used by:

nexus vite.config.ts

Provides Vite plugins.

Main responsibilities:

  • Injecting the import map
  • Supporting the plugin preview dev environment

Plugin production bundle rules

A plugin's production bundle should only import these entry points:

./react
./sdk
./host-ui

./host-ui is used optionally, only when needed.

The following entry points, on the other hand, are host-only:

./host
./vite-host

These modules include host-only dependencies:

@repo/rb-components
vite

So ./host and ./vite-host are never included in a plugin bundle.