@rainbow-robotics/plugin-ui 1.3.8-dev.4 → 1.10.1-dev.1

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/src/sdk.ts CHANGED
@@ -1,175 +1,175 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- /**
3
- * Plugin runtime SDK (`useRb()`).
4
- *
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.
8
- */
9
- import { createContext, useContext, useEffect, useRef } from "react";
10
- import type {
11
- WsSubscriber as Subscriber,
12
- WsQueryable as Queryable,
13
- } from "./ws";
14
- // Type-only import — no runtime coupling to host-ui.ts (which itself has no
15
- // dependency back on sdk.ts, so this stays one-directional).
16
- import type { PluginUIServices } from "./host-ui";
17
-
18
- export type RbNotifyType = "info" | "success" | "warning" | "error";
19
-
20
- export interface RbSdk {
21
- /**
22
- * Publish JSON payload to a topic.
23
- * "sensor/data" → {pluginId}/sensor/data
24
- * "/broadcast/x" → broadcast/x (absolute)
25
- */
26
- pub: <P = any>(topic: string, payload: P) => Promise<void>;
27
-
28
- /** Subscribe to a topic. Returns a handle with undeclare(). */
29
- sub: <P = any>(
30
- topic: string,
31
- handler: (payload: P) => void,
32
- ) => Promise<Subscriber>;
33
-
34
- /**
35
- * Send a request and await the first reply (Zenoh query).
36
- * Use this to call a backend handler registered with rb.serve() / ZenohRouter.
37
- */
38
- action: <R = any, P = any>(topic: string, payload?: P) => Promise<R>;
39
-
40
- /**
41
- * Register a queryable — the frontend responds to backend queries.
42
- * Mirror of rb.action() on the other side.
43
- */
44
- serve: <P = any, R = any>(
45
- topic: string,
46
- handler: (payload?: P) => R | Promise<R>,
47
- ) => Promise<Queryable>;
48
-
49
- /** Show a transient notification through the host's toast system. */
50
- toast: PluginUIServices["toast"];
51
-
52
- /** Show a host alert dialog. */
53
- alert: PluginUIServices["alert"];
54
- /** Show a host confirm dialog. Resolves true/false. */
55
- confirm: PluginUIServices["confirm"];
56
- /** Show a host prompt dialog. Resolves the entered value, or undefined if cancelled. */
57
- prompt: PluginUIServices["prompt"];
58
-
59
- /** Push a step tree into the host Program page as an unsaved edit. */
60
- program: PluginUIServices["program"];
61
-
62
- /**
63
- * Host-provided initial data for this mount — e.g. the previously-saved
64
- * values when the host re-opens a form. `undefined` for a fresh mount.
65
- * Prefer the `useRbFormData()` hook over reading this directly.
66
- */
67
- data?: unknown;
68
-
69
- /**
70
- * Register a getter the host calls (synchronously) to collect this plugin's
71
- * save payload — e.g. when the host's Save button is clicked. The returned
72
- * value must be JSON-serialisable (the host persists it). Returns an
73
- * unregister fn. Prefer the `useRbSave()` hook.
74
- */
75
- onSave: (getPayload: () => unknown) => () => void;
76
-
77
- /**
78
- * PCs allowed to talk to this plugin's ROS 2 nodes. ROS discovery is robot-local
79
- * (ROS_AUTOMATIC_DISCOVERY_RANGE=LOCALHOST); Muscat keeps this list per plugin, starts
80
- * the plugin with ROS_STATIC_PEERS set to it, and restarts the plugin when it changes.
81
- */
82
- rosPeers: {
83
- get: () => Promise<string[]>;
84
- /** Saves the list and restarts the plugin backend. Rejects on an invalid IP. */
85
- set: (peers: string[]) => Promise<string[]>;
86
- };
87
- }
88
-
89
- const noopSdk: RbSdk = {
90
- pub: async <P = any>(_topic?: string, _payload?: P) => {
91
- console.warn("[plugin-ui] rb.pub called outside of a host context");
92
- },
93
- sub: async <P = any>(_topic?: string, _handler?: (payload: P) => void) => {
94
- console.warn("[plugin-ui] rb.sub called outside of a host context");
95
- return { undeclare: async () => {} } as unknown as Subscriber;
96
- },
97
- action: async <R = any, P = any>(_topic?: string, _payload?: P) => {
98
- console.warn("[plugin-ui] rb.action called outside of a host context");
99
- return undefined as R;
100
- },
101
- serve: async <P = any, R = any>(
102
- _topic?: string,
103
- _handler?: (payload?: P) => R,
104
- ) => {
105
- console.warn("[plugin-ui] rb.serve called outside of a host context");
106
- return { undeclare: async () => {} } as unknown as Queryable;
107
- },
108
- toast: (params) => {
109
- console.warn("[plugin-ui] rb.toast (no host):", params);
110
- },
111
- alert: async (_) => {
112
- console.warn("[plugin-ui] rb.alert called outside of a host context");
113
- },
114
- confirm: async (_) => {
115
- console.warn("[plugin-ui] rb.confirm called outside of a host context");
116
- return false;
117
- },
118
- prompt: async (_) => {
119
- console.warn("[plugin-ui] rb.prompt called outside of a host context");
120
- return undefined;
121
- },
122
- program: {
123
- setSteps: async () => {
124
- console.warn("[plugin-ui] rb.program called outside of a host context");
125
- },
126
- getCatalog: async () => {
127
- console.warn("[plugin-ui] rb.program called outside of a host context");
128
- return [];
129
- },
130
- },
131
- data: undefined,
132
- onSave: () => () => {},
133
- rosPeers: {
134
- get: async () => {
135
- console.warn("[plugin-ui] rb.rosPeers called outside of a host context");
136
- return [];
137
- },
138
- set: async () => {
139
- console.warn("[plugin-ui] rb.rosPeers called outside of a host context");
140
- return [];
141
- },
142
- },
143
- };
144
-
145
- export const RbContext = createContext<RbSdk>(noopSdk);
146
-
147
- /** Access the host SDK from within a plugin component. */
148
- export function useRb(): RbSdk {
149
- return useContext(RbContext);
150
- }
151
-
152
- /**
153
- * Read the host-provided initial data for this mount (e.g. previously-saved
154
- * form values). `undefined` for a fresh mount.
155
- *
156
- * const saved = useRbFormData<MyArgs>();
157
- * const methods = useForm({ defaultValues: saved ?? DEFAULTS });
158
- */
159
- export function useRbFormData<T = unknown>(): T | undefined {
160
- return useRb().data as T | undefined;
161
- }
162
-
163
- /**
164
- * Register a getter the host calls to collect this plugin's save payload when
165
- * the host's Save is clicked. The latest `getPayload` is always used, so it can
166
- * close over current state. The returned value must be JSON-serialisable.
167
- *
168
- * useRbSave(() => ({ args: getValues(), summary: buildSummary() }));
169
- */
170
- export function useRbSave<T = unknown>(getPayload: () => T): void {
171
- const rb = useRb();
172
- const ref = useRef(getPayload);
173
- ref.current = getPayload;
174
- useEffect(() => rb.onSave(() => ref.current()), [rb]);
175
- }
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ /**
3
+ * Plugin runtime SDK (`useRb()`).
4
+ *
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.
8
+ */
9
+ import { createContext, useContext, useEffect, useRef } from "react";
10
+ import type {
11
+ WsSubscriber as Subscriber,
12
+ WsQueryable as Queryable,
13
+ } from "./ws";
14
+ // Type-only import — no runtime coupling to host-ui.ts (which itself has no
15
+ // dependency back on sdk.ts, so this stays one-directional).
16
+ import type { PluginUIServices } from "./host-ui";
17
+
18
+ export type RbNotifyType = "info" | "success" | "warning" | "error";
19
+
20
+ export interface RbSdk {
21
+ /**
22
+ * Publish JSON payload to a topic.
23
+ * "sensor/data" → {pluginId}/sensor/data
24
+ * "/broadcast/x" → broadcast/x (absolute)
25
+ */
26
+ pub: <P = any>(topic: string, payload: P) => Promise<void>;
27
+
28
+ /** Subscribe to a topic. Returns a handle with undeclare(). */
29
+ sub: <P = any>(
30
+ topic: string,
31
+ handler: (payload: P) => void,
32
+ ) => Promise<Subscriber>;
33
+
34
+ /**
35
+ * Send a request and await the first reply (Zenoh query).
36
+ * Use this to call a backend handler registered with rb.serve() / ZenohRouter.
37
+ */
38
+ action: <R = any, P = any>(topic: string, payload?: P) => Promise<R>;
39
+
40
+ /**
41
+ * Register a queryable — the frontend responds to backend queries.
42
+ * Mirror of rb.action() on the other side.
43
+ */
44
+ serve: <P = any, R = any>(
45
+ topic: string,
46
+ handler: (payload?: P) => R | Promise<R>,
47
+ ) => Promise<Queryable>;
48
+
49
+ /** Show a transient notification through the host's toast system. */
50
+ toast: PluginUIServices["toast"];
51
+
52
+ /** Show a host alert dialog. */
53
+ alert: PluginUIServices["alert"];
54
+ /** Show a host confirm dialog. Resolves true/false. */
55
+ confirm: PluginUIServices["confirm"];
56
+ /** Show a host prompt dialog. Resolves the entered value, or undefined if cancelled. */
57
+ prompt: PluginUIServices["prompt"];
58
+
59
+ /** Push a step tree into the host Program page as an unsaved edit. */
60
+ program: PluginUIServices["program"];
61
+
62
+ /**
63
+ * Host-provided initial data for this mount — e.g. the previously-saved
64
+ * values when the host re-opens a form. `undefined` for a fresh mount.
65
+ * Prefer the `useRbFormData()` hook over reading this directly.
66
+ */
67
+ data?: unknown;
68
+
69
+ /**
70
+ * Register a getter the host calls (synchronously) to collect this plugin's
71
+ * save payload — e.g. when the host's Save button is clicked. The returned
72
+ * value must be JSON-serialisable (the host persists it). Returns an
73
+ * unregister fn. Prefer the `useRbSave()` hook.
74
+ */
75
+ onSave: (getPayload: () => unknown) => () => void;
76
+
77
+ /**
78
+ * PCs allowed to talk to this plugin's ROS 2 nodes. ROS discovery is robot-local
79
+ * (ROS_AUTOMATIC_DISCOVERY_RANGE=LOCALHOST); Muscat keeps this list per plugin, starts
80
+ * the plugin with ROS_STATIC_PEERS set to it, and restarts the plugin when it changes.
81
+ */
82
+ rosPeers: {
83
+ get: () => Promise<string[]>;
84
+ /** Saves the list and restarts the plugin backend. Rejects on an invalid IP. */
85
+ set: (peers: string[]) => Promise<string[]>;
86
+ };
87
+ }
88
+
89
+ const noopSdk: RbSdk = {
90
+ pub: async <P = any>(_topic?: string, _payload?: P) => {
91
+ console.warn("[plugin-ui] rb.pub called outside of a host context");
92
+ },
93
+ sub: async <P = any>(_topic?: string, _handler?: (payload: P) => void) => {
94
+ console.warn("[plugin-ui] rb.sub called outside of a host context");
95
+ return { undeclare: async () => {} } as unknown as Subscriber;
96
+ },
97
+ action: async <R = any, P = any>(_topic?: string, _payload?: P) => {
98
+ console.warn("[plugin-ui] rb.action called outside of a host context");
99
+ return undefined as R;
100
+ },
101
+ serve: async <P = any, R = any>(
102
+ _topic?: string,
103
+ _handler?: (payload?: P) => R,
104
+ ) => {
105
+ console.warn("[plugin-ui] rb.serve called outside of a host context");
106
+ return { undeclare: async () => {} } as unknown as Queryable;
107
+ },
108
+ toast: (params) => {
109
+ console.warn("[plugin-ui] rb.toast (no host):", params);
110
+ },
111
+ alert: async (_) => {
112
+ console.warn("[plugin-ui] rb.alert called outside of a host context");
113
+ },
114
+ confirm: async (_) => {
115
+ console.warn("[plugin-ui] rb.confirm called outside of a host context");
116
+ return false;
117
+ },
118
+ prompt: async (_) => {
119
+ console.warn("[plugin-ui] rb.prompt called outside of a host context");
120
+ return undefined;
121
+ },
122
+ program: {
123
+ setSteps: async () => {
124
+ console.warn("[plugin-ui] rb.program called outside of a host context");
125
+ },
126
+ getCatalog: async () => {
127
+ console.warn("[plugin-ui] rb.program called outside of a host context");
128
+ return [];
129
+ },
130
+ },
131
+ data: undefined,
132
+ onSave: () => () => {},
133
+ rosPeers: {
134
+ get: async () => {
135
+ console.warn("[plugin-ui] rb.rosPeers called outside of a host context");
136
+ return [];
137
+ },
138
+ set: async () => {
139
+ console.warn("[plugin-ui] rb.rosPeers called outside of a host context");
140
+ return [];
141
+ },
142
+ },
143
+ };
144
+
145
+ export const RbContext = createContext<RbSdk>(noopSdk);
146
+
147
+ /** Access the host SDK from within a plugin component. */
148
+ export function useRb(): RbSdk {
149
+ return useContext(RbContext);
150
+ }
151
+
152
+ /**
153
+ * Read the host-provided initial data for this mount (e.g. previously-saved
154
+ * form values). `undefined` for a fresh mount.
155
+ *
156
+ * const saved = useRbFormData<MyArgs>();
157
+ * const methods = useForm({ defaultValues: saved ?? DEFAULTS });
158
+ */
159
+ export function useRbFormData<T = unknown>(): T | undefined {
160
+ return useRb().data as T | undefined;
161
+ }
162
+
163
+ /**
164
+ * Register a getter the host calls to collect this plugin's save payload when
165
+ * the host's Save is clicked. The latest `getPayload` is always used, so it can
166
+ * close over current state. The returned value must be JSON-serialisable.
167
+ *
168
+ * useRbSave(() => ({ args: getValues(), summary: buildSummary() }));
169
+ */
170
+ export function useRbSave<T = unknown>(getPayload: () => T): void {
171
+ const rb = useRb();
172
+ const ref = useRef(getPayload);
173
+ ref.current = getPayload;
174
+ useEffect(() => rb.onSave(() => ref.current()), [rb]);
175
+ }