@rainbow-robotics/plugin-ui 0.1.2-dev.46 → 0.1.2-dev.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,32 +1,32 @@
1
1
  # @rainbow-robotics/plugin-ui
2
2
 
3
- # Muscat 플러그인 UI 계약 및 호스트 마운팅 구조
3
+ # Muscat plugin UI contract and host mounting architecture
4
4
 
5
- Muscat 플러그인은 `export default App` 형태로 내보내는 일반 React 컴포넌트입니다.
5
+ A Muscat plugin is a plain React component exported as `export default App`.
6
6
 
7
- 플러그인 마운팅은 두 가지 경로로 동작하며, 두 경로 모두 `./host` 패키지에서 처리합니다.
7
+ Plugin mounting works through two paths, both handled by the `./host` package.
8
8
 
9
- ## 개발 환경: `muscat-pk dev`
9
+ ## Dev environment: `muscat-pk dev`
10
10
 
11
- 개발 환경에서는 플러그인 자체의 vite.config.ts를 이용하여 Vite 개발 서버가 bootstrap 모듈을 제공합니다.
11
+ In dev, the plugin's own Vite dev server (using the plugin's `vite.config.ts`) serves a bootstrap module.
12
12
 
13
- 호스트는 해당 bootstrap 모듈을 동적으로 import하여 플러그인을 실행합니다.
13
+ The host dynamically imports that bootstrap module to run the plugin.
14
14
 
15
- 이 방식의 특징은 다음과 같습니다.
15
+ Characteristics of this approach:
16
16
 
17
- * 전체 HMR 지원
18
- * 플러그인은 독립적인 React Root로 실행
19
- * 플러그인이 자체 React 인스턴스를 사용
20
- * 호스트 React 트리와 플러그인 React 트리가 분리됨
17
+ * Full HMR support
18
+ * The plugin runs as its own independent React root
19
+ * The plugin uses its own React instance
20
+ * The host's React tree and the plugin's React tree are separate
21
21
 
22
- 즉, 개발 환경에서는 플러그인이 호스트 페이지 내부에서 실행되지만 React 런타임 자체는 호스트와 공유하지 않습니다.
22
+ In other words, in dev the plugin runs inside the host page, but it does not share the React runtime with the host.
23
23
 
24
- ## 빌드 및 로봇 환경: `muscat-pk build`
24
+ ## Build & robot environment: `muscat-pk build`
25
25
 
26
- `muscat-pk build`는 자체 번들러가 아니라, **플러그인 프로젝트 자신의 `vite.config.ts`로 `vite build`를 실행**하는 얇은 래퍼입니다
27
- (`--config` 지정 없이 `cwd`만 플러그인 루트로 잡고 `vite build`를 실행 — Vite가 그 디렉토리의 `vite.config.ts`를 알아서 읽습니다).
26
+ `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`**
27
+ (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).
28
28
 
29
- 스캐폴딩되는 `vite.config.ts`는 이렇게 생겼습니다.
29
+ The scaffolded `vite.config.ts` looks like this:
30
30
 
31
31
  ```ts
32
32
  import { defineConfig } from "vite";
@@ -35,14 +35,14 @@ import { muscat } from "@rainbow-robotics/muscat-plugin-kit/vite";
35
35
  export default defineConfig({ plugins: [tailwindcss(), muscat()] });
36
36
  ```
37
37
 
38
- **external 목록과 rollup 설정은 이 패키지(`@rainbow-robotics/plugin-ui`)가 아니라 `@rainbow-robotics/muscat-plugin-kit`의 `muscat()` preset(`cli/vite.mjs`)에 정의돼 있습니다.** 역할이 이렇게 나뉩니다.
38
+ **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:
39
39
 
40
- | 패키지 | 담당 |
40
+ | Package | Responsibility |
41
41
  |---|---|
42
- | `@rainbow-robotics/muscat-plugin-kit` | 빌드 타임 — `muscat()` preset (external 목록, entry 탐색, rollup 설정) |
43
- | `@rainbow-robotics/plugin-ui` (이 패키지) | 런타임 — `./host`(마운트), `./vite-host`(nexus 쪽 import map 주입) |
42
+ | `@rainbow-robotics/muscat-plugin-kit` | Build time — the `muscat()` preset (external list, entry discovery, rollup config) |
43
+ | `@rainbow-robotics/plugin-ui` (this package) | Runtime — `./host` (mounting), `./vite-host` (import-map injection on the nexus side) |
44
44
 
45
- `muscat()` preset이 다음 의존성을 플러그인 번들에 포함하지 않고 external bare specifier로 남겨둡니다.
45
+ The `muscat()` preset leaves the following dependencies out of the plugin bundle as external bare specifiers:
46
46
 
47
47
  ```text
48
48
  react
@@ -51,35 +51,35 @@ react-dom
51
51
  @repo/rb-components
52
52
  ```
53
53
 
54
- 호스트 페이지에는 `./vite-host`에서 설정한 import map이 포함되어 있습니다.
54
+ The host page includes an import map set up by `./vite-host`.
55
55
 
56
- 이 import map은 위 bare specifier들을 호스트가 이미 로드한 실제 모듈 인스턴스로 연결합니다.
56
+ This import map resolves those bare specifiers to the actual module instances the host has already loaded.
57
57
 
58
- 따라서 프로덕션 환경에서 로드된 플러그인은 다음과 같이 동작합니다.
58
+ So a plugin loaded in production behaves as follows:
59
59
 
60
- * 호스트와 동일한 React 인스턴스 사용
61
- * 호스트 React 트리 내부에 실제 중첩 React 트리로 마운트
62
- * `RbContext` 공유
63
- * 호스트 UI 컨텍스트 공유
64
- * 플러그인 번들 크기 최소화
60
+ * Uses the same React instance as the host
61
+ * Mounts as a real nested React tree inside the host's React tree
62
+ * Shares `RbContext`
63
+ * Shares the host's UI context
64
+ * Minimizes the plugin bundle size
65
65
 
66
- 개발 환경에서는 플러그인이 독립적인 React Root로 실행되지만, 빌드 및 로봇 환경에서는 호스트 React 트리 내부에 중첩되어 실행됩니다.
66
+ In dev the plugin runs as its own independent React root; in build/robot environments it runs nested inside the host's React tree.
67
67
 
68
- ### UI가 없는 플러그인은 vite build 자체를 건너뜀
68
+ ### Plugins with no UI skip the vite build entirely
69
69
 
70
- `muscat-pk build`는 `manifest.json`의 `contributes[].ui`를 모아서 실제로 `ui/{name}.tsx`(또는 `.ts`/`.jsx`/`.js`) 파일이 있는지 검증합니다.
70
+ `muscat-pk build` collects `contributes[].ui` from `manifest.json` and verifies that a matching `ui/{name}.tsx` (or `.ts`/`.jsx`/`.js`) file actually exists.
71
71
 
72
- * `contributes`에 `ui`가 하나라도 선언돼 있는데 해당 파일이 없으면 → 빌드 실패(명확한 에러)
73
- * `contributes`에 선언된 `ui`가 없고 `ui/` 디렉토리에 파일도 없으면(백엔드 전용 플러그인) → **vite build를 아예 건너뜁니다.** `dist/`엔 manifest.json/assets/백엔드 파일만 들어갑니다.
72
+ * If `contributes` declares a `ui` but the file doesn't exist → build fails (clear error)
73
+ * 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.
74
74
 
75
- 로직은 `libraries/muscat-plugin-kit/cli/src/build.mjs`의 `buildPlugin()`에 있습니다.
75
+ This logic lives in `buildPlugin()` in `libraries/muscat-plugin-kit/cli/src/build.mjs`.
76
76
 
77
- ### Node.js가 없을 때
77
+ ### When Node.js isn't available
78
78
 
79
- `muscat-pk build`는 자체 번들러가 아니라 `vite build`를 **자식 프로세스로 스폰**하므로, 그 자식 프로세스를 실행할 JS 런타임이 있어야 합니다.
79
+ `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.
80
80
 
81
- * **npm으로 설치한 `muscat-pk`** → 이미 Node로 실행 중이므로 이 문제 자체가 발생하지 않습니다.
82
- * **S3 self-contained 바이너리(Bun으로 컴파일됨)로 설치한 `muscat-pk`** → PATH에서 `bun` → `node` → `nodejs` 순서로 찾습니다. 셋 다 없으면:
81
+ * **`muscat-pk` installed via npm** → already running under Node, so this issue never occurs.
82
+ * **`muscat-pk` installed via the S3 self-contained binary (compiled with Bun)** → looks on PATH in order: `bun` → `node` → `nodejs`. If none are found:
83
83
 
84
84
  ```
85
85
  Node.js 20+ (or Bun) is required to build the plugin UI.
@@ -87,125 +87,139 @@ react-dom
87
87
  Then run: npm install (inside the plugin directory)
88
88
  ```
89
89
 
90
- 를 출력하고 `exitCode 1`로 조용히 종료합니다(스택트레이스 없음). 로직은 `build.mjs`의 `findJsRuntime()` / `runViteBuild()`에 있습니다.
90
+ is printed and the process exits with `exitCode 1` quietly (no stack trace). This logic lives in `findJsRuntime()` / `runViteBuild()` in `build.mjs`.
91
91
 
92
- # 엔트리 포인트
92
+ # Entry points
93
93
 
94
94
  ## `./react`
95
95
 
96
- 사용 대상:
96
+ Used by:
97
97
 
98
98
  ```text
99
99
  plugin
100
100
  ```
101
101
 
102
- 실제 `@repo/rb-components`에서 `new-components` 전부 플러그인에 export합니다.
102
+ Re-exports every `new-components` export from `@repo/rb-components`, plus the `useRb`, `useRbFormData`, and `useRbSave` hooks from `./sdk`.
103
103
 
104
+ Plugins should use UI components through this entry point rather than importing `@repo/rb-components` directly.
104
105
 
105
- 플러그인은 `@repo/rb-components`를 직접 import하기보다 이 엔트리 포인트를 통해 UI 컴포넌트를 사용합니다.
106
+ `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.
106
107
 
107
108
  ## `./sdk`
108
109
 
109
- 사용 대상:
110
+ Used by:
110
111
 
111
112
  ```text
112
113
  plugin
113
114
  ```
114
115
 
115
- `useRb()`를 제공합니다.
116
+ Provides `useRb()`.
116
117
 
117
- `useRb()`를 통해 다음 기능을 사용할 수 있습니다.
118
+ `useRb()` gives access to:
118
119
 
119
- * Zenoh WebSocket 기반 로봇 publish
120
+ * Zenoh-WebSocket-based robot publish
120
121
  * subscribe
121
- * action 호출
122
+ * action calls
122
123
  * serve
123
124
  * toast
124
125
  * alert
125
126
  * confirm
126
127
  * prompt
127
128
 
128
- 이 API들은 플러그인 내부에서 직접 UI를 구현하는 대신, 호스트가 실제로 사용하는 dialog, toast 기능에 요청을 위임합니다.
129
+ Instead of implementing dialog/toast UI itself, a plugin delegates these requests to the host's real dialog and toast implementation.
129
130
 
130
- 실제 호스트 UI 서비스 구현은 `./host-ui`를 참고합니다.
131
+ See `./host-ui` for the actual host UI service implementation.
132
+
133
+ `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.
131
134
 
132
135
  ## `./ws`
133
136
 
134
- 사용 대상:
137
+ Used by:
135
138
 
136
139
  ```text
137
140
  plugin dev bootstrap only
138
141
  ```
139
142
 
140
- `RbWs`를 제공합니다.
143
+ Provides `RbWs`.
141
144
 
142
- `RbWs`는 `useRb()`가 내부적으로 사용하는 WebSocket 클라이언트입니다.
145
+ `RbWs` is the WebSocket client `useRb()` uses internally.
143
146
 
144
- 이 엔트리 포인트는 플러그인의 일반 프로덕션 코드에서 사용하는 것이 아니라, 개발 환경의 bootstrap 모듈에서만 사용합니다.
147
+ This entry point isn't used by a plugin's normal production code — it's only used by the dev-environment bootstrap module.
145
148
 
146
149
  ## `./manifest`
147
150
 
148
- 사용 대상:
151
+ Used by:
149
152
 
150
153
  ```text
151
154
  nexus
152
155
  muscat-pk
