@rainbow-robotics/plugin-ui 0.1.2-dev.8 → 1.0.0

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.
Files changed (61) hide show
  1. package/README.md +236 -75
  2. package/build-dist.mjs +55 -17
  3. package/dist/host-ui.d.ts +49 -0
  4. package/dist/host-ui.d.ts.map +1 -0
  5. package/dist/host-ui.js +33 -0
  6. package/dist/host-ui.js.map +7 -0
  7. package/dist/manifest.d.ts +20 -4
  8. package/dist/manifest.d.ts.map +1 -1
  9. package/dist/manifest.js +1 -1
  10. package/dist/manifest.js.map +2 -2
  11. package/dist/pluginData.d.ts +62 -0
  12. package/dist/pluginData.d.ts.map +1 -0
  13. package/dist/react.d.ts +3436 -36
  14. package/dist/react.js +5 -54
  15. package/dist/react.js.map +2 -2
  16. package/dist/sdk.d.ts +38 -2
  17. package/dist/sdk.d.ts.map +1 -1
  18. package/dist/sdk.js +29 -4
  19. package/dist/sdk.js.map +2 -2
  20. package/dist/viteHost.d.ts +46 -0
  21. package/dist/viteHost.d.ts.map +1 -0
  22. package/dist/viteHost.js +262 -0
  23. package/dist/viteHost.js.map +7 -0
  24. package/dist/ws.d.ts +53 -0
  25. package/dist/ws.d.ts.map +1 -1
  26. package/dist/ws.js +141 -0
  27. package/dist/ws.js.map +7 -0
  28. package/package.json +45 -21
  29. package/src/host-ui.ts +80 -0
  30. package/src/host.tsx +229 -308
  31. package/src/index.d.ts +1 -0
  32. package/src/manifest.ts +25 -4
  33. package/src/pluginData.tsx +232 -0
  34. package/src/react.tsx +9 -127
  35. package/src/sdk.ts +81 -13
  36. package/src/viteHost.ts +309 -0
  37. package/src/ws.ts +127 -20
  38. package/build-browser.mjs +0 -84
  39. package/dist/connect.d.ts +0 -11
  40. package/dist/connect.d.ts.map +0 -1
  41. package/dist/connect.js +0 -34
  42. package/dist/connect.js.map +0 -7
  43. package/dist/elements.d.ts +0 -195
  44. package/dist/elements.d.ts.map +0 -1
  45. package/dist/elements.js +0 -115
  46. package/dist/elements.js.map +0 -7
  47. package/dist/react.d.ts.map +0 -1
  48. package/dist-browser/@rainbow-robotics/plugin-ui/chunks/chunk-37HVFFJB.js +0 -1
  49. package/dist-browser/@rainbow-robotics/plugin-ui/chunks/chunk-4MGSRUAS.js +0 -1
  50. package/dist-browser/@rainbow-robotics/plugin-ui/chunks/chunk-Q6XGCIPE.js +0 -1
  51. package/dist-browser/@rainbow-robotics/plugin-ui/connect.js +0 -1
  52. package/dist-browser/@rainbow-robotics/plugin-ui/elements.js +0 -1
  53. package/dist-browser/@rainbow-robotics/plugin-ui/react.js +0 -1
  54. package/dist-browser/@rainbow-robotics/plugin-ui/sdk.js +0 -1
  55. package/dist-browser/vendor/react-dom-client.js +0 -1
  56. package/dist-browser/vendor/react-jsx-dev-runtime.js +0 -1
  57. package/dist-browser/vendor/react-jsx-runtime.js +0 -1
  58. package/dist-browser/vendor/react-runtime.js +0 -65
  59. package/dist-browser/vendor/react.js +0 -1
  60. package/src/connect.ts +0 -62
  61. package/src/elements.ts +0 -257
