@rainbow-robotics/plugin-ui 0.1.2-dev.47 → 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 +91 -77
- package/dist/pluginData.d.ts.map +1 -1
- package/dist/react.d.ts +7 -7
- package/dist/viteHost.d.ts.map +1 -1
- package/dist/viteHost.js.map +2 -2
- package/dist/ws.d.ts.map +1 -1
- package/dist/ws.js +4 -3
- package/dist/ws.js.map +2 -2
- package/package.json +3 -3
- package/src/pluginData.tsx +1 -1
- package/src/ws.ts +4 -3
package/README.md
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
# @rainbow-robotics/plugin-ui
|
|
2
2
|
|
|
3
|
-
# Muscat
|
|
3
|
+
# Muscat plugin UI contract and host mounting architecture
|
|
4
4
|
|
|
5
|
-
Muscat
|
|
5
|
+
A Muscat plugin is a plain React component exported as `export default App`.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Plugin mounting works through two paths, both handled by the `./host` package.
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Dev environment: `muscat-pk dev`
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
In dev, the plugin's own Vite dev server (using the plugin's `vite.config.ts`) serves a bootstrap module.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
The host dynamically imports that bootstrap module to run the plugin.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Characteristics of this approach:
|
|
16
16
|
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
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
|
-
|
|
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
|
-
##
|
|
24
|
+
## Build & robot environment: `muscat-pk build`
|
|
25
25
|
|
|
26
|
-
`muscat-pk build
|
|
27
|
-
(`--config`
|
|
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
|
-
|
|
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
|
|
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` |
|
|
43
|
-
| `@rainbow-robotics/plugin-ui` (
|
|
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
|
|
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
|
-
|
|
54
|
+
The host page includes an import map set up by `./vite-host`.
|
|
55
55
|
|
|
56
|
-
|
|
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
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* `RbContext`
|
|
63
|
-
*
|
|
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
|
-
|
|
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
|
|
68
|
+
### Plugins with no UI skip the vite build entirely
|
|
69
69
|
|
|
70
|
-
`muscat-pk build
|
|
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
|
|
73
|
-
* `contributes
|
|
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
|
-
|
|
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
|
|
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
|
-
*
|
|
82
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
145
|
+
`RbWs` is the WebSocket client `useRb()` uses internally.
|
|
143
146
|
|
|
144
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
189
|
-
*
|
|
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
|
-
*
|
|
217
|
+
* Injecting the import map
|
|
218
|
+
* Supporting the plugin preview dev environment
|
|
205
219
|
|
|
206
|
-
#
|
|
220
|
+
# Plugin production bundle rules
|
|
207
221
|
|
|
208
|
-
|
|
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
|
-
|
|
246
|
+
So `./host` and `./vite-host` are never included in a plugin bundle.
|
package/dist/pluginData.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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,
|
|
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?: "
|
|
69
|
-
variant?: "transparent" | "outlined" | "soft" | "solid" | "inverse" | "inverseSoft" | "brand" | "
|
|
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?: "
|
|
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?: "
|
|
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?: "
|
|
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?: "
|
|
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?: "
|
|
1190
|
+
variant?: "default" | "success";
|
|
1191
1191
|
focus?: boolean;
|
|
1192
1192
|
error?: boolean;
|
|
1193
1193
|
readOnly?: boolean;
|
package/dist/viteHost.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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;
|
|
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"}
|
package/dist/viteHost.js.map
CHANGED
|
@@ -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 * 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 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": "AAeA,SAAS,kBAAkB,YAAY,cAAc,gBAAgB;AACrE,SAAS,qBAAqB;AAC9B,SAAS,SAAS,eAAe;AAEjC,MAAMA,WAAU,cAAc,YAAY,GAAG;AAEtC,MAAM,8BAA8B;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,MAAM,mBAA2C;AAAA,EACtD,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,UAAU;AACZ;AAEO,MAAM,sBAAsB,CAAC,SAAiB,SAAS;AAM9D,MAAM,cAAc;
|
|
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": "AAeA,SAAS,kBAAkB,YAAY,cAAc,gBAAgB;AACrE,SAAS,qBAAqB;AAC9B,SAAS,SAAS,eAAe;AAEjC,MAAMA,WAAU,cAAc,YAAY,GAAG;AAEtC,MAAM,8BAA8B;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,MAAM,mBAA2C;AAAA,EACtD,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,UAAU;AACZ;AAEO,MAAM,sBAAsB,CAAC,SAAiB,SAAS;AAM9D,MAAM,cAAc;AAUpB,MAAM,iBAQF;AAAA,EACF,OAAO;AAAA,IACL,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,OAAO;AAAA,MACL;AAAA,MAAY;AAAA,MAAa;AAAA,MAAY;AAAA,MAAY;AAAA,MACjD;AAAA,MAAc;AAAA,MAAY;AAAA,MAAgB;AAAA,MAC1C;AAAA,MAAiB;AAAA,MAAa;AAAA,MAAc;AAAA,MAAkB;AAAA,MAC9D;AAAA,MAAQ;AAAA,MAAmB;AAAA,MAAe;AAAA,MAAc;AAAA,MACxD;AAAA,MAAoB;AAAA,MAAa;AAAA,MAAS;AAAA,MAC1C;AAAA,MAAsB;AAAA,MAAmB;AAAA,MAAW;AAAA,MACpD;AAAA,MAAU;AAAA,MAAY;AAAA,MAAwB;AAAA,MAAiB;AAAA,IACjE;AAAA,EACF;AAAA,EACA,qBAAqB;AAAA,IACnB,MAAM;AAAA,IACN,OAAO,CAAC,YAAY,OAAO,MAAM;AAAA,EACnC;AAAA,EACA,yBAAyB;AAAA,IACvB,MAAM;AAAA,IACN,OAAO,CAAC,YAAY,QAAQ;AAAA,EAC9B;AAAA,EACA,aAAa;AAAA,IACX,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA,oBAAoB;AAAA,IAClB,MAAM;AAAA,IACN,OAAO,CAAC,cAAc,aAAa;AAAA,EACrC;AAAA,EACA,qCAAqC;AAAA,IACnC,MAAM;AAAA;AAAA;AAAA,IAGN,gBAAgB;AAAA,MACd,WAAW;AAAA,MACX,YAAY,CAAC,SAAS,iBAAiB,WAAW;AAAA,IACpD;AAAA,EACF;AAAA,EACA,mCAAmC;AAAA,IACjC,MAAM;AAAA,IACN,OAAO,CAAC,aAAa,SAAS,iBAAiB,WAAW;AAAA,EAC5D;AAAA,EACA,uCAAuC;AAAA,IACrC,MAAM;AAAA,IACN,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA,kCAAkC;AAAA,IAChC,MAAM;AAAA,IACN,OAAO,CAAC,QAAQ,iBAAiB;AAAA,EACnC;AACF;AAOA,SAAS,wBAAwB,QAA0B;AACzD,QAAM,QAAQ,oBAAI,IAAY;AAC9B,QAAM,UAAU;AAChB,MAAI;AACJ,SAAQ,QAAQ,QAAQ,KAAK,MAAM,GAAI;AACrC,QAAI,MAAM,CAAC,EAAG;AACd,eAAW,YAAY,MAAM,CAAC,EAAE,MAAM,GAAG,GAAG;AAC1C,YAAM,QAAQ,SAAS,KAAK;AAC5B,UAAI,CAAC,SAAS,MAAM,WAAW,OAAO,EAAG;AACzC,YAAM,UAAU,MAAM,MAAM,oBAAoB;AAChD,YAAM,IAAI,UAAU,QAAQ,CAAC,IAAI,KAAK;AAAA,IACxC;AAAA,EACF;AACA,SAAO,CAAC,GAAG,KAAK;AAClB;AAEA,MAAM,YAAY,oBAAI,IAAsB;AAE5C,SAAS,aAAa,MAAwB;AAC5C,QAAM,MAAM,eAAe,IAAI;AAC/B,MAAI,IAAI,MAAO,QAAO,IAAI;AAC1B,MAAI,CAAC,IAAI,gBAAgB;AACvB,UAAM,IAAI,MAAM,eAAe,IAAI,2CAA2C;AAAA,EAChF;AACA,MAAI,CAAC,UAAU,IAAI,IAAI,GAAG;AACxB,UAAM,EAAE,WAAW,aAAa,CAAC,EAAE,IAAI,IAAI;AAC3C,UAAM,aAAaA,SAAQ,QAAQ,SAAS;AAC5C,UAAM,SAAS,wBAAwB,aAAa,YAAY,MAAM,CAAC;AACvE,cAAU,IAAI,MAAM,CAAC,GAAG,oBAAI,IAAI,CAAC,GAAG,QAAQ,GAAG,UAAU,CAAC,CAAC,CAAC;AAAA,EAC9D;AACA,SAAO,UAAU,IAAI,IAAI;AAC3B;AAEA,SAAS,WAAW,MAAsB;AACxC,QAAM,MAAM,eAAe,IAAI;AAC/B,QAAM,QAAQ,aAAa,IAAI;AAC/B,QAAM,IAAI,kCAAkC,KAAK,UAAU,IAAI,CAAC;AAChE,SACE,aAAa,CAAC;AAAA,0EAC6D,IAAI;AAAA,iBAC7D,MAAM,KAAK,IAAI,CAAC;AAAA,KACjC,IAAI,aAAa;AAAA,IAAuC;AAE7D;AAEA,MAAM,aAAa,OAAO;AAAA,EACxB,OAAO,QAAQ,cAAc,EAAE,IAAI,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC;AACvE;AAEA,SAAS,kBAA0B;AACjC,QAAM,UAAkC,CAAC;AACzC,aAAW,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,OAAO,QAAQ,cAAc,GAAG;AAC7D,YAAQ,IAAI,IAAI,GAAG,WAAW,IAAI,IAAI;AAAA,EACxC;AACA,SAAO,4BAA4B,KAAK,UAAU,EAAE,QAAQ,CAAC,CAAC;AAChE;AASO,MAAM,4BAA4B,OAAe;AAAA,EACtD,MAAM;AAAA,EACN,mBAAmB,MAAM;AACvB,WAAO,KAAK,QAAQ,WAAW,OAAO,gBAAgB,CAAC;AAAA,UAAa;AAAA,EACtE;AAAA,EACA,gBAAgB,QAAQ;AACtB,WAAO,YAAY,IAAI,CAAC,KAAK,KAAK,SAAS;AACzC,YAAM,MAAM,IAAI,KAAK,MAAM,GAAG,EAAE,CAAC;AACjC,UAAI,CAAC,KAAK,WAAW,GAAG,WAAW,GAAG,EAAG,QAAO,KAAK;AACrD,YAAM,OAAO,IAAI,MAAM,GAAG,WAAW,IAAI,MAAM,EAAE,QAAQ,SAAS,EAAE;AACpE,YAAM,OAAO,WAAW,IAAI;AAC5B,UAAI,CAAC,KAAM,QAAO,KAAK;AACvB,UAAI,UAAU,gBAAgB,gCAAgC;AAC9D,UAAI,UAAU,+BAA+B,GAAG;AAChD,UAAI,IAAI,WAAW,IAAI,CAAC;AAAA,IAC1B,CAAC;AAAA,EACH;AAAA,EACA,iBAAiB;AACf,eAAW,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,OAAO,QAAQ,cAAc,GAAG;AAC7D,WAAK,SAAS;AAAA,QACZ,MAAM;AAAA,QACN,UAAU,iBAAiB,IAAI;AAAA,QAC/B,QAAQ,WAAW,IAAI;AAAA,MACzB,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAGO,MAAM,6BAA6B,OAAe;AAAA,EACvD,MAAM;AAAA,EACN,OAAO,CAAC,SAAS,EAAE,KAAK,MAAM,oBAAoB,IAAI;AAAA,EACtD,mBAAmB,MAAM;AACvB,WAAO,KAAK;AAAA,MACV;AAAA,MACA;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,IACb;AAAA,EACF;AACF;AAGO,MAAM,4BAA4B,CAAC,SAG5B;AACZ,QAAM,EAAE,WAAW,WAAW,4BAA4B,IAAI;AAC9D,SAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO,CAAC,SAAS,EAAE,SAAS,KAAK,MAC/B,YAAY,WAAW,oBAAoB,IAAI;AAAA,IACjD,gBAAgB,QAAQ;AACtB,aAAO,YAAY,IAAI,CAAC,KAAK,KAAK,SAAS;AACzC,cAAM,MAAM,IAAI,KAAK,MAAM,GAAG,EAAE,CAAC;AACjC,YAAI,CAAC,OAAO,CAAC,SAAS,KAAK,CAAC,WAAW,IAAI,WAAW,MAAM,CAAC;AAC3D,iBAAO,KAAK;AAEd,cAAM,aAAa,mBAAmB,GAAG;AACzC,cAAM,WAAW,QAAQ,WAAW,IAAI,UAAU,EAAE;AACpD,YAAI,CAAC,SAAS,WAAW,SAAS,EAAG,QAAO,KAAK;AACjD,YAAI,CAAC,WAAW,QAAQ,KAAK,CAAC,SAAS,QAAQ,EAAE,OAAO,EAAG,QAAO,KAAK;AAEvE,cAAM,cAAc,iBAAiB,QAAQ,QAAQ,CAAC;AACtD,YAAI,YAAa,KAAI,UAAU,gBAAgB,WAAW;AAC1D,YAAI,UAAU,+BAA+B,GAAG;AAChD,yBAAiB,QAAQ,EAAE,KAAK,GAAG;AAAA,MACrC,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAQO,MAAM,mBAAmB,CAAC,SAKjB;AAAA,EACd,0BAA0B;AAAA,EAC1B,2BAA2B;AAAA,EAC3B,0BAA0B;AAAA,IACxB,WAAW,KAAK;AAAA,IAChB,UAAU,KAAK;AAAA,EACjB,CAAC;AACH;",
|
|
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;
|
|
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
|
-
(
|
|
86
|
-
|
|
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 (
|
|
5
|
-
"mappings": "AAWA,SAAS,UAAuB;AAWhC,SAAS,aAAa,UAAkB,OAAuB;AAC7D,SAAO,MAAM,WAAW,GAAG,IAAI,MAAM,MAAM,CAAC,IAAI,GAAG,QAAQ,IAAI,KAAK;AACtE;AAEO,MAAM,KAAK;AAAA,EACC;AAAA,EACA;AAAA,EACA,eAAe,oBAAI,IAGlC;AAAA,EACe,iBAAiB,oBAAI,IAGpC;AAAA,EAEM,YAAY,QAAgB,UAAkB;AACpD,SAAK,UAAU;AACf,SAAK,YAAY;AAGjB,WAAO;AAAA,MACL;AAAA,MACA,CAAC,EAAE,OAAO,QAAQ,MAA2C;AAC3D,aAAK,aAAa,IAAI,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;AAAA,MACzD;AAAA,IACF;AAGA,WAAO;AAAA,MACL;AAAA,MACA,OAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,MACF,MAIM;AACJ,cAAM,UAAU,KAAK,eAAe,IAAI,KAAK;AAC7C,cAAM,SAAS,UAAU,MAAM,QAAQ,OAAO,IAAI;AAClD,eAAO,KAAK,eAAe,EAAE,IAAI,SAAS,OAAO,CAAC;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AAAA,EAEA,OAAO,QAAQ,KAAa,UAAiC;AAC3D,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,SAAS,GAAG,KAAK,EAAE,YAAY,CAAC,WAAW,EAAE,CAAC;AACpD,YAAM,YAAY,MAAM;AACtB,gBAAQ;AACR,gBAAQ,IAAI,KAAK,QAAQ,QAAQ,CAAC;AAAA,MACpC;AACA,YAAM,UAAU,CAAC,QAAe;AAC9B,gBAAQ;AACR,eAAO,GAAG;AAAA,MACZ;AACA,YAAM,UAAU,MAAM;AACpB,eAAO,IAAI,WAAW,SAAS;AAC/B,eAAO,IAAI,iBAAiB,OAAO;AAAA,MACrC;AACA,aAAO,KAAK,WAAW,SAAS;AAChC,aAAO,KAAK,iBAAiB,OAAO;AAAA,IACtC,CAAC;AAAA,EACH;AAAA,EAEQ,KAAK,OAAuB;AAClC,WAAO,aAAa,KAAK,WAAW,KAAK;AAAA,EAC3C;AAAA;AAAA,EAGA,MAAM,IAAa,OAAe,SAA2B;AAC3D,SAAK,QAAQ,KAAK,OAAO,EAAE,OAAO,KAAK,KAAK,KAAK,GAAG,QAAQ,CAAC;AAAA,EAC/D;AAAA;AAAA,EAGA,MAAM,IACJ,OACA,SACuB;AACvB,UAAM,MAAM,KAAK,KAAK,KAAK;AAC3B,QAAI,CAAC,KAAK,aAAa,IAAI,GAAG,GAAG;AAC/B,WAAK,aAAa,IAAI,KAAK,oBAAI,IAAI,CAAC;AACpC,WAAK,QAAQ,KAAK,OAAO,EAAE,OAAO,IAAI,CAAC;AAAA,IACzC;AACA,UAAM,IAAI;AACV,SAAK,aAAa,IAAI,GAAG,EAAG,IAAI,CAAC;AACjC,WAAO;AAAA,MACL,WAAW,MAAM;AACf,cAAM,MAAM,KAAK,aAAa,IAAI,GAAG;AACrC,YAAI,CAAC,IAAK;AACV,YAAI,OAAO,CAAC;AACZ,YAAI,IAAI,SAAS,GAAG;AAClB,eAAK,aAAa,OAAO,GAAG;AAC5B,eAAK,QAAQ,KAAK,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,QAC3C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,OAAyB,OAAe,SAAyB;AACrE,WAAO,IAAI,QAAQ,CAAC,
|
|
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.
|
|
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.
|
|
62
|
+
"vite": "^6.2.0"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
65
|
"@radix-ui/react-context": "1.1.2",
|
package/src/pluginData.tsx
CHANGED
|
@@ -191,7 +191,7 @@ export function PluginDataProvider({
|
|
|
191
191
|
children,
|
|
192
192
|
hostServices,
|
|
193
193
|
wsUrl = "ws://127.0.0.1:10000",
|
|
194
|
-
liveBase = "/plugin",
|
|
194
|
+
liveBase = "http://localhost:3000/common/plugin",
|
|
195
195
|
}: PropsWithChildren<PluginDataProviderProps>) {
|
|
196
196
|
// Register in an effect (setHostServices is a side effect, not render-safe).
|
|
197
197
|
// Layout effect so it runs before children (a mounting PluginHost) read it.
|
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
|
-
(
|
|
133
|
-
|
|
132
|
+
(err: Error | null, result: R) => {
|
|
133
|
+
if (err) reject(err);
|
|
134
|
+
else resolve(result);
|
|
134
135
|
},
|
|
135
136
|
);
|
|
136
137
|
});
|