153
156
  ```
154
157
 
155
- 플러그인 manifest 스키마를 제공합니다.
158
+ Provides the plugin manifest schema.
159
+
160
+ Acts as the single source of truth for the manifest definition.
161
+
162
+ `contributes[].point` (`CONTRIBUTION_POINTS`) has 4 possible values:
163
+
164
+ | point | Description |
165
+ |---|---|
166
+ | `toolbar` | A live UI panel exposed in the top toolbar |
167
+ | `lnb` | A live UI panel exposed in the left navigation bar (LNB) |
168
+ | `move.panel` | A live UI panel exposed in the Move panel |
169
+ | `program.panel` | A custom command the plugin defines for the Program page's rule-based sequence builder |
156
170
 
157
- manifest 정의의 단일 진실 공급원, 즉 Single Source of Truth 역할을 합니다.
171
+ `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`).
158
172
 
159
173
  ## `./plugin-data`
160
174
 
161
- 사용 대상:
175
+ Used by:
162
176
 
163
177
  ```text
164
178
  nexus only
165
179
  ```
166
180
 
167
- 다음 API를 제공합니다.
181
+ Provides:
168
182
 
169
183
  ```text
170
184
  PluginDataProvider
171
185
  usePluginData()
172
186
  ```
173
187
 
174
- 플러그인을 어디에서, 어떤 방식으로 불러올지 결정합니다.
188
+ Decides where and how a plugin is loaded from.
175
189
 
176
190
  ## `./host`
177
191
 
178
- 사용 대상:
192
+ Used by:
179
193
 
180
194
  ```text
181
195
  nexus only
182
196
  ```
183
197
 
184
- `PluginHost`를 제공합니다.
198
+ Provides `PluginHost`.
185
199
 
186
- `PluginHost`는 앞에서 설명한 두 가지 마운트 경로를 모두 처리합니다.
200
+ `PluginHost` handles both mounting paths described above:
187
201
 
188
- * 개발 환경의 Vite bootstrap 동적 import 방식
189
- * 빌드 및 로봇 환경의 ESM 모듈 마운트 방식
202
+ * The dev environment's dynamic Vite bootstrap import
203
+ * The build/robot environment's ESM module mount
190
204
 
191
205
  ## `./vite-host`
192
206
 
193
- 사용 대상:
207
+ Used by:
194
208
 
195
209
  ```text
196
210
  nexus vite.config.ts
197
211
  ```
198
212
 
199
- Vite 플러그인을 제공합니다.
213
+ Provides Vite plugins.
200
214
 
201
- 주요 역할은 다음과 같습니다.
215
+ Main responsibilities:
202
216
 
203
- * import map 삽입
204
- * 플러그인 preview 개발 환경 지원
217
+ * Injecting the import map
218
+ * Supporting the plugin preview dev environment
205
219
 
206
- # 플러그인 프로덕션 번들 규칙
220
+ # Plugin production bundle rules
207
221
 
208
- 플러그인의 프로덕션 번들은 다음 엔트리 포인트만 import합니다.
222
+ A plugin's production bundle should only import these entry points:
209
223
 
210
224
  ```text
211
225
  ./react
@@ -213,20 +227,20 @@ Vite 플러그인을 제공합니다.
213
227
  ./host-ui
214
228
  ```
215
229
 
216
- `./host-ui`는 필요한 경우에만 선택적으로 사용합니다.
230
+ `./host-ui` is used optionally, only when needed.
217
231
 
218
- 반면 다음 엔트리 포인트는 호스트 전용입니다.
232
+ The following entry points, on the other hand, are host-only:
219
233
 
220
234
  ```text
221
235
  ./host
222
236
  ./vite-host
223
237
  ```
224
238
 
225
- 이 모듈들은 다음과 같은 호스트 전용 의존성을 포함합니다.
239
+ These modules include host-only dependencies:
226
240
 
227
241
  ```text
228
242
  @repo/rb-components
229
243
  vite