package/dist/ws.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../src/ws.ts"],"names":[],"mappings":"AAaA,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,CAAsD;IACnF,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAwE;IAEvG,OAAO;IAiBP,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW5D,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,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC;IAsBjF,MAAM,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAWhE,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"}
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;IAuChE,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;;aA1MtB,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;;;;;;yBA2LuB,MAAM,OAAO,KAAK,MAAM,IAAI;EA2BrD"}
package/dist/ws.js ADDED
@@ -0,0 +1,141 @@
1
+ import { io } from "socket.io-client";
2
+ function resolveTopic(pluginId, topic) {
3
+ return topic.startsWith("/") ? topic.slice(1) : `${pluginId}/${topic}`;
4
+ }
5
+ class RbWs {
6
+ _socket;
7
+ _pluginId;
8
+ _subHandlers = /* @__PURE__ */ new Map();
9
+ _serveHandlers = /* @__PURE__ */ new Map();
10
+ constructor(socket, pluginId) {
11
+ this._socket = socket;
12
+ this._pluginId = pluginId;
13
+ socket.on(
14
+ "msg",
15
+ ({ topic, payload }) => {
16
+ this._subHandlers.get(topic)?.forEach((h) => h(payload));
17
+ }
18
+ );
19
+ socket.on(
20
+ "serve_call",
21
+ async ({
22
+ id,
23
+ topic,
24
+ payload
25
+ }) => {
26
+ const handler = this._serveHandlers.get(topic);
27
+ const result = handler ? await handler(payload) : void 0;
28
+ socket.emit("serve_reply", { id, payload: result });
29
+ }
30
+ );
31
+ }
32
+ static connect(url, pluginId) {
33
+ return new Promise((resolve, reject) => {
34
+ const socket = io(url, { transports: ["websocket"] });
35
+ const onConnect = () => {
36
+ cleanup();
37
+ resolve(new RbWs(socket, pluginId));
38
+ };
39
+ const onError = (err) => {
40
+ cleanup();
41
+ reject(err);
42
+ };
43
+ const cleanup = () => {
44
+ socket.off("connect", onConnect);
45
+ socket.off("connect_error", onError);
46
+ };
47
+ socket.once("connect", onConnect);
48
+ socket.once("connect_error", onError);
49
+ });
50
+ }
51
+ _key(topic) {
52
+ return resolveTopic(this._pluginId, topic);
53
+ }
54
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
+ async pub(topic, payload) {
56
+ this._socket.emit("pub", { topic: this._key(topic), payload });
57
+ }
58
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
59
+ async sub(topic, handler) {
60
+ const key = this._key(topic);
61
+ if (!this._subHandlers.has(key)) {
62
+ this._subHandlers.set(key, /* @__PURE__ */ new Set());
63
+ this._socket.emit("sub", { topic: key });
64
+ }
65
+ const h = handler;
66
+ this._subHandlers.get(key).add(h);
67
+ return {
68
+ undeclare: () => {
69
+ const set = this._subHandlers.get(key);
70
+ if (!set) return;
71
+ set.delete(h);
72
+ if (set.size === 0) {
73
+ this._subHandlers.delete(key);
74
+ this._socket.emit("unsub", { topic: key });
75
+ }
76
+ }
77
+ };
78
+ }
79
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
80
+ async action(topic, payload) {
81
+ const key = this._key(topic);
82
+ return new Promise((resolve, reject) => {
83
+ this._socket.timeout(1e4).emit(
84
+ "action",
85
+ { topic: key, payload },
86
+ (err, result) => {
87
+ if (err) {
88
+ reject(new Error(`action "${key}": no ack from bridge (${err.message})`));
89
+ } else if (result && typeof result === "object" && "error" in result && typeof result.error === "string") {
90
+ reject(new Error(result.error));
91
+ } else if (result === void 0 || result === null) {
92
+ reject(
93
+ new Error(
94
+ `action "${key}": no reply from plugin backend \u2014 the handler was reached but didn't return in time (check the backend's stdout on the robot).`
95
+ )
96
+ );
97
+ } else {
98
+ resolve(result);
99
+ }
100
+ }
101
+ );
102
+ });
103
+ }
104
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
105
+ async serve(topic, handler) {
106
+ const key = this._key(topic);
107
+ this._serveHandlers.set(key, handler);
108
+ this._socket.emit("serve_register", { topic: key });
109
+ return {
110
+ undeclare: () => {
111
+ this._serveHandlers.delete(key);
112
+ this._socket.emit("serve_unregister", { topic: key });
113
+ }
114
+ };
115
+ }
116
+ close() {
117
+ this._socket.disconnect();
118
+ }
119
+ }
120
+ function createLazyRbSdk(wsPromise, services, mount) {
121
+ return {
122
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
123
+ pub: async (topic, payload) => (await wsPromise).pub(topic, payload),
124
+ sub: async (topic, handler) => (await wsPromise).sub(topic, handler),
125
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
126
+ action: async (topic, payload) => (await wsPromise).action(topic, payload),
127
+ serve: async (topic, handler) => (await wsPromise).serve(topic, handler),
128
+ toast: services.toast,
129
+ alert: services.alert,
130
+ confirm: services.confirm,
131
+ prompt: services.prompt,
132
+ data: mount?.data,
133
+ onSave: mount?.onSave ?? (() => () => {
134
+ })
135
+ };
136
+ }
137
+ export {
138
+ RbWs,
139
+ createLazyRbSdk
140
+ };
141
+ //# sourceMappingURL=ws.js.map
package/dist/ws.js.map ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 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 const key = this._key(topic);\n return new Promise((resolve, reject) => {\n this._socket\n .timeout(10_000)\n .emit(\n \"action\",\n { topic: key, payload },\n (err: Error | null, result: R) => {\n if (err) {\n // socket.io ack timeout \u2014 the bridge itself never answered.\n reject(new Error(`action \"${key}\": no ack from bridge (${err.message})`));\n } else if (\n result &&\n typeof result === \"object\" &&\n \"error\" in result &&\n typeof result.error === \"string\"\n ) {\n // Backend handler returned an error payload \u2014 surface it verbatim.\n reject(new Error(result.error));\n } else if (result === undefined || result === null) {\n // The bridge acked but with no payload: the backend handler is\n // registered yet didn't reply in time (hung inside the handler,\n // e.g. a blocked SDK call) or returned nothing. Distinct from a\n // \"No handler registered\" error, which comes back as result.error.\n reject(\n new Error(\n `action \"${key}\": no reply from plugin backend \u2014 the handler was reached but didn't return in time (check the backend's stdout on the robot).`,\n ),\n );\n } else {\n resolve(result);\n }\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
+ "names": []
7
+ }
package/package.json CHANGED
@@ -1,48 +1,51 @@
1
1
  {
2
2
  "name": "@rainbow-robotics/plugin-ui",
3
- "version": "0.1.2-dev.8",
3
+ "version": "1.0.0",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=20"
7
7
  },
8
- "description": "Muscat plugin UI contract/runtime: rb-* remote elements, sandbox bindings, and host renderer",
8
+ "description": "Muscat plugin UI contract/runtime: author bindings, robot SDK, and direct-mount host runtime",
9
9
  "publishConfig": {
10
10
  "access": "public"
11
11
  },
12
12
  "exports": {
13
- "./elements": {
14
- "import": "./dist/elements.js",
15
- "types": "./dist/elements.d.ts"
16
- },
17
13
  "./react": {
18
14
  "import": "./dist/react.js",
19
15
  "types": "./dist/react.d.ts"
20
16
  },
21
- "./connect": {
22
- "import": "./dist/connect.js",
23
- "types": "./dist/connect.d.ts"
24
- },
25
17
  "./sdk": {
26
18
  "import": "./dist/sdk.js",
27
19
  "types": "./dist/sdk.d.ts"
28
20
  },
21
+ "./ws": {
22
+ "import": "./dist/ws.js",
23
+ "types": "./dist/ws.d.ts"
24
+ },
25
+ "./host-ui": {
26
+ "import": "./dist/host-ui.js",
27
+ "types": "./dist/host-ui.d.ts"
28
+ },
29
29
  "./manifest": {
30
30
  "import": "./dist/manifest.js",
31
31
  "types": "./dist/manifest.d.ts"
32
32
  },
33
33
  "./host": "./src/host.tsx",
34
- "./dist-browser": "./dist-browser",
34
+ "./plugin-data": "./src/pluginData.tsx",
35
+ "./vite-host": {
36
+ "import": "./dist/viteHost.js",
37
+ "types": "./dist/viteHost.d.ts"
38
+ },
35
39
  "./package.json": "./package.json"
36
40
  },
37
41
  "files": [
38
42
  "src",
39
43
  "dist",
40
- "dist-browser",
41
- "build-browser.mjs",
42
44
  "build-dist.mjs"
43
45
  ],
44
46
  "scripts": {
45
- "build": "node build-dist.mjs && node build-browser.mjs",
47
+ "build": "node build-dist.mjs",
48
+ "build-plugin-ui": "node build-dist.mjs",
46
49
  "lint": "node ../../node_modules/eslint/bin/eslint.js .",
47
50
  "check-types": "tsc --noEmit"
48
51
  },
@@ -50,20 +53,41 @@
50
53
  "@repo/eslint-config": "*",
51
54
  "@repo/rb-components": "*",
52
55
  "@repo/typescript-config": "*",
56
+ "@rollup/plugin-node-resolve": "^15.3.0",
53
57
  "@types/node": "^22.13.9",
54
58
  "@types/react": "^19.0.10",
55
59
  "@types/react-dom": "^19.0.4",
56
60
  "esbuild": "^0.25.0",
57
- "typescript": "^5.8.0"
61
+ "rollup-plugin-dts": "^6.1.1",
62
+ "typescript": "^5.8.0",
63
+ "vite": "^6.2.0"
58
64
  },
59
65
  "dependencies": {
60
- "@quilted/threads": "^4.0.3",
61
- "@remote-dom/core": "^1.11.1",
62
- "@remote-dom/react": "^1.2.2",
63
- "socket.io-client": "^4.8.0"
66
+ "@radix-ui/react-context": "1.1.2",
67
+ "@radix-ui/react-dialog": "^1.1.14",
68
+ "@radix-ui/react-dropdown-menu": "^2.1.16",
69
+ "@radix-ui/react-menubar": "^1.1.6",
70
+ "@radix-ui/react-popover": "^1.1.7",
71
+ "@radix-ui/react-slider": "^1.2.3",
72
+ "@radix-ui/react-switch": "^1.2.5",
73
+ "@radix-ui/react-tabs": "^1.1.13",
74
+ "@radix-ui/react-toggle": "^1.1.10",
75
+ "@radix-ui/react-toggle-group": "^1.1.11",
76
+ "class-variance-authority": "^0.7.1",
77
+ "cmdk": "^1.1.1",
78
+ "react-day-picker": "8.10.1",
79
+ "react-resizable-panels": "^3.0.6",
80
+ "socket.io-client": "^4.8.0",
81
+ "vaul": "^1.1.2"
64
82
  },
65
83
  "peerDependencies": {
66
84
  "react": ">=18",
67
- "react-dom": ">=18"
85
+ "react-dom": ">=18",
86
+ "vite": ">=5"
87
+ },
88
+ "peerDependenciesMeta": {
89
+ "vite": {
90
+ "optional": true
91
+ }
68
92
  }
69
- }
93
+ }
package/src/host-ui.ts ADDED
@@ -0,0 +1,80 @@
1
+ import { ReactElement } from "react";
2
+
3
+ export interface PluginUIServices {
4
+ alert: (params: {
5
+ title?: string;
6
+ message: string;
7
+ buttonText?: string;
8
+ }) => Promise<void>;
9
+ confirm: (params: {
10
+ title?: string;
11
+ message: string;
12
+ buttonText?: {
13
+ yes: string;
14
+ no: string;
15
+ };
16
+ }) => Promise<boolean>;
17
+ prompt: (params: {
18
+ title?: string;
19
+ message: string;
20
+ value?: string;
21
+ placeholder?: string;
22
+ buttonText?: {
23
+ yes: string;
24
+ no: string;
25
+ };
26
+ }) => Promise<string | undefined>;
27
+ toast: (params: {
28
+ title?: string;
29
+ message: string | ReactElement;
30
+ type?: "info" | "danger" | "warning" | "caution" | "success";
31
+ actionLabel?: string;
32
+ handleActionClick?: () => void;
33
+ closeOnActionClick?: boolean;
34
+ duration?: number;
35
+ onClose?: () => void;
36
+ }) => void;
37
+ }
38
+
39
+ let services: PluginUIServices | null = null;
40
+
41
+ /** Called by the host when it mounts a plugin. */
42
+ export function setHostServices(next: PluginUIServices): void {
43
+ services = next;
44
+ }
45
+
46
+ /** Read the injected services, throwing a clear error if the host forgot. */
47
+ export function getHostServices(): PluginUIServices {
48
+ if (!services) {
49
+ throw new Error(
50
+ "[plugin-ui] services not set — the plugin must be mounted by a Muscat host.",
51
+ );
52
+ }
53
+ return services;
54
+ }
55
+
56
+ /** Open a host alert dialog from a plugin. */
57
+ export function alert(
58
+ params: Parameters<PluginUIServices["alert"]>[0],
59
+ ): Promise<void> {
60
+ return getHostServices().alert(params);
61
+ }
62
+
63
+ /** Open a host confirm dialog from a plugin. Resolves true/false. */
64
+ export function confirm(
65
+ params: Parameters<PluginUIServices["confirm"]>[0],
66
+ ): Promise<boolean> {
67
+ return getHostServices().confirm(params);
68
+ }
69
+
70
+ /** Open a host prompt dialog from a plugin. Resolves the entered value, or undefined if cancelled. */
71
+ export function prompt(
72
+ params: Parameters<PluginUIServices["prompt"]>[0],
73
+ ): Promise<string | undefined> {
74
+ return getHostServices().prompt(params);
75
+ }
76
+
77
+ /** Show a host toast from a plugin. */
78
+ export function toast(params: Parameters<PluginUIServices["toast"]>[0]): void {
79
+ getHostServices().toast(params);
80
+ }