230
244
  ```
231
245
 
232
- 따라서 `./host`와 `./vite-host`는 플러그인 번들에 포함되지 않습니다.
246
+ So `./host` and `./vite-host` are never included in a plugin bundle.
@@ -1,15 +1,9 @@
1
1
  /**
2
- * Plugin data seam (host-side).
3
- *
4
- * A single source serves both the real back end and the locally-developed
5
- * plugin. `muscat-pk dev` injects `window.__RB_PREVIEW_PLUGIN_ID__`; any request
6
- * for that id is routed to preview-host (dev plugin), everything else to the
7
- * host's back end. So the dev plugin and installed plugins coexist.
8
- *
9
- * Host-agnostic: no app-stack dependency (no react-query). The host passes its
10
- * real dialog/toast as `hostServices` — the provider registers them via
11
- * setHostServices so plugins' useDialog/toast delegate to the host's single
12
- * instance (see ./host-ui).
2
+ * Plugin data seam (host-side). Resolves where each plugin's UI/assets load
3
+ * from. `muscat-pk dev` sets `window.__RB_PREVIEW_PLUGIN_ID__`; that id routes
4
+ * to preview-host, everything else to the installed-plugin back end, so both
5
+ * coexist. The host passes its dialog/toast as `hostServices`, registered via
6
+ * setHostServices (see ./host-ui).
13
7
  */
14
8
  import { type PropsWithChildren } from "react";
15
9
  import type { PluginManifest } from "./manifest";
@@ -1 +1 @@
1
- {"version":3,"file":"pluginData.d.ts","sourceRoot":"","sources":["../src/pluginData.tsx"],"names":[],"mappings":"AACA;;;;;;;;;;;;GAYG;AACH,OAAO,EAML,KAAK,iBAAiB,EACvB,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAMjD,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,qCAAqC,CAAC;AAE7C;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtC,MAAM,WAAW,gBAAgB;IAC/B,4EAA4E;IAC5E,iBAAiB,EAAE,MAAM;QAAE,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IAC7E,mEAAmE;IACnE,eAAe,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,kBAAkB,CAAC;IACtE,0EAA0E;IAC1E,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;IAC7D,mDAAmD;IACnD,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;CAC5C;AAID,wBAAgB,aAAa,IAAI,gBAAgB,CAMhD;AAgHD,MAAM,WAAW,uBAAuB;IACtC;;;;;;OAMG;IACH,YAAY,EAAE,gBAAgB,CAAC;IAC/B,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,kBAAkB,CAAC,EACjC,QAAQ,EACR,YAAY,EACZ,KAA8B,EAC9B,QAAoB,GACrB,EAAE,iBAAiB,CAAC,uBAAuB,CAAC,2CAqC5C"}
1
+ {"version":3,"file":"pluginData.d.ts","sourceRoot":"","sources":["../src/pluginData.tsx"],"names":[],"mappings":"AACA;;;;;;GAMG;AACH,OAAO,EAML,KAAK,iBAAiB,EACvB,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjD,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,qCAAqC,CAAC;AAE7C;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtC,MAAM,WAAW,gBAAgB;IAC/B,4EAA4E;IAC5E,iBAAiB,EAAE,MAAM;QAAE,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IAC7E,mEAAmE;IACnE,eAAe,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,kBAAkB,CAAC;IACtE,0EAA0E;IAC1E,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;IAC7D,mDAAmD;IACnD,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;CAC5C;AAID,wBAAgB,aAAa,IAAI,gBAAgB,CAMhD;AAgHD,MAAM,WAAW,uBAAuB;IACtC;;;;;;OAMG;IACH,YAAY,EAAE,gBAAgB,CAAC;IAC/B,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,kBAAkB,CAAC,EACjC,QAAQ,EACR,YAAY,EACZ,KAA8B,EAC9B,QAAgD,GACjD,EAAE,iBAAiB,CAAC,uBAAuB,CAAC,2CAqC5C"}
package/dist/react.d.ts CHANGED
@@ -65,8 +65,8 @@ interface RippleOption {
65
65
  declare const labelButtonSizes: readonly ["3xs", "2xs", "xs", "xs2", "sm", "md"];
66
66
  type LabelButtonSizeType = (typeof labelButtonSizes)[number];
67
67
  declare const labelButtonVariants: (props?: {
68
- size?: "3xs" | "2xs" | "xs" | "xs2" | "sm" | "md";
69
- variant?: "transparent" | "outlined" | "soft" | "solid" | "inverse" | "inverseSoft" | "brand" | "brandSoft" | "danger" | "dangerSoft" | "success" | "successSoft";
68
+ size?: "xs" | "xs2" | "sm" | "md" | "3xs" | "2xs";
69
+ variant?: "transparent" | "outlined" | "soft" | "solid" | "inverse" | "inverseSoft" | "brand" | "success" | "brandSoft" | "danger" | "dangerSoft" | "successSoft";
70
70
  disabled?: boolean;
71
71
  } & class_variance_authority_types.ClassProp) => string;
72
72
  type LabelButtonVariantProps = VariantProps<typeof labelButtonVariants>;
@@ -136,7 +136,7 @@ declare const RBLabelButton: React$1.ForwardRefExoticComponent<Omit<Omit<React$1
136
136
  declare const iconButtonSizes: readonly ["3xs", "2xs", "xs", "xs2", "sm", "md"];
137
137
  type IconButtonSizeType = (typeof iconButtonSizes)[number];
138
138
  declare const iconButtonVariants: (props?: {
139
- size?: "3xs" | "2xs" | "xs" | "xs2" | "sm" | "md";
139
+ size?: "xs" | "xs2" | "sm" | "md" | "3xs" | "2xs";
140
140
  variant?: "circle" | "transparent" | "outlined" | "soft" | "solid" | "inverse" | "inverseSoft" | "brand";
141
141
  disabled?: boolean;
142
142
  } & class_variance_authority_types.ClassProp) => string;
@@ -293,7 +293,7 @@ declare function RBInlineLabelButton({ className, size, variant, type, leadingIc
293
293
  declare const accordionTypes: readonly ["fill", "transparent"];
294
294
  type AccordionType = (typeof accordionTypes)[number];
295
295
  declare const accordionVariants: (props?: {
296
- type?: "transparent" | "fill";
296
+ type?: "fill" | "transparent";
297
297
  } & class_variance_authority_types.ClassProp) => string;
298
298
  type AccordionVariantProps = VariantProps<typeof accordionVariants>;
299
299
 
@@ -448,7 +448,7 @@ declare const textInputVariantsList: readonly ["default", "success"];
448
448
  type TextInputVariant = (typeof textInputVariantsList)[number];
449
449
  declare const textInputVariants: (props?: {
450
450
  size?: "xs2" | "sm" | "md" | "lg";
451
- variant?: "success" | "default";
451
+ variant?: "default" | "success";
452
452
  focus?: boolean;
453
453
  error?: boolean;
454
454
  readOnly?: boolean;
@@ -519,7 +519,7 @@ type TextAreaSize = (typeof textAreaSizes)[number];
519
519
  declare const textAreaSizes: readonly ["sm", "md", "lg"];
520
520
  declare const textAreaVariants: (props?: {
521
521
  size?: "sm" | "md" | "lg";
522
- variant?: "success" | "default";
522
+ variant?: "default" | "success";
523
523
  focus?: boolean;
524
524
  error?: boolean;
525
525
  readOnly?: boolean;
@@ -1187,7 +1187,7 @@ declare const sideNumberInputVariantsList: readonly ["default", "success"];
1187
1187
  type SideNumberInputVariant = (typeof sideNumberInputVariantsList)[number];
1188
1188
  declare const sideNumberInputVariants: (props?: {
1189
1189
  size?: "xs2" | "sm" | "md" | "lg";
1190
- variant?: "success" | "default";
1190
+ variant?: "default" | "success";
1191
1191
  focus?: boolean;
1192
1192
  error?: boolean;
1193
1193
  readOnly?: boolean;
package/dist/react.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/react.tsx"],
4
- "sourcesContent": ["/**\n * Author component surface.\n *\n * import { RBSlider, RBLabelButton, ... } from \"@rainbow-robotics/plugin-ui/react\";\n *\n * Full re-export of the REAL @repo/rb-components (`new_components` barrel).\n * Plugins mount directly in the host document (no iframe / remote-dom), so\n * these are ordinary React components with their real props \u2014 authors also\n * use native <div>/<span>/<button> and className freely.\n *\n * This re-export surface IS the version contract: the host serves exactly the\n * plugin-ui runtime that these resolve to, so a plugin can't drift from the\n * host's components. The import-map shim that mirrors this at runtime (see\n * ./viteHost.ts) reads this module's actual exports rather than a hardcoded\n * list, so it never drifts from what's re-exported here.\n */\nexport * from \"@repo/rb-components/new_components\";\n\nexport { useRb, useRbFormData, useRbSave } from \"./sdk.js\";\nexport type { RbSdk } from \"./sdk\";\n"],
5
- "mappings": "AAgBA,cAAc;AAEd,SAAS,OAAO,eAAe,iBAAiB;",
4
+ "sourcesContent": ["/**\n * Author component surface \u2014 re-exports the @repo/rb-components new_components\n * barrel, plus the sdk hooks.\n *\n * import { RBSlider, RBLabelButton, useRb } from \"@rainbow-robotics/plugin-ui/react\";\n *\n * The runtime import-map shim (./viteHost.ts) reads this module's exports\n * rather than a hardcoded list, so the two stay in sync.\n */\nexport * from \"@repo/rb-components/new_components\";\n\nexport { useRb, useRbFormData, useRbSave } from \"./sdk.js\";\nexport type { RbSdk } from \"./sdk\";\n"],
5
+ "mappings": "AASA,cAAc;AAEd,SAAS,OAAO,eAAe,iBAAiB;",
6
6
  "names": []
7
7
  }
package/dist/sdk.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../src/sdk.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,YAAY,IAAI,UAAU,EAC1B,WAAW,IAAI,SAAS,EACzB,MAAM,MAAM,CAAC;AAGd,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAElD,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAEpE,MAAM,WAAW,KAAK;IACpB;;;;OAIG;IACH,GAAG,EAAE,CAAC,CAAC,GAAG,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAE3D,+DAA+D;IAC/D,GAAG,EAAE,CAAC,CAAC,GAAG,GAAG,EACX,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,KAC1B,OAAO,CAAC,UAAU,CAAC,CAAC;IAEzB;;;OAGG;IACH,MAAM,EAAE,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAErE;;;OAGG;IACH,KAAK,EAAE,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EACtB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,KACrC,OAAO,CAAC,SAAS,CAAC,CAAC;IAExB,qEAAqE;IACrE,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAEjC,gCAAgC;IAChC,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACjC,uDAAuD;IACvD,OAAO,EAAE,gBAAgB,CAAC,SAAS,CAAC,CAAC;IACrC,wFAAwF;IACxF,MAAM,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAEnC;;;;OAIG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;;;OAKG;IACH,MAAM,EAAE,CAAC,UAAU,EAAE,MAAM,OAAO,KAAK,MAAM,IAAI,CAAC;CACnD;AAuCD,eAAO,MAAM,SAAS,gCAAgC,CAAC;AAEvD,0DAA0D;AAC1D,wBAAgB,KAAK,IAAI,KAAK,CAE7B;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,CAAC,GAAG,OAAO,KAAK,CAAC,GAAG,SAAS,CAE1D;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,CAAC,GAAG,OAAO,EAAE,UAAU,EAAE,MAAM,CAAC,GAAG,IAAI,CAKhE"}
1
+ {"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../src/sdk.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,YAAY,IAAI,UAAU,EAC1B,WAAW,IAAI,SAAS,EACzB,MAAM,MAAM,CAAC;AAGd,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAElD,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAEpE,MAAM,WAAW,KAAK;IACpB;;;;OAIG;IACH,GAAG,EAAE,CAAC,CAAC,GAAG,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAE3D,+DAA+D;IAC/D,GAAG,EAAE,CAAC,CAAC,GAAG,GAAG,EACX,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,KAC1B,OAAO,CAAC,UAAU,CAAC,CAAC;IAEzB;;;OAGG;IACH,MAAM,EAAE,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAErE;;;OAGG;IACH,KAAK,EAAE,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EACtB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,KACrC,OAAO,CAAC,SAAS,CAAC,CAAC;IAExB,qEAAqE;IACrE,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAEjC,gCAAgC;IAChC,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACjC,uDAAuD;IACvD,OAAO,EAAE,gBAAgB,CAAC,SAAS,CAAC,CAAC;IACrC,wFAAwF;IACxF,MAAM,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAEnC;;;;OAIG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;;;OAKG;IACH,MAAM,EAAE,CAAC,UAAU,EAAE,MAAM,OAAO,KAAK,MAAM,IAAI,CAAC;CACnD;AAuCD,eAAO,MAAM,SAAS,gCAAgC,CAAC;AAEvD,0DAA0D;AAC1D,wBAAgB,KAAK,IAAI,KAAK,CAE7B;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,CAAC,GAAG,OAAO,KAAK,CAAC,GAAG,SAAS,CAE1D;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,CAAC,GAAG,OAAO,EAAE,UAAU,EAAE,MAAM,CAAC,GAAG,IAAI,CAKhE"}
package/dist/sdk.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/sdk.ts"],
4
- "sourcesContent": ["/* eslint-disable @typescript-eslint/no-explicit-any */\n/**\n * Plugin runtime SDK \u2014 the surface a plugin author calls from inside the sandbox.\n *\n * Zenoh methods (pub/sub/action/serve) talk directly to the Zenoh router via\n * WebSocket. Topics are auto-namespaced to {pluginId}/... to prevent\n * cross-plugin collisions. A leading \"/\" bypasses namespacing (absolute path).\n *\n * notify() is still host-mediated (goes through the Muscat shell).\n */\nimport { createContext, useContext, useEffect, useRef } from \"react\";\nimport type {\n WsSubscriber as Subscriber,\n WsQueryable as Queryable,\n} from \"./ws\";\n// Type-only import \u2014 no runtime coupling to host-ui.ts (which itself has no\n// dependency back on sdk.ts, so this stays one-directional).\nimport type { PluginUIServices } from \"./host-ui\";\n\nexport type RbNotifyType = \"info\" | \"success\" | \"warning\" | \"error\";\n\nexport interface RbSdk {\n /**\n * Publish JSON payload to a topic.\n * \"sensor/data\" \u2192 {pluginId}/sensor/data\n * \"/broadcast/x\" \u2192 broadcast/x (absolute)\n */\n pub: <P = any>(topic: string, payload: P) => Promise<void>;\n\n /** Subscribe to a topic. Returns a handle with undeclare(). */\n sub: <P = any>(\n topic: string,\n handler: (payload: P) => void,\n ) => Promise<Subscriber>;\n\n /**\n * Send a request and await the first reply (Zenoh query).\n * Use this to call a backend handler registered with rb.serve() / ZenohRouter.\n */\n action: <R = any, P = any>(topic: string, payload?: P) => Promise<R>;\n\n /**\n * Register a queryable \u2014 the frontend responds to backend queries.\n * Mirror of rb.action() on the other side.\n */\n serve: <P = any, R = any>(\n topic: string,\n handler: (payload?: P) => R | Promise<R>,\n ) => Promise<Queryable>;\n\n /** Show a transient notification through the host's toast system. */\n toast: PluginUIServices[\"toast\"];\n\n /** Show a host alert dialog. */\n alert: PluginUIServices[\"alert\"];\n /** Show a host confirm dialog. Resolves true/false. */\n confirm: PluginUIServices[\"confirm\"];\n /** Show a host prompt dialog. Resolves the entered value, or undefined if cancelled. */\n prompt: PluginUIServices[\"prompt\"];\n\n /**\n * Host-provided initial data for this mount \u2014 e.g. the previously-saved\n * values when the host re-opens a form. `undefined` for a fresh mount.\n * Prefer the `useRbFormData()` hook over reading this directly.\n */\n data?: unknown;\n\n /**\n * Register a getter the host calls (synchronously) to collect this plugin's\n * save payload \u2014 e.g. when the host's Save button is clicked. The returned\n * value must be JSON-serialisable (the host persists it). Returns an\n * unregister fn. Prefer the `useRbSave()` hook.\n */\n onSave: (getPayload: () => unknown) => () => void;\n}\n\nconst noopSdk: RbSdk = {\n pub: async <P = any>(_topic?: string, _payload?: P) => {\n console.warn(\"[plugin-ui] rb.pub called outside of a host context\");\n },\n sub: async <P = any>(_topic?: string, _handler?: (payload: P) => void) => {\n console.warn(\"[plugin-ui] rb.sub called outside of a host context\");\n return { undeclare: async () => {} } as unknown as Subscriber;\n },\n action: async <R = any, P = any>(_topic?: string, _payload?: P) => {\n console.warn(\"[plugin-ui] rb.action called outside of a host context\");\n return undefined as R;\n },\n serve: async <P = any, R = any>(\n _topic?: string,\n _handler?: (payload?: P) => R,\n ) => {\n console.warn(\"[plugin-ui] rb.serve called outside of a host context\");\n return { undeclare: async () => {} } as unknown as Queryable;\n },\n toast: (params) => {\n console.warn(\"[plugin-ui] rb.toast (no host):\", params);\n },\n alert: async (_) => {\n console.warn(\"[plugin-ui] rb.alert called outside of a host context\");\n },\n confirm: async (_) => {\n console.warn(\"[plugin-ui] rb.confirm called outside of a host context\");\n return false;\n },\n prompt: async (_) => {\n console.warn(\"[plugin-ui] rb.prompt called outside of a host context\");\n return undefined;\n },\n data: undefined,\n onSave: () => () => {},\n};\n\nexport const RbContext = createContext<RbSdk>(noopSdk);\n\n/** Access the host SDK from within a plugin component. */\nexport function useRb(): RbSdk {\n return useContext(RbContext);\n}\n\n/**\n * Read the host-provided initial data for this mount (e.g. previously-saved\n * form values). `undefined` for a fresh mount.\n *\n * const saved = useRbFormData<MyArgs>();\n * const methods = useForm({ defaultValues: saved ?? DEFAULTS });\n */\nexport function useRbFormData<T = unknown>(): T | undefined {\n return useRb().data as T | undefined;\n}\n\n/**\n * Register a getter the host calls to collect this plugin's save payload when\n * the host's Save is clicked. The latest `getPayload` is always used, so it can\n * close over current state. The returned value must be JSON-serialisable.\n *\n * useRbSave(() => ({ args: getValues(), summary: buildSummary() }));\n */\nexport function useRbSave<T = unknown>(getPayload: () => T): void {\n const rb = useRb();\n const ref = useRef(getPayload);\n ref.current = getPayload;\n useEffect(() => rb.onSave(() => ref.current()), [rb]);\n}\n"],
5
- "mappings": "AAUA,SAAS,eAAe,YAAY,WAAW,cAAc;AAkE7D,MAAM,UAAiB;AAAA,EACrB,KAAK,OAAgB,QAAiB,aAAiB;AACrD,YAAQ,KAAK,qDAAqD;AAAA,EACpE;AAAA,EACA,KAAK,OAAgB,QAAiB,aAAoC;AACxE,YAAQ,KAAK,qDAAqD;AAClE,WAAO,EAAE,WAAW,YAAY;AAAA,IAAC,EAAE;AAAA,EACrC;AAAA,EACA,QAAQ,OAAyB,QAAiB,aAAiB;AACjE,YAAQ,KAAK,wDAAwD;AACrE,WAAO;AAAA,EACT;AAAA,EACA,OAAO,OACL,QACA,aACG;AACH,YAAQ,KAAK,uDAAuD;AACpE,WAAO,EAAE,WAAW,YAAY;AAAA,IAAC,EAAE;AAAA,EACrC;AAAA,EACA,OAAO,CAAC,WAAW;AACjB,YAAQ,KAAK,mCAAmC,MAAM;AAAA,EACxD;AAAA,EACA,OAAO,OAAO,MAAM;AAClB,YAAQ,KAAK,uDAAuD;AAAA,EACtE;AAAA,EACA,SAAS,OAAO,MAAM;AACpB,YAAQ,KAAK,yDAAyD;AACtE,WAAO;AAAA,EACT;AAAA,EACA,QAAQ,OAAO,MAAM;AACnB,YAAQ,KAAK,wDAAwD;AACrE,WAAO;AAAA,EACT;AAAA,EACA,MAAM;AAAA,EACN,QAAQ,MAAM,MAAM;AAAA,EAAC;AACvB;AAEO,MAAM,YAAY,cAAqB,OAAO;AAG9C,SAAS,QAAe;AAC7B,SAAO,WAAW,SAAS;AAC7B;AASO,SAAS,gBAA4C;AAC1D,SAAO,MAAM,EAAE;AACjB;AASO,SAAS,UAAuB,YAA2B;AAChE,QAAM,KAAK,MAAM;AACjB,QAAM,MAAM,OAAO,UAAU;AAC7B,MAAI,UAAU;AACd,YAAU,MAAM,GAAG,OAAO,MAAM,IAAI,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;AACtD;",
4
+ "sourcesContent": ["/* eslint-disable @typescript-eslint/no-explicit-any */\n/**\n * Plugin runtime SDK (`useRb()`).\n *\n * pub/sub/action/serve talk to the Zenoh router over WebSocket. Topics are\n * namespaced to {pluginId}/... ; a leading \"/\" makes the path absolute.\n * alert/confirm/prompt/toast delegate to the host.\n */\nimport { createContext, useContext, useEffect, useRef } from \"react\";\nimport type {\n WsSubscriber as Subscriber,\n WsQueryable as Queryable,\n} from \"./ws\";\n// Type-only import \u2014 no runtime coupling to host-ui.ts (which itself has no\n// dependency back on sdk.ts, so this stays one-directional).\nimport type { PluginUIServices } from \"./host-ui\";\n\nexport type RbNotifyType = \"info\" | \"success\" | \"warning\" | \"error\";\n\nexport interface RbSdk {\n /**\n * Publish JSON payload to a topic.\n * \"sensor/data\" \u2192 {pluginId}/sensor/data\n * \"/broadcast/x\" \u2192 broadcast/x (absolute)\n */\n pub: <P = any>(topic: string, payload: P) => Promise<void>;\n\n /** Subscribe to a topic. Returns a handle with undeclare(). */\n sub: <P = any>(\n topic: string,\n handler: (payload: P) => void,\n ) => Promise<Subscriber>;\n\n /**\n * Send a request and await the first reply (Zenoh query).\n * Use this to call a backend handler registered with rb.serve() / ZenohRouter.\n */\n action: <R = any, P = any>(topic: string, payload?: P) => Promise<R>;\n\n /**\n * Register a queryable \u2014 the frontend responds to backend queries.\n * Mirror of rb.action() on the other side.\n */\n serve: <P = any, R = any>(\n topic: string,\n handler: (payload?: P) => R | Promise<R>,\n ) => Promise<Queryable>;\n\n /** Show a transient notification through the host's toast system. */\n toast: PluginUIServices[\"toast\"];\n\n /** Show a host alert dialog. */\n alert: PluginUIServices[\"alert\"];\n /** Show a host confirm dialog. Resolves true/false. */\n confirm: PluginUIServices[\"confirm\"];\n /** Show a host prompt dialog. Resolves the entered value, or undefined if cancelled. */\n prompt: PluginUIServices[\"prompt\"];\n\n /**\n * Host-provided initial data for this mount \u2014 e.g. the previously-saved\n * values when the host re-opens a form. `undefined` for a fresh mount.\n * Prefer the `useRbFormData()` hook over reading this directly.\n */\n data?: unknown;\n\n /**\n * Register a getter the host calls (synchronously) to collect this plugin's\n * save payload \u2014 e.g. when the host's Save button is clicked. The returned\n * value must be JSON-serialisable (the host persists it). Returns an\n * unregister fn. Prefer the `useRbSave()` hook.\n */\n onSave: (getPayload: () => unknown) => () => void;\n}\n\nconst noopSdk: RbSdk = {\n pub: async <P = any>(_topic?: string, _payload?: P) => {\n console.warn(\"[plugin-ui] rb.pub called outside of a host context\");\n },\n sub: async <P = any>(_topic?: string, _handler?: (payload: P) => void) => {\n console.warn(\"[plugin-ui] rb.sub called outside of a host context\");\n return { undeclare: async () => {} } as unknown as Subscriber;\n },\n action: async <R = any, P = any>(_topic?: string, _payload?: P) => {\n console.warn(\"[plugin-ui] rb.action called outside of a host context\");\n return undefined as R;\n },\n serve: async <P = any, R = any>(\n _topic?: string,\n _handler?: (payload?: P) => R,\n ) => {\n console.warn(\"[plugin-ui] rb.serve called outside of a host context\");\n return { undeclare: async () => {} } as unknown as Queryable;\n },\n toast: (params) => {\n console.warn(\"[plugin-ui] rb.toast (no host):\", params);\n },\n alert: async (_) => {\n console.warn(\"[plugin-ui] rb.alert called outside of a host context\");\n },\n confirm: async (_) => {\n console.warn(\"[plugin-ui] rb.confirm called outside of a host context\");\n return false;\n },\n prompt: async (_) => {\n console.warn(\"[plugin-ui] rb.prompt called outside of a host context\");\n return undefined;\n },\n data: undefined,\n onSave: () => () => {},\n};\n\nexport const RbContext = createContext<RbSdk>(noopSdk);\n\n/** Access the host SDK from within a plugin component. */\nexport function useRb(): RbSdk {\n return useContext(RbContext);\n}\n\n/**\n * Read the host-provided initial data for this mount (e.g. previously-saved\n * form values). `undefined` for a fresh mount.\n *\n * const saved = useRbFormData<MyArgs>();\n * const methods = useForm({ defaultValues: saved ?? DEFAULTS });\n */\nexport function useRbFormData<T = unknown>(): T | undefined {\n return useRb().data as T | undefined;\n}\n\n/**\n * Register a getter the host calls to collect this plugin's save payload when\n * the host's Save is clicked. The latest `getPayload` is always used, so it can\n * close over current state. The returned value must be JSON-serialisable.\n *\n * useRbSave(() => ({ args: getValues(), summary: buildSummary() }));\n */\nexport function useRbSave<T = unknown>(getPayload: () => T): void {\n const rb = useRb();\n const ref = useRef(getPayload);\n ref.current = getPayload;\n useEffect(() => rb.onSave(() => ref.current()), [rb]);\n}\n"],
5
+ "mappings": "AAQA,SAAS,eAAe,YAAY,WAAW,cAAc;AAkE7D,MAAM,UAAiB;AAAA,EACrB,KAAK,OAAgB,QAAiB,aAAiB;AACrD,YAAQ,KAAK,qDAAqD;AAAA,EACpE;AAAA,EACA,KAAK,OAAgB,QAAiB,aAAoC;AACxE,YAAQ,KAAK,qDAAqD;AAClE,WAAO,EAAE,WAAW,YAAY;AAAA,IAAC,EAAE;AAAA,EACrC;AAAA,EACA,QAAQ,OAAyB,QAAiB,aAAiB;AACjE,YAAQ,KAAK,wDAAwD;AACrE,WAAO;AAAA,EACT;AAAA,EACA,OAAO,OACL,QACA,aACG;AACH,YAAQ,KAAK,uDAAuD;AACpE,WAAO,EAAE,WAAW,YAAY;AAAA,IAAC,EAAE;AAAA,EACrC;AAAA,EACA,OAAO,CAAC,WAAW;AACjB,YAAQ,KAAK,mCAAmC,MAAM;AAAA,EACxD;AAAA,EACA,OAAO,OAAO,MAAM;AAClB,YAAQ,KAAK,uDAAuD;AAAA,EACtE;AAAA,EACA,SAAS,OAAO,MAAM;AACpB,YAAQ,KAAK,yDAAyD;AACtE,WAAO;AAAA,EACT;AAAA,EACA,QAAQ,OAAO,MAAM;AACnB,YAAQ,KAAK,wDAAwD;AACrE,WAAO;AAAA,EACT;AAAA,EACA,MAAM;AAAA,EACN,QAAQ,MAAM,MAAM;AAAA,EAAC;AACvB;AAEO,MAAM,YAAY,cAAqB,OAAO;AAG9C,SAAS,QAAe;AAC7B,SAAO,WAAW,SAAS;AAC7B;AASO,SAAS,gBAA4C;AAC1D,SAAO,MAAM,EAAE;AACjB;AASO,SAAS,UAAuB,YAA2B;AAChE,QAAM,KAAK,MAAM;AACjB,QAAM,MAAM,OAAO,UAAU;AAC7B,MAAI,UAAU;AACd,YAAU,MAAM,GAAG,OAAO,MAAM,IAAI,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;AACtD;",
6
6
  "names": []
7
7
  }
@@ -8,10 +8,8 @@
8
8
  * isPluginPreviewMode,
9
9
  * } from "@rainbow-robotics/plugin-ui/vite-host";
10
10
  *
11
- * There's no "plugin runtime vendor bundle" to serve anymore — a plugin
12
- * either loads via its own dev server (dev) or as a plain ESM module whose
13
- * bare imports (react, rb-components, plugin-ui/*) resolve via the import map
14
- * this file injects, to the host's own already-loaded instances (build/robot).
11
+ * A built plugin's bare imports (react, rb-components, plugin-ui/*) resolve via
12
+ * the import map this file injects, to the host's already-loaded instances.
15
13
  * See ./host.tsx.
16
14
  */
17
15
  import type { Plugin } from "vite";
@@ -1 +1 @@
1
- {"version":3,"file":"viteHost.d.ts","sourceRoot":"","sources":["../src/viteHost.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAOnC,eAAO,MAAM,2BAA2B,UAMvC,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAgBnD,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,MAAM,MAAM,6BAA8B,CAAC;AAqK/E;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,QAAO,MA0B3C,CAAC;AAEH,mFAAmF;AACnF,eAAO,MAAM,0BAA0B,QAAO,MAe5C,CAAC;AAEH,kFAAkF;AAClF,eAAO,MAAM,yBAAyB,GAAI,MAAM;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB,KAAG,MAwBH,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,MAAM;IACrC,gEAAgE;IAChE,gBAAgB,EAAE,MAAM,CAAC;IACzB,2CAA2C;IAC3C,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC,KAAG,MAAM,EAOT,CAAC"}
1
+ {"version":3,"file":"viteHost.d.ts","sourceRoot":"","sources":["../src/viteHost.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAOnC,eAAO,MAAM,2BAA2B,UAMvC,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAgBnD,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,MAAM,MAAM,6BAA8B,CAAC;AAgK/E;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,QAAO,MA0B3C,CAAC;AAEH,mFAAmF;AACnF,eAAO,MAAM,0BAA0B,QAAO,MAe5C,CAAC;AAEH,kFAAkF;AAClF,eAAO,MAAM,yBAAyB,GAAI,MAAM;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB,KAAG,MAwBH,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,MAAM;IACrC,gEAAgE;IAChE,gBAAgB,EAAE,MAAM,CAAC;IACzB,2CAA2C;IAC3C,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC,KAAG,MAAM,EAOT,CAAC"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/viteHost.ts"],
4
- "sourcesContent": ["/**\n * Vite plugins for a HOST app previewing locally-developed plugins.\n * Host-agnostic \u2014 paths are parameters.\n *\n * import {\n * pluginPreviewRuntimePlugin,\n * pluginPreviewAssetsPlugin,\n * isPluginPreviewMode,\n * } from \"@rainbow-robotics/plugin-ui/vite-host\";\n *\n * There's no \"plugin runtime vendor bundle\" to serve anymore \u2014 a plugin\n * either loads via its own dev server (dev) or as a plain ESM module whose\n * bare imports (react, rb-components, plugin-ui/*) resolve via the import map\n * this file injects, to the host's own already-loaded instances (build/robot).\n * See ./host.tsx.\n */\nimport type { Plugin } from \"vite\";\nimport { createReadStream, existsSync, readFileSync, statSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { extname, resolve } from \"node:path\";\n\nconst require = createRequire(import.meta.url);\n\nexport const defaultPreviewAssetPrefixes = [\n \"/imgs/\",\n \"/fonts/\",\n \"/models/\",\n \"/sounds/\",\n \"/data/\",\n];\n\nexport const contentTypeByExt: Record<string, string> = {\n \".avif\": \"image/avif\",\n \".css\": \"text/css; charset=utf-8\",\n \".gif\": \"image/gif\",\n \".glb\": \"model/gltf-binary\",\n \".jpeg\": \"image/jpeg\",\n \".jpg\": \"image/jpeg\",\n \".js\": \"text/javascript; charset=utf-8\",\n \".json\": \"application/json; charset=utf-8\",\n \".mp3\": \"audio/mpeg\",\n \".png\": \"image/png\",\n \".svg\": \"image/svg+xml\",\n \".wasm\": \"application/wasm\",\n \".webp\": \"image/webp\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n};\n\nexport const isPluginPreviewMode = (mode: string) => mode === \"plugin-preview\";\n\n/* -------------------------------------------------------------------------- */\n/* Shared-runtime import map (plugins reuse the host's loaded instances) */\n/* -------------------------------------------------------------------------- */\n\nconst SHARED_BASE = \"/plugin-shared\";\n\n// Each shared specifier \u2192 named exports the shim re-exports from\n// window.__RB_SHARED__[specifier] (set by the host, e.g. nexus/src/pluginShared).\n// A built plugin imports these as bare externals; the host page's import map\n// (below) points them at these shims \u2192 the host's single instance.\n//\n// `names`: hardcoded list (small, stable modules).\n// `parseNamesFrom`: a require()-resolvable specifier whose SOURCE text we\n// parse for `export { ... }` names \u2014 used for react.tsx, which fully\n// re-exports the rb-components barrel (100+ names) and would drift if\n// hardcoded here. We parse the source text rather than importing/executing\n// the module: the barrel is a .ts source file, unresolvable/unexecutable by\n// plain Node (no loader), and even importing our OWN compiled dist/react.js\n// would still transitively hit that same unexecutable .ts re-export.\nconst SHARED_MODULES: Record<\n string,\n {\n file: string;\n names?: string[];\n parseNamesFrom?: { specifier: string; extraNames?: string[] };\n hasDefault?: boolean;\n }\n> = {\n react: {\n file: \"react\",\n hasDefault: true,\n names: [\n \"Children\", \"Component\", \"Fragment\", \"Profiler\", \"PureComponent\",\n \"StrictMode\", \"Suspense\", \"cloneElement\", \"createContext\",\n \"createElement\", \"createRef\", \"forwardRef\", \"isValidElement\", \"lazy\",\n \"memo\", \"startTransition\", \"useCallback\", \"useContext\", \"useDebugValue\",\n \"useDeferredValue\", \"useEffect\", \"useId\", \"useImperativeHandle\",\n \"useInsertionEffect\", \"useLayoutEffect\", \"useMemo\", \"useReducer\",\n \"useRef\", \"useState\", \"useSyncExternalStore\", \"useTransition\", \"version\",\n ],\n },\n \"react/jsx-runtime\": {\n file: \"react_jsx-runtime\",\n names: [\"Fragment\", \"jsx\", \"jsxs\"],\n },\n \"react/jsx-dev-runtime\": {\n file: \"react_jsx-dev-runtime\",\n names: [\"Fragment\", \"jsxDEV\"],\n },\n \"react-dom\": {\n file: \"react-dom\",\n hasDefault: true,\n names: [\n \"__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED\",\n \"createPortal\",\n \"createRoot\",\n \"findDOMNode\",\n \"flushSync\",\n \"hydrate\",\n \"hydrateRoot\",\n \"render\",\n \"unmountComponentAtNode\",\n \"unstable_batchedUpdates\",\n \"unstable_renderSubtreeIntoContainer\",\n \"version\",\n ],\n },\n \"react-dom/client\": {\n file: \"react-dom_client\",\n names: [\"createRoot\", \"hydrateRoot\"],\n },\n \"@rainbow-robotics/plugin-ui/react\": {\n file: \"plugin-ui_react\",\n // react.tsx re-exports the full rb-components barrel (export *) plus the sdk\n // hooks.\n parseNamesFrom: {\n specifier: \"@repo/rb-components/new_components\",\n extraNames: [\"useRb\", \"useRbFormData\", \"useRbSave\"],\n },\n },\n \"@rainbow-robotics/plugin-ui/sdk\": {\n file: \"plugin-ui_sdk\",\n names: [\"RbContext\", \"useRb\", \"useRbFormData\", \"useRbSave\"],\n },\n \"@rainbow-robotics/plugin-ui/host-ui\": {\n file: \"plugin-ui_host-ui\",\n names: [\n \"alert\",\n \"confirm\",\n \"getHostServices\",\n \"prompt\",\n \"setHostServices\",\n \"toast\",\n ],\n },\n \"@rainbow-robotics/plugin-ui/ws\": {\n file: \"plugin-ui_ws\",\n names: [\"RbWs\", \"createLazyRbSdk\"],\n },\n};\n\n// Parses `export { a, b as c, ... }` value names out of a source file's text\n// (skips `export type { ... }` blocks and individual `type X` entries within\n// a mixed block \u2014 types don't exist as runtime module properties). Static\n// text parsing, not module execution, so it works for .ts source files with\n// no loader and has no ESM-resolution footguns.\nfunction parseExportedValueNames(source: string): string[] {\n const names = new Set<string>();\n const blockRe = /export\\s+(type\\s+)?\\{([^}]*)\\}/g;\n let match: RegExpExecArray | null;\n while ((match = blockRe.exec(source))) {\n if (match[1]) continue; // whole `export type { ... }` block\n for (const rawEntry of match[2].split(\",\")) {\n const entry = rawEntry.trim();\n if (!entry || entry.startsWith(\"type \")) continue;\n const asMatch = entry.match(/^\\S+\\s+as\\s+(\\S+)$/);\n names.add(asMatch ? asMatch[1] : entry);\n }\n }\n return [...names];\n}\n\nconst nameCache = new Map<string, string[]>();\n\nfunction resolveNames(spec: string): string[] {\n const mod = SHARED_MODULES[spec];\n if (mod.names) return mod.names;\n if (!mod.parseNamesFrom) {\n throw new Error(`[plugin-ui] ${spec} has neither \"names\" nor \"parseNamesFrom\"`);\n }\n if (!nameCache.has(spec)) {\n const { specifier, extraNames = [] } = mod.parseNamesFrom;\n const sourcePath = require.resolve(specifier);\n const parsed = parseExportedValueNames(readFileSync(sourcePath, \"utf8\"));\n nameCache.set(spec, [...new Set([...parsed, ...extraNames])]);\n }\n return nameCache.get(spec)!;\n}\n\nfunction shimSource(spec: string): string {\n const mod = SHARED_MODULES[spec];\n const names = resolveNames(spec);\n const g = `(globalThis.__RB_SHARED__||{})[${JSON.stringify(spec)}]`;\n return (\n `const m = ${g};\\n` +\n `if (!m) throw new Error(\"[plugin-ui] host shared module not registered: ${spec}\");\\n` +\n `export const { ${names.join(\", \")} } = m;\\n` +\n (mod.hasDefault ? `export default (m.default ?? m);\\n` : \"\")\n );\n}\n\nconst fileToSpec = Object.fromEntries(\n Object.entries(SHARED_MODULES).map(([spec, { file }]) => [file, spec]),\n);\n\nfunction importMapScript(): string {\n const imports: Record<string, string> = {};\n for (const [spec, { file }] of Object.entries(SHARED_MODULES)) {\n imports[spec] = `${SHARED_BASE}/${file}.js`;\n }\n return `<script type=\"importmap\">${JSON.stringify({ imports })}</script>`;\n}\n\n/**\n * Serves the shared-runtime shim modules at /plugin-shared/*.js and injects the\n * import map so a dynamically-imported plugin's bare `import \"react\"` /\n * `import \"@rainbow-robotics/plugin-ui/react\"` etc. resolve to the host's OWN\n * instances (via window.__RB_SHARED__). Applied in all modes \u2014 any nexus build\n * that can host plugins needs it.\n */\nexport const pluginSharedRuntimePlugin = (): Plugin => ({\n name: \"plugin-ui-shared-runtime\",\n transformIndexHtml(html) {\n return html.replace(\"</head>\", ` ${importMapScript()}\\n </head>`);\n },\n configureServer(server) {\n server.middlewares.use((req, res, next) => {\n const url = req.url?.split(\"?\")[0];\n if (!url?.startsWith(`${SHARED_BASE}/`)) return next();\n const file = url.slice(`${SHARED_BASE}/`.length).replace(/\\.js$/, \"\");\n const spec = fileToSpec[file];\n if (!spec) return next();\n res.setHeader(\"Content-Type\", \"text/javascript; charset=utf-8\");\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.end(shimSource(spec));\n });\n },\n generateBundle() {\n for (const [spec, { file }] of Object.entries(SHARED_MODULES)) {\n this.emitFile({\n type: \"asset\",\n fileName: `plugin-shared/${file}.js`,\n source: shimSource(spec),\n });\n }\n },\n});\n\n/** Inject the preview-host runtime flags into index.html (plugin-preview only). */\nexport const pluginPreviewRuntimePlugin = (): Plugin => ({\n name: \"plugin-ui-preview-runtime\",\n apply: (_config, { mode }) => isPluginPreviewMode(mode),\n transformIndexHtml(html) {\n return html.replace(\n \"</head>\",\n [\n \" <script>\",\n \" window.__RB_PLUGIN_PREVIEW_HOST__ = true;\",\n \" window.__RB_ASSETS_BASE_URL__ = window.location.origin;\",\n \" </script>\",\n \" </head>\",\n ].join(\"\\n\"),\n );\n },\n});\n\n/** Serve the host's static assets (imgs/fonts/...) in plugin-preview dev mode. */\nexport const pluginPreviewAssetsPlugin = (opts: {\n assetsDir: string;\n prefixes?: string[];\n}): Plugin => {\n const { assetsDir, prefixes = defaultPreviewAssetPrefixes } = opts;\n return {\n name: \"plugin-ui-preview-assets\",\n apply: (_config, { command, mode }) =>\n command === \"serve\" && isPluginPreviewMode(mode),\n configureServer(server) {\n server.middlewares.use((req, res, next) => {\n const url = req.url?.split(\"?\")[0];\n if (!url || !prefixes.some((prefix) => url.startsWith(prefix)))\n return next();\n\n const decodedUrl = decodeURIComponent(url);\n const filePath = resolve(assetsDir, `.${decodedUrl}`);\n if (!filePath.startsWith(assetsDir)) return next();\n if (!existsSync(filePath) || !statSync(filePath).isFile()) return next();\n\n const contentType = contentTypeByExt[extname(filePath)];\n if (contentType) res.setHeader(\"Content-Type\", contentType);\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n createReadStream(filePath).pipe(res);\n });\n },\n };\n};\n\n/**\n * All host-side plugin-ui Vite plugins in one call. Each self-gates by mode, so\n * a host just spreads this into its `plugins` array.\n *\n * plugins: [muscatVitePlugin({ previewAssetsDir: resolve(here, \"../assets/public\") })]\n */\nexport const muscatVitePlugin = (opts: {\n /** Host static assets dir served in plugin-preview dev mode. */\n previewAssetsDir: string;\n /** Override preview asset URL prefixes. */\n previewAssetPrefixes?: string[];\n}): Plugin[] => [\n pluginSharedRuntimePlugin(),\n pluginPreviewRuntimePlugin(),\n pluginPreviewAssetsPlugin({\n assetsDir: opts.previewAssetsDir,\n prefixes: opts.previewAssetPrefixes,\n }),\n];\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Vite plugins for a HOST app previewing locally-developed plugins.\n * Host-agnostic \u2014 paths are parameters.\n *\n * import {\n * pluginPreviewRuntimePlugin,\n * pluginPreviewAssetsPlugin,\n * isPluginPreviewMode,\n * } from \"@rainbow-robotics/plugin-ui/vite-host\";\n *\n * A built plugin's bare imports (react, rb-components, plugin-ui/*) resolve via\n * the import map this file injects, to the host's already-loaded instances.\n * See ./host.tsx.\n */\nimport type { Plugin } from \"vite\";\nimport { createReadStream, existsSync, readFileSync, statSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { extname, resolve } from \"node:path\";\n\nconst require = createRequire(import.meta.url);\n\nexport const defaultPreviewAssetPrefixes = [\n \"/imgs/\",\n \"/fonts/\",\n \"/models/\",\n \"/sounds/\",\n \"/data/\",\n];\n\nexport const contentTypeByExt: Record<string, string> = {\n \".avif\": \"image/avif\",\n \".css\": \"text/css; charset=utf-8\",\n \".gif\": \"image/gif\",\n \".glb\": \"model/gltf-binary\",\n \".jpeg\": \"image/jpeg\",\n \".jpg\": \"image/jpeg\",\n \".js\": \"text/javascript; charset=utf-8\",\n \".json\": \"application/json; charset=utf-8\",\n \".mp3\": \"audio/mpeg\",\n \".png\": \"image/png\",\n \".svg\": \"image/svg+xml\",\n \".wasm\": \"application/wasm\",\n \".webp\": \"image/webp\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n};\n\nexport const isPluginPreviewMode = (mode: string) => mode === \"plugin-preview\";\n\n/* -------------------------------------------------------------------------- */\n/* Shared-runtime import map (plugins reuse the host's loaded instances) */\n/* -------------------------------------------------------------------------- */\n\nconst SHARED_BASE = \"/plugin-shared\";\n\n// Each shared specifier \u2192 the named exports its shim re-exports from\n// window.__RB_SHARED__[specifier] (set by the host, e.g. nexus/src/pluginShared).\n//\n// `names`: hardcoded list (small, stable modules).\n// `parseNamesFrom`: parse the export names out of a source file's text \u2014 used\n// for react.tsx, which re-exports the whole rb-components barrel (100+ names).\n// We text-parse rather than import it: the barrel is .ts source, which plain\n// Node can't execute.\nconst SHARED_MODULES: Record<\n string,\n {\n file: string;\n names?: string[];\n parseNamesFrom?: { specifier: string; extraNames?: string[] };\n hasDefault?: boolean;\n }\n> = {\n react: {\n file: \"react\",\n hasDefault: true,\n names: [\n \"Children\", \"Component\", \"Fragment\", \"Profiler\", \"PureComponent\",\n \"StrictMode\", \"Suspense\", \"cloneElement\", \"createContext\",\n \"createElement\", \"createRef\", \"forwardRef\", \"isValidElement\", \"lazy\",\n \"memo\", \"startTransition\", \"useCallback\", \"useContext\", \"useDebugValue\",\n \"useDeferredValue\", \"useEffect\", \"useId\", \"useImperativeHandle\",\n \"useInsertionEffect\", \"useLayoutEffect\", \"useMemo\", \"useReducer\",\n \"useRef\", \"useState\", \"useSyncExternalStore\", \"useTransition\", \"version\",\n ],\n },\n \"react/jsx-runtime\": {\n file: \"react_jsx-runtime\",\n names: [\"Fragment\", \"jsx\", \"jsxs\"],\n },\n \"react/jsx-dev-runtime\": {\n file: \"react_jsx-dev-runtime\",\n names: [\"Fragment\", \"jsxDEV\"],\n },\n \"react-dom\": {\n file: \"react-dom\",\n hasDefault: true,\n names: [\n \"__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED\",\n \"createPortal\",\n \"createRoot\",\n \"findDOMNode\",\n \"flushSync\",\n \"hydrate\",\n \"hydrateRoot\",\n \"render\",\n \"unmountComponentAtNode\",\n \"unstable_batchedUpdates\",\n \"unstable_renderSubtreeIntoContainer\",\n \"version\",\n ],\n },\n \"react-dom/client\": {\n file: \"react-dom_client\",\n names: [\"createRoot\", \"hydrateRoot\"],\n },\n \"@rainbow-robotics/plugin-ui/react\": {\n file: \"plugin-ui_react\",\n // react.tsx re-exports the full rb-components barrel (export *) plus the sdk\n // hooks.\n parseNamesFrom: {\n specifier: \"@repo/rb-components/new_components\",\n extraNames: [\"useRb\", \"useRbFormData\", \"useRbSave\"],\n },\n },\n \"@rainbow-robotics/plugin-ui/sdk\": {\n file: \"plugin-ui_sdk\",\n names: [\"RbContext\", \"useRb\", \"useRbFormData\", \"useRbSave\"],\n },\n \"@rainbow-robotics/plugin-ui/host-ui\": {\n file: \"plugin-ui_host-ui\",\n names: [\n \"alert\",\n \"confirm\",\n \"getHostServices\",\n \"prompt\",\n \"setHostServices\",\n \"toast\",\n ],\n },\n \"@rainbow-robotics/plugin-ui/ws\": {\n file: \"plugin-ui_ws\",\n names: [\"RbWs\", \"createLazyRbSdk\"],\n },\n};\n\n// Parses `export { a, b as c, ... }` value names out of a source file's text\n// (skips `export type { ... }` blocks and individual `type X` entries within\n// a mixed block \u2014 types don't exist as runtime module properties). Static\n// text parsing, not module execution, so it works for .ts source files with\n// no loader and has no ESM-resolution footguns.\nfunction parseExportedValueNames(source: string): string[] {\n const names = new Set<string>();\n const blockRe = /export\\s+(type\\s+)?\\{([^}]*)\\}/g;\n let match: RegExpExecArray | null;\n while ((match = blockRe.exec(source))) {\n if (match[1]) continue; // whole `export type { ... }` block\n for (const rawEntry of match[2].split(\",\")) {\n const entry = rawEntry.trim();\n if (!entry || entry.startsWith(\"type \")) continue;\n const asMatch = entry.match(/^\\S+\\s+as\\s+(\\S+)$/);\n names.add(asMatch ? asMatch[1] : entry);\n }\n }\n return [...names];\n}\n\nconst nameCache = new Map<string, string[]>();\n\nfunction resolveNames(spec: string): string[] {\n const mod = SHARED_MODULES[spec];\n if (mod.names) return mod.names;\n if (!mod.parseNamesFrom) {\n throw new Error(`[plugin-ui] ${spec} has neither \"names\" nor \"parseNamesFrom\"`);\n }\n if (!nameCache.has(spec)) {\n const { specifier, extraNames = [] } = mod.parseNamesFrom;\n const sourcePath = require.resolve(specifier);\n const parsed = parseExportedValueNames(readFileSync(sourcePath, \"utf8\"));\n nameCache.set(spec, [...new Set([...parsed, ...extraNames])]);\n }\n return nameCache.get(spec)!;\n}\n\nfunction shimSource(spec: string): string {\n const mod = SHARED_MODULES[spec];\n const names = resolveNames(spec);\n const g = `(globalThis.__RB_SHARED__||{})[${JSON.stringify(spec)}]`;\n return (\n `const m = ${g};\\n` +\n `if (!m) throw new Error(\"[plugin-ui] host shared module not registered: ${spec}\");\\n` +\n `export const { ${names.join(\", \")} } = m;\\n` +\n (mod.hasDefault ? `export default (m.default ?? m);\\n` : \"\")\n );\n}\n\nconst fileToSpec = Object.fromEntries(\n Object.entries(SHARED_MODULES).map(([spec, { file }]) => [file, spec]),\n);\n\nfunction importMapScript(): string {\n const imports: Record<string, string> = {};\n for (const [spec, { file }] of Object.entries(SHARED_MODULES)) {\n imports[spec] = `${SHARED_BASE}/${file}.js`;\n }\n return `<script type=\"importmap\">${JSON.stringify({ imports })}</script>`;\n}\n\n/**\n * Serves the shared-runtime shim modules at /plugin-shared/*.js and injects the\n * import map so a dynamically-imported plugin's bare `import \"react\"` /\n * `import \"@rainbow-robotics/plugin-ui/react\"` etc. resolve to the host's OWN\n * instances (via window.__RB_SHARED__). Applied in all modes \u2014 any nexus build\n * that can host plugins needs it.\n */\nexport const pluginSharedRuntimePlugin = (): Plugin => ({\n name: \"plugin-ui-shared-runtime\",\n transformIndexHtml(html) {\n return html.replace(\"</head>\", ` ${importMapScript()}\\n </head>`);\n },\n configureServer(server) {\n server.middlewares.use((req, res, next) => {\n const url = req.url?.split(\"?\")[0];\n if (!url?.startsWith(`${SHARED_BASE}/`)) return next();\n const file = url.slice(`${SHARED_BASE}/`.length).replace(/\\.js$/, \"\");\n const spec = fileToSpec[file];\n if (!spec) return next();\n res.setHeader(\"Content-Type\", \"text/javascript; charset=utf-8\");\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.end(shimSource(spec));\n });\n },\n generateBundle() {\n for (const [spec, { file }] of Object.entries(SHARED_MODULES)) {\n this.emitFile({\n type: \"asset\",\n fileName: `plugin-shared/${file}.js`,\n source: shimSource(spec),\n });\n }\n },\n});\n\n/** Inject the preview-host runtime flags into index.html (plugin-preview only). */\nexport const pluginPreviewRuntimePlugin = (): Plugin => ({\n name: \"plugin-ui-preview-runtime\",\n apply: (_config, { mode }) => isPluginPreviewMode(mode),\n transformIndexHtml(html) {\n return html.replace(\n \"</head>\",\n [\n \" <script>\",\n \" window.__RB_PLUGIN_PREVIEW_HOST__ = true;\",\n \" window.__RB_ASSETS_BASE_URL__ = window.location.origin;\",\n \" </script>\",\n \" </head>\",\n ].join(\"\\n\"),\n );\n },\n});\n\n/** Serve the host's static assets (imgs/fonts/...) in plugin-preview dev mode. */\nexport const pluginPreviewAssetsPlugin = (opts: {\n assetsDir: string;\n prefixes?: string[];\n}): Plugin => {\n const { assetsDir, prefixes = defaultPreviewAssetPrefixes } = opts;\n return {\n name: \"plugin-ui-preview-assets\",\n apply: (_config, { command, mode }) =>\n command === \"serve\" && isPluginPreviewMode(mode),\n configureServer(server) {\n server.middlewares.use((req, res, next) => {\n const url = req.url?.split(\"?\")[0];\n if (!url || !prefixes.some((prefix) => url.startsWith(prefix)))\n return next();\n\n const decodedUrl = decodeURIComponent(url);\n const filePath = resolve(assetsDir, `.${decodedUrl}`);\n if (!filePath.startsWith(assetsDir)) return next();\n if (!existsSync(filePath) || !statSync(filePath).isFile()) return next();\n\n const contentType = contentTypeByExt[extname(filePath)];\n if (contentType) res.setHeader(\"Content-Type\", contentType);\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n createReadStream(filePath).pipe(res);\n });\n },\n };\n};\n\n/**\n * All host-side plugin-ui Vite plugins in one call. Each self-gates by mode, so\n * a host just spreads this into its `plugins` array.\n *\n * plugins: [muscatVitePlugin({ previewAssetsDir: resolve(here, \"../assets/public\") })]\n */\nexport const muscatVitePlugin = (opts: {\n /** Host static assets dir served in plugin-preview dev mode. */\n previewAssetsDir: string;\n /** Override preview asset URL prefixes. */\n previewAssetPrefixes?: string[];\n}): Plugin[] => [\n pluginSharedRuntimePlugin(),\n pluginPreviewRuntimePlugin(),\n pluginPreviewAssetsPlugin({\n assetsDir: opts.previewAssetsDir,\n prefixes: opts.previewAssetPrefixes,\n }),\n];\n"],
5
+ "mappings": "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",
6
6
  "names": ["require"]
7
7
  }
package/dist/ws.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../src/ws.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAElD,MAAM,WAAW,QAAQ;IACvB,SAAS,IAAI,IAAI,CAAC;CACnB;AAED,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC;AACpC,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC;AAMnC,qBAAa,IAAI;IACf,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAGzB;IACJ,OAAO,CAAC,QAAQ,CAAC,cAAc,CAG3B;IAEJ,OAAO;IA+BP,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoB5D,OAAO,CAAC,IAAI;IAKN,GAAG,CAAC,CAAC,GAAG,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAKtD,GAAG,CAAC,CAAC,GAAG,GAAG,EACf,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,GAC5B,OAAO,CAAC,YAAY,CAAC;IAsBlB,MAAM,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAehE,KAAK,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EAC1B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACvC,OAAO,CAAC,WAAW,CAAC;IAYvB,KAAK,IAAI,IAAI;CAGd;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,EACxB,QAAQ,EAAE,gBAAgB,EAI1B,KAAK,CAAC,EAAE;IACN,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,OAAO,KAAK,MAAM,IAAI,CAAC;CACpD;iBAIoB,MAAM,WAAW,GAAG;iBAG9B,MAAM,WAEJ,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI;oBAGX,MAAM,YAAY,GAAG;mBAGlC,MAAM,WAEJ,CAAC,OAAO,CAAC,EAAE,GAAG,KAAK,GAAG;;aAlLtB,CAAC;;YAIE,CAAC;mBAGF,CAAC;yBAA+B,CAAC;0BAC7B,CAAC;gBAAuB,CAAC;eAAqB,CAAC;;;aApB1B,CAAA;;kBAC9B,CAAC;;;aACgB,CAAC;;kBAExB,CAAC;;;;;;aAEF,CAAD;;aAED,CAAC;mBAAyB,CAAC;kBAC3B,CAAA;;;;;;yBAmKuB,MAAM,OAAO,KAAK,MAAM,IAAI;EA2BrD"}
1
+ {"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../src/ws.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAElD,MAAM,WAAW,QAAQ;IACvB,SAAS,IAAI,IAAI,CAAC;CACnB;AAED,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC;AACpC,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC;AAMnC,qBAAa,IAAI;IACf,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAGzB;IACJ,OAAO,CAAC,QAAQ,CAAC,cAAc,CAG3B;IAEJ,OAAO;IA+BP,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoB5D,OAAO,CAAC,IAAI;IAKN,GAAG,CAAC,CAAC,GAAG,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAKtD,GAAG,CAAC,CAAC,GAAG,GAAG,EACf,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,GAC5B,OAAO,CAAC,YAAY,CAAC;IAsBlB,MAAM,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAgBhE,KAAK,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EAC1B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACvC,OAAO,CAAC,WAAW,CAAC;IAYvB,KAAK,IAAI,IAAI;CAGd;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,EACxB,QAAQ,EAAE,gBAAgB,EAI1B,KAAK,CAAC,EAAE;IACN,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,OAAO,KAAK,MAAM,IAAI,CAAC;CACpD;iBAIoB,MAAM,WAAW,GAAG;iBAG9B,MAAM,WAEJ,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI;oBAGX,MAAM,YAAY,GAAG;mBAGlC,MAAM,WAEJ,CAAC,OAAO,CAAC,EAAE,GAAG,KAAK,GAAG;;aAnLtB,CAAC;;YAIE,CAAC;mBAGF,CAAC;yBAA+B,CAAC;0BAC7B,CAAC;gBAAuB,CAAC;eAAqB,CAAC;;;aApB1B,CAAA;;kBAC9B,CAAC;;;aACgB,CAAC;;kBAExB,CAAC;;;;;;aAEF,CAAD;;aAED,CAAC;mBAAyB,CAAC;kBAC3B,CAAA;;;;;;yBAoKuB,MAAM,OAAO,KAAK,MAAM,IAAI;EA2BrD"}
package/dist/ws.js CHANGED
@@ -78,12 +78,13 @@ class RbWs {
78
78
  }
79
79
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
80
80
  async action(topic, payload) {
81
- return new Promise((resolve) => {
81
+ return new Promise((resolve, reject) => {
82
82
  this._socket.timeout(1e4).emit(
83
83
  "action",
84
84
  { topic: this._key(topic), payload },
85
- (_err, result) => {
86
- resolve(_err ? void 0 : result);
85
+ (err, result) => {
86
+ if (err) reject(err);
87
+ else resolve(result);
87
88
  }
88
89
  );
89
90
  });
package/dist/ws.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/ws.ts"],
4
- "sourcesContent": ["/**\n * Socket.IO communication client for plugin frontends.\n *\n * Topics are automatically namespaced to prevent cross-plugin collisions:\n * \"sensor/data\" \u2192 \"{pluginId}/sensor/data\"\n * \"/broadcast/x\" \u2192 \"broadcast/x\" (leading slash = absolute)\n *\n * Browser \u2190\u2192 Python backend protocol:\n * browser emits : sub, unsub, pub, action (with ack), serve_reply\n * server emits : msg, serve_call\n */\nimport { io, type Socket } from \"socket.io-client\";\n// Type-only \u2014 no runtime coupling to host-ui.ts.\nimport type { PluginUIServices } from \"./host-ui\";\n\nexport interface WsHandle {\n undeclare(): void;\n}\n\nexport type WsSubscriber = WsHandle;\nexport type WsQueryable = WsHandle;\n\nfunction resolveTopic(pluginId: string, topic: string): string {\n return topic.startsWith(\"/\") ? topic.slice(1) : `${pluginId}/${topic}`;\n}\n\nexport class RbWs {\n private readonly _socket: Socket;\n private readonly _pluginId: string;\n private readonly _subHandlers = new Map<\n string,\n Set<(payload: unknown) => void>\n >();\n private readonly _serveHandlers = new Map<\n string,\n (payload?: unknown) => unknown | Promise<unknown>\n >();\n\n private constructor(socket: Socket, pluginId: string) {\n this._socket = socket;\n this._pluginId = pluginId;\n\n // Deliver incoming pub/sub messages to registered handlers\n socket.on(\n \"msg\",\n ({ topic, payload }: { topic: string; payload: unknown }) => {\n this._subHandlers.get(topic)?.forEach((h) => h(payload));\n },\n );\n\n // Python calls a browser-side serve handler\n socket.on(\n \"serve_call\",\n async ({\n id,\n topic,\n payload,\n }: {\n id: string;\n topic: string;\n payload: unknown;\n }) => {\n const handler = this._serveHandlers.get(topic);\n const result = handler ? await handler(payload) : undefined;\n socket.emit(\"serve_reply\", { id, payload: result });\n },\n );\n }\n\n static connect(url: string, pluginId: string): Promise<RbWs> {\n return new Promise((resolve, reject) => {\n const socket = io(url, { transports: [\"websocket\"] });\n const onConnect = () => {\n cleanup();\n resolve(new RbWs(socket, pluginId));\n };\n const onError = (err: Error) => {\n cleanup();\n reject(err);\n };\n const cleanup = () => {\n socket.off(\"connect\", onConnect);\n socket.off(\"connect_error\", onError);\n };\n socket.once(\"connect\", onConnect);\n socket.once(\"connect_error\", onError);\n });\n }\n\n private _key(topic: string): string {\n return resolveTopic(this._pluginId, topic);\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async pub<P = any>(topic: string, payload: P): Promise<void> {\n this._socket.emit(\"pub\", { topic: this._key(topic), payload });\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async sub<P = any>(\n topic: string,\n handler: (payload: P) => void,\n ): Promise<WsSubscriber> {\n const key = this._key(topic);\n if (!this._subHandlers.has(key)) {\n this._subHandlers.set(key, new Set());\n this._socket.emit(\"sub\", { topic: key });\n }\n const h = handler as (payload: unknown) => void;\n this._subHandlers.get(key)!.add(h);\n return {\n undeclare: () => {\n const set = this._subHandlers.get(key);\n if (!set) return;\n set.delete(h);\n if (set.size === 0) {\n this._subHandlers.delete(key);\n this._socket.emit(\"unsub\", { topic: key });\n }\n },\n };\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async action<R = any, P = any>(topic: string, payload?: P): Promise<R> {\n return new Promise((resolve) => {\n this._socket\n .timeout(10_000)\n .emit(\n \"action\",\n { topic: this._key(topic), payload },\n (_err: Error | null, result: R) => {\n resolve(_err ? (undefined as R) : result);\n },\n );\n });\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async serve<P = any, R = any>(\n topic: string,\n handler: (payload?: P) => R | Promise<R>,\n ): Promise<WsQueryable> {\n const key = this._key(topic);\n this._serveHandlers.set(key, handler as (payload?: unknown) => unknown);\n this._socket.emit(\"serve_register\", { topic: key });\n return {\n undeclare: () => {\n this._serveHandlers.delete(key);\n this._socket.emit(\"serve_unregister\", { topic: key });\n },\n };\n }\n\n close(): void {\n this._socket.disconnect();\n }\n}\n\n/**\n * Builds an rb-sdk-shaped object (pub/sub/action/serve + the passed-through\n * toast/alert/confirm/prompt) that lazily awaits a not-yet-connected `RbWs` \u2014\n * so callers don't block their first render on the WebSocket handshake (there\n * may be no backend reachable yet). Shared by both host mount paths (see\n * ./host.tsx and muscat-plugin-kit's dev bootstrap), which otherwise\n * duplicate this wiring.\n */\nexport function createLazyRbSdk(\n wsPromise: Promise<RbWs>,\n services: PluginUIServices,\n // Per-mount, host-provided (unlike `services`, which is a global singleton):\n // initial `data` and the `onSave` registration the host reads back. Plain\n // values/closures, so they cross the dev React-instance boundary safely.\n mount?: {\n data?: unknown;\n onSave?: (getPayload: () => unknown) => () => void;\n },\n) {\n return {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n pub: async (topic: string, payload: any) =>\n (await wsPromise).pub(topic, payload),\n sub: async (\n topic: string,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n handler: (payload: any) => void,\n ) => (await wsPromise).sub(topic, handler),\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n action: async (topic: string, payload?: any) =>\n (await wsPromise).action(topic, payload),\n serve: async (\n topic: string,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n handler: (payload?: any) => any,\n ) => (await wsPromise).serve(topic, handler),\n toast: services.toast,\n alert: services.alert,\n confirm: services.confirm,\n prompt: services.prompt,\n data: mount?.data,\n onSave: mount?.onSave ?? (() => () => {}),\n };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Socket.IO communication client for plugin frontends.\n *\n * Topics are automatically namespaced to prevent cross-plugin collisions:\n * \"sensor/data\" \u2192 \"{pluginId}/sensor/data\"\n * \"/broadcast/x\" \u2192 \"broadcast/x\" (leading slash = absolute)\n *\n * Browser \u2190\u2192 Python backend protocol:\n * browser emits : sub, unsub, pub, action (with ack), serve_reply\n * server emits : msg, serve_call\n */\nimport { io, type Socket } from \"socket.io-client\";\n// Type-only \u2014 no runtime coupling to host-ui.ts.\nimport type { PluginUIServices } from \"./host-ui\";\n\nexport interface WsHandle {\n undeclare(): void;\n}\n\nexport type WsSubscriber = WsHandle;\nexport type WsQueryable = WsHandle;\n\nfunction resolveTopic(pluginId: string, topic: string): string {\n return topic.startsWith(\"/\") ? topic.slice(1) : `${pluginId}/${topic}`;\n}\n\nexport class RbWs {\n private readonly _socket: Socket;\n private readonly _pluginId: string;\n private readonly _subHandlers = new Map<\n string,\n Set<(payload: unknown) => void>\n >();\n private readonly _serveHandlers = new Map<\n string,\n (payload?: unknown) => unknown | Promise<unknown>\n >();\n\n private constructor(socket: Socket, pluginId: string) {\n this._socket = socket;\n this._pluginId = pluginId;\n\n // Deliver incoming pub/sub messages to registered handlers\n socket.on(\n \"msg\",\n ({ topic, payload }: { topic: string; payload: unknown }) => {\n this._subHandlers.get(topic)?.forEach((h) => h(payload));\n },\n );\n\n // Python calls a browser-side serve handler\n socket.on(\n \"serve_call\",\n async ({\n id,\n topic,\n payload,\n }: {\n id: string;\n topic: string;\n payload: unknown;\n }) => {\n const handler = this._serveHandlers.get(topic);\n const result = handler ? await handler(payload) : undefined;\n socket.emit(\"serve_reply\", { id, payload: result });\n },\n );\n }\n\n static connect(url: string, pluginId: string): Promise<RbWs> {\n return new Promise((resolve, reject) => {\n const socket = io(url, { transports: [\"websocket\"] });\n const onConnect = () => {\n cleanup();\n resolve(new RbWs(socket, pluginId));\n };\n const onError = (err: Error) => {\n cleanup();\n reject(err);\n };\n const cleanup = () => {\n socket.off(\"connect\", onConnect);\n socket.off(\"connect_error\", onError);\n };\n socket.once(\"connect\", onConnect);\n socket.once(\"connect_error\", onError);\n });\n }\n\n private _key(topic: string): string {\n return resolveTopic(this._pluginId, topic);\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async pub<P = any>(topic: string, payload: P): Promise<void> {\n this._socket.emit(\"pub\", { topic: this._key(topic), payload });\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async sub<P = any>(\n topic: string,\n handler: (payload: P) => void,\n ): Promise<WsSubscriber> {\n const key = this._key(topic);\n if (!this._subHandlers.has(key)) {\n this._subHandlers.set(key, new Set());\n this._socket.emit(\"sub\", { topic: key });\n }\n const h = handler as (payload: unknown) => void;\n this._subHandlers.get(key)!.add(h);\n return {\n undeclare: () => {\n const set = this._subHandlers.get(key);\n if (!set) return;\n set.delete(h);\n if (set.size === 0) {\n this._subHandlers.delete(key);\n this._socket.emit(\"unsub\", { topic: key });\n }\n },\n };\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async action<R = any, P = any>(topic: string, payload?: P): Promise<R> {\n return new Promise((resolve, reject) => {\n this._socket\n .timeout(10_000)\n .emit(\n \"action\",\n { topic: this._key(topic), payload },\n (err: Error | null, result: R) => {\n if (err) reject(err);\n else resolve(result);\n },\n );\n });\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async serve<P = any, R = any>(\n topic: string,\n handler: (payload?: P) => R | Promise<R>,\n ): Promise<WsQueryable> {\n const key = this._key(topic);\n this._serveHandlers.set(key, handler as (payload?: unknown) => unknown);\n this._socket.emit(\"serve_register\", { topic: key });\n return {\n undeclare: () => {\n this._serveHandlers.delete(key);\n this._socket.emit(\"serve_unregister\", { topic: key });\n },\n };\n }\n\n close(): void {\n this._socket.disconnect();\n }\n}\n\n/**\n * Builds an rb-sdk-shaped object (pub/sub/action/serve + the passed-through\n * toast/alert/confirm/prompt) that lazily awaits a not-yet-connected `RbWs` \u2014\n * so callers don't block their first render on the WebSocket handshake (there\n * may be no backend reachable yet). Shared by both host mount paths (see\n * ./host.tsx and muscat-plugin-kit's dev bootstrap), which otherwise\n * duplicate this wiring.\n */\nexport function createLazyRbSdk(\n wsPromise: Promise<RbWs>,\n services: PluginUIServices,\n // Per-mount, host-provided (unlike `services`, which is a global singleton):\n // initial `data` and the `onSave` registration the host reads back. Plain\n // values/closures, so they cross the dev React-instance boundary safely.\n mount?: {\n data?: unknown;\n onSave?: (getPayload: () => unknown) => () => void;\n },\n) {\n return {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n pub: async (topic: string, payload: any) =>\n (await wsPromise).pub(topic, payload),\n sub: async (\n topic: string,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n handler: (payload: any) => void,\n ) => (await wsPromise).sub(topic, handler),\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n action: async (topic: string, payload?: any) =>\n (await wsPromise).action(topic, payload),\n serve: async (\n topic: string,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n handler: (payload?: any) => any,\n ) => (await wsPromise).serve(topic, handler),\n toast: services.toast,\n alert: services.alert,\n confirm: services.confirm,\n prompt: services.prompt,\n data: mount?.data,\n onSave: mount?.onSave ?? (() => () => {}),\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rainbow-robotics/plugin-ui",
3
- "version": "0.1.2-dev.46",
3
+ "version": "0.1.2-dev.48",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=20"
@@ -44,7 +44,7 @@
44
44
  "build-dist.mjs"
45
45
  ],
46
46
  "scripts": {
47
- "build": "node build-dist.mjs",
47
+ "build-plugin-ui": "node build-dist.mjs",
48
48
  "lint": "node ../../node_modules/eslint/bin/eslint.js .",
49
49
  "check-types": "tsc --noEmit"
50
50
  },
@@ -59,7 +59,7 @@
59
59
  "esbuild": "^0.25.0",
60
60
  "rollup-plugin-dts": "^6.1.1",
61
61
  "typescript": "^5.8.0",
62
- "vite": "^6.0.0"
62
+ "vite": "^6.2.0"
63
63
  },
64
64
  "dependencies": {
65
65
  "@radix-ui/react-context": "1.1.2",
package/src/host.tsx CHANGED
@@ -1,27 +1,17 @@
1
1
  /**
2
- * Host mounting — used by nexus (Muscat) only.
2
+ * Host mounting (nexus only). A plugin renders directly in the host document —
3
+ * no iframe. Two strategies, per `PluginModuleSource.kind` (see ./pluginData):
3
4
  *
4
- * No iframe, no remote-dom, no Module Federation: a plugin renders directly in
5
- * the host document. Two mount strategies, chosen per `PluginModuleSource.kind`
6
- * (see ./pluginData):
5
+ * - "esm" (built plugin / robot runtime): dynamic `import()` of the plugin's
6
+ * built entry. react / react-dom / rb-components / plugin-ui are external bare
7
+ * specifiers, resolved by the host page's import map to the host's own loaded
8
+ * instances (see ./viteHost.ts). So the plugin shares the host's React,
9
+ * rb-components, and RbContext, with no duplicate React instance.
7
10
  *
8
- * - "esm" (built plugin, `muscat-pk build` / robot runtime): the host does a
9
- * plain dynamic `import()` of the plugin's built entry. The plugin is built
10
- * with react / react-dom / rb-components / plugin-ui context modules left
11
- * EXTERNAL as bare specifiers; the host page carries an import map (see
12
- * @rainbow-robotics/plugin-ui/host-runtime + the shim modules nexus serves)
13
- * that resolves those specifiers to the host's OWN already-loaded instances.
14
- * So the loaded component shares the host's single React + rb-components +
15
- * RbContext + host-ui — rendered as a genuinely nested React tree, with no
16
- * duplicate React instance and minimal plugin bundle size.
17
- *
18
- * - "dev" (`muscat-pk dev`): the plugin's own Vite dev server serves a
19
- * bootstrap module exposing `mount(container, opts)`. That module uses ITS
20
- * OWN react/react-dom/plugin-ui (resolved from the plugin's own node_modules),
21
- * so it self-mounts into the container as a fully independent React root.
22
- * HostServices passed in are plain functions (see ./host-ui) — safe to
23
- * call across the React-instance boundary, since they don't touch React
24
- * internals themselves.
11
+ * - "dev" (`muscat-pk dev`): the plugin's Vite dev server serves a bootstrap
12
+ * module exposing `mount(container, opts)`, mounted as an independent React
13
+ * root using the plugin's own deps. HostServices passed in are plain
14
+ * functions (see ./host-ui), so they cross the React-instance boundary safely.
25
15
  */
26
16
  import {
27
17
  useCallback,
@@ -33,11 +23,10 @@ import {
33
23
  } from "react";
34
24
  import { RbContext, type RbSdk } from "./sdk";
35
25
  import { RbWs, createLazyRbSdk } from "./ws";
36
- // IMPORTANT: this file is shipped as SOURCE (see package.json — "./host" maps
37
- // to src/host.tsx, not dist), so a relative "./host-ui" import here would
38
- // resolve to src/host-ui.ts, a DIFFERENT file than the dist/host-ui.js every
39
- // other consumer (e.g. nexus's App.tsx) resolves to — two files, two separate
40
- // module-scope `services` singletons. Must import through the package specifier.
26
+ // Import via the package specifier, not a relative "./host-ui": this file ships
27
+ // as source (exports "./host" -> src/host.tsx), so a relative path would hit
28
+ // src/host-ui.ts while other consumers hit dist/host-ui.js — two modules, two
29
+ // `services` singletons.
41
30
  import { getHostServices } from "@rainbow-robotics/plugin-ui/host-ui";
42
31
  import { usePluginData, type PluginModuleSource } from "./pluginData";
43
32
 
@@ -1,16 +1,10 @@
1
1
  /// <reference types="vite/client" />
2
2
  /**
3
- * Plugin data seam (host-side).
4
- *
5
- * A single source serves both the real back end and the locally-developed
6
- * plugin. `muscat-pk dev` injects `window.__RB_PREVIEW_PLUGIN_ID__`; any request
7
- * for that id is routed to preview-host (dev plugin), everything else to the
8
- * host's back end. So the dev plugin and installed plugins coexist.
9
- *
10
- * Host-agnostic: no app-stack dependency (no react-query). The host passes its
11
- * real dialog/toast as `hostServices` — the provider registers them via
12
- * setHostServices so plugins' useDialog/toast delegate to the host's single
13
- * instance (see ./host-ui).
3
+ * Plugin data seam (host-side). Resolves where each plugin's UI/assets load
4
+ * from. `muscat-pk dev` sets `window.__RB_PREVIEW_PLUGIN_ID__`; that id routes
5
+ * to preview-host, everything else to the installed-plugin back end, so both
6
+ * coexist. The host passes its dialog/toast as `hostServices`, registered via
7
+ * setHostServices (see ./host-ui).
14
8
  */
15
9
  import {
16
10
  createContext,
@@ -21,11 +15,8 @@ import {
21
15
  type PropsWithChildren,
22
16
  } from "react";
23
17
  import type { PluginManifest } from "./manifest";
24
- // NOTE: import through the PACKAGE specifier, not a relative path — this file
25
- // ships as SOURCE (exports "./plugin-data" -> src/pluginData.tsx), so a
26
- // relative "./host-ui" import would resolve to src/host-ui.ts, a DIFFERENT
27
- // file than the dist/host-ui.js every other consumer resolves to — two files,
28
- // two `services` singletons. Same reason host.tsx imports this specifier too.
18
+ // Package specifier, not a relative path — see host.tsx for why (source vs dist
19
+ // host-ui would be two `services` singletons).
29
20
  import {
30
21
  setHostServices,
31
22
  type PluginUIServices,
@@ -200,7 +191,7 @@ export function PluginDataProvider({
200
191
  children,
201
192
  hostServices,
202
193
  wsUrl = "ws://127.0.0.1:10000",
203
- liveBase = "/plugin",
194
+ liveBase = "http://localhost:3000/common/plugin",
204
195
  }: PropsWithChildren<PluginDataProviderProps>) {
205
196
  // Register in an effect (setHostServices is a side effect, not render-safe).
206
197
  // Layout effect so it runs before children (a mounting PluginHost) read it.
package/src/react.tsx CHANGED
@@ -1,18 +1,11 @@
1
1
  /**
2
- * Author component surface.
2
+ * Author component surface — re-exports the @repo/rb-components new_components
3
+ * barrel, plus the sdk hooks.
3
4
  *
4
- * import { RBSlider, RBLabelButton, ... } from "@rainbow-robotics/plugin-ui/react";
5
+ * import { RBSlider, RBLabelButton, useRb } from "@rainbow-robotics/plugin-ui/react";
5
6
  *
6
- * Full re-export of the REAL @repo/rb-components (`new_components` barrel).
7
- * Plugins mount directly in the host document (no iframe / remote-dom), so
8
- * these are ordinary React components with their real props — authors also
9
- * use native <div>/<span>/<button> and className freely.
10
- *
11
- * This re-export surface IS the version contract: the host serves exactly the
12
- * plugin-ui runtime that these resolve to, so a plugin can't drift from the
13
- * host's components. The import-map shim that mirrors this at runtime (see
14
- * ./viteHost.ts) reads this module's actual exports rather than a hardcoded
15
- * list, so it never drifts from what's re-exported here.
7
+ * The runtime import-map shim (./viteHost.ts) reads this module's exports
8
+ * rather than a hardcoded list, so the two stay in sync.
16
9
  */
17
10
  export * from "@repo/rb-components/new_components";
18
11
 
package/src/sdk.ts CHANGED
@@ -1,12 +1,10 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  /**
3
- * Plugin runtime SDK — the surface a plugin author calls from inside the sandbox.
3
+ * Plugin runtime SDK (`useRb()`).
4
4
  *
5
- * Zenoh methods (pub/sub/action/serve) talk directly to the Zenoh router via
6
- * WebSocket. Topics are auto-namespaced to {pluginId}/... to prevent
7
- * cross-plugin collisions. A leading "/" bypasses namespacing (absolute path).
8
- *
9
- * notify() is still host-mediated (goes through the Muscat shell).
5
+ * pub/sub/action/serve talk to the Zenoh router over WebSocket. Topics are
6
+ * namespaced to {pluginId}/... ; a leading "/" makes the path absolute.
7
+ * alert/confirm/prompt/toast delegate to the host.
10
8
  */
11
9
  import { createContext, useContext, useEffect, useRef } from "react";
12
10
  import type {
package/src/viteHost.ts CHANGED
@@ -8,10 +8,8 @@
8
8
  * isPluginPreviewMode,
9
9
  * } from "@rainbow-robotics/plugin-ui/vite-host";
10
10
  *
11
- * There's no "plugin runtime vendor bundle" to serve anymore — a plugin
12
- * either loads via its own dev server (dev) or as a plain ESM module whose
13
- * bare imports (react, rb-components, plugin-ui/*) resolve via the import map
14
- * this file injects, to the host's own already-loaded instances (build/robot).
11
+ * A built plugin's bare imports (react, rb-components, plugin-ui/*) resolve via
12
+ * the import map this file injects, to the host's already-loaded instances.
15
13
  * See ./host.tsx.
16
14
  */
17
15
  import type { Plugin } from "vite";
@@ -55,19 +53,14 @@ export const isPluginPreviewMode = (mode: string) => mode === "plugin-preview";
55
53
 
56
54
  const SHARED_BASE = "/plugin-shared";
57
55
 
58
- // Each shared specifier → named exports the shim re-exports from
56
+ // Each shared specifier → the named exports its shim re-exports from
59
57
  // window.__RB_SHARED__[specifier] (set by the host, e.g. nexus/src/pluginShared).
60
- // A built plugin imports these as bare externals; the host page's import map
61
- // (below) points them at these shims → the host's single instance.
62
58
  //
63
59
  // `names`: hardcoded list (small, stable modules).
64
- // `parseNamesFrom`: a require()-resolvable specifier whose SOURCE text we
65
- // parse for `export { ... }` names — used for react.tsx, which fully
66
- // re-exports the rb-components barrel (100+ names) and would drift if
67
- // hardcoded here. We parse the source text rather than importing/executing
68
- // the module: the barrel is a .ts source file, unresolvable/unexecutable by
69
- // plain Node (no loader), and even importing our OWN compiled dist/react.js
70
- // would still transitively hit that same unexecutable .ts re-export.
60
+ // `parseNamesFrom`: parse the export names out of a source file's text — used
61
+ // for react.tsx, which re-exports the whole rb-components barrel (100+ names).
62
+ // We text-parse rather than import it: the barrel is .ts source, which plain
63
+ // Node can't execute.
71
64
  const SHARED_MODULES: Record<
72
65
  string,
73
66
  {
package/src/ws.ts CHANGED
@@ -123,14 +123,15 @@ export class RbWs {
123
123
 
124
124
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
125
125
  async action<R = any, P = any>(topic: string, payload?: P): Promise<R> {
126
- return new Promise((resolve) => {
126
+ return new Promise((resolve, reject) => {
127
127
  this._socket
128
128
  .timeout(10_000)
129
129
  .emit(
130
130
  "action",
131
131
  { topic: this._key(topic), payload },
132
- (_err: Error | null, result: R) => {
133
- resolve(_err ? (undefined as R) : result);
132
+ (err: Error | null, result: R) => {
133
+ if (err) reject(err);
134
+ else resolve(result);
134
135
  },
135
136
  );
136
137
  });