@rainbow-robotics/plugin-ui 1.3.6-dev.8 → 1.3.7
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/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +2 -0
- package/dist/manifest.js.map +2 -2
- package/dist/react.d.ts +101 -38
- package/dist/sdk.d.ts +10 -0
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +10 -0
- package/dist/sdk.js.map +2 -2
- package/dist/ws.d.ts +5 -0
- package/dist/ws.d.ts.map +1 -1
- package/dist/ws.js +21 -0
- package/dist/ws.js.map +2 -2
- package/package.json +2 -2
- package/src/manifest.ts +2 -0
- package/src/sdk.ts +21 -0
- package/src/ws.ts +28 -0
package/dist/manifest.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AACA;;;;;GAKG;AAEH,sDAAsD;AACtD,eAAO,MAAM,mBAAmB,4DAKtB,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAErE,4DAA4D;AAC5D,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;AAE7D,MAAM,WAAW,kBAAkB;IACjC,sCAAsC;IACtC,KAAK,EAAE,iBAAiB,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IACzC,gFAAgF;IAChF,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,sEAAsE;IACtE,IAAI,CAAC,EACD,MAAM,GACN;QACE,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACN,qBAAqB;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,yDAAyD;IACzD,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B,WAAW,CAAC,EAAE;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KAC5B,CAAC;CACH;AAED,MAAM,WAAW,YAAY;IAC3B,wDAAwD;IACxD,EAAE,EAAE,MAAM,CAAC;IACX,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,oDAAoD;AACpD,eAAO,MAAM,cAAc,mDAAoD,CAAC;AAEhF,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjE,mEAAmE;AACnE,MAAM,WAAW,cAAc;IAC7B,gGAAgG;IAChG,IAAI,EAAE,OAAO,MAAM,EAAE,CAAC;IACtB,IAAI,EAAE,kBAAkB,CAAC;IACzB,yCAAyC;IACzC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,GAAG,EAAE,CAAC;IACxC,2EAA2E;IAC3E,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/C;AAED,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACjC;IACE,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC/B,CAAC;AAEN,MAAM,WAAW,cAAc;IAC7B,8DAA8D;IAC9D,EAAE,EAAE,MAAM,CAAC;IACX,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,yCAAyC;IACzC,KAAK,EACD,MAAM,GACN;QACE,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACN,iCAAiC;IACjC,WAAW,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACnC,0CAA0C;IAC1C,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,cAAc,EAAE,CAAC;IAC7B,oCAAoC;IACpC,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"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"}
|
package/dist/manifest.js
CHANGED
|
@@ -13,6 +13,8 @@ function validateManifest(value) {
|
|
|
13
13
|
const m = value;
|
|
14
14
|
if (typeof m.id !== "string" || m.id.length === 0)
|
|
15
15
|
errors.push("manifest.id is required (string)");
|
|
16
|
+
else if (!/^[a-z][a-z0-9]*(-[a-z0-9]+)*(\.[a-z][a-z0-9]*(-[a-z0-9]+)*)*$/.test(m.id))
|
|
17
|
+
errors.push('manifest.id must be "."-separated parts of lowercase letters/digits and single "-", each starting with a letter (e.g. com.example.my-plugin)');
|
|
16
18
|
if (typeof m.name !== "string" || m.name.length === 0)
|
|
17
19
|
errors.push("manifest.name is required (string)");
|
|
18
20
|
if (typeof m.version !== "string" || m.version.length === 0)
|
package/dist/manifest.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/manifest.ts"],
|
|
4
|
-
"sourcesContent": ["/* eslint-disable @typescript-eslint/no-explicit-any */\n/**\n * Plugin manifest schema \u2014 the single source of truth shared by the front end\n * (nexus reads it) and the back end (rby2 mirrors it for validation).\n *\n * Evolve additively only.\n */\n\n/** Known contribution points the host understands. */\nexport const CONTRIBUTION_POINTS = [\n \"toolbar\",\n \"lnb\",\n \"move.panel\",\n \"program.panel\",\n] as const;\n\nexport type ContributionPoint = (typeof CONTRIBUTION_POINTS)[number];\n\n/** How a header.toolbar contribution opens when clicked. */\nexport type ContributionOpens = \"popover\" | \"panel\" | \"page\";\n\nexport interface PluginContribution {\n /** Where this contribution mounts. */\n point: ContributionPoint | (string & {});\n /** UI entry name relative to ui/ (without extension). Defaults to \"content\". */\n ui?: string;\n /** Icon path relative to the plugin root (e.g. \"assets/icon.svg\"). */\n icon?:\n | string\n | {\n \"16\"?: string;\n \"48\"?: string;\n \"126\"?: string;\n };\n /** Display label. */\n label?: string | Record<string, string>;\n /** For header.toolbar: how the contribution surfaces. */\n opens?: ContributionOpens;\n commandInfo?: {\n name: string;\n icon: string;\n funcName: string;\n args?: Record<string, any>;\n };\n}\n\nexport interface PluginAction {\n /** Action id referenced by `rb.action(id, payload)`. */\n id: string;\n /** Transport topic (Zenoh / ROS2) the back end routes to. */\n topic: string;\n /** Optional payload schema hint (field \u2192 type name). */\n schema?: Record<string, string>;\n}\n\n/** Allowed variable value types exposed to pyfm. */\nexport const VARIABLE_TYPES = [\"number\", \"string\", \"boolean\", \"array\"] as const;\n\nexport type PluginVariableType = (typeof VARIABLE_TYPES)[number];\n\n/** A variable the plugin pre-declares for use in pyfm programs. */\nexport interface PluginVariable {\n /** Identifier, must start with \"RBP_\" (then letters, digits, underscore). Unique per plugin. */\n name: `RBP_${string}`;\n type: PluginVariableType;\n /** Initial value \u2014 must match `type`. */\n init: number | string | boolean | any[];\n /** Optional. Without it the UI cannot explain what the variable is for. */\n description?: string | Record<string, string>;\n}\n\nexport type PluginRuntime =\n | { type: \"none\" }\n | { type: \"python\"; entry: string }\n | {\n type: \"ros2\";\n package: string;\n launch: string;\n rosClient: \"rclpy\" | \"rclcpp\";\n };\n\nexport interface PluginManifest {\n /** Reverse-DNS unique id, e.g. \"com.rainbow.gripper-tool\". */\n id: string;\n /** Human-readable name. */\n name: string;\n /** Plugin version (semver). */\n version: string;\n /** Minimum host (Muscat) version this plugin supports. */\n minAppVersion?: string;\n /** Icon path relative to plugin root. */\n icons:\n | string\n | {\n \"16\"?: string;\n \"48\"?: string;\n \"128\"?: string;\n };\n /** UI / chrome contributions. */\n contributes?: PluginContribution[];\n /** Host actions the plugin can invoke. */\n actions?: PluginAction[];\n /** Variables pre-declared for pyfm; the variable picker UI lists these. */\n variables?: PluginVariable[];\n /** Back-end runtime declaration. */\n runtime?: PluginRuntime;\n}\n\n/**\n * Lightweight structural check. Returns the list of problems (empty = valid).\n * Kept dependency-free so both front and back ends can reuse it; richer\n * validation (zod) can layer on top later without breaking this contract.\n */\nexport function validateManifest(value: unknown): string[] {\n const errors: string[] = [];\n if (typeof value !== \"object\" || value === null) {\n return [\"manifest must be an object\"];\n }\n const m = value as Record<string, unknown>;\n if (typeof m.id !== \"string\" || m.id.length === 0)\n errors.push(\"manifest.id is required (string)\");\n if (typeof m.name !== \"string\" || m.name.length === 0)\n errors.push(\"manifest.name is required (string)\");\n if (typeof m.version !== \"string\" || m.version.length === 0)\n errors.push(\"manifest.version is required (string)\");\n if (m.contributes !== undefined && !Array.isArray(m.contributes))\n errors.push(\"manifest.contributes must be an array\");\n if (m.actions !== undefined && !Array.isArray(m.actions))\n errors.push(\"manifest.actions must be an array\");\n if (m.variables !== undefined && !Array.isArray(m.variables))\n errors.push(\"manifest.variables must be an array\");\n return errors;\n}\n"],
|
|
5
|
-
"mappings": "AASO,MAAM,sBAAsB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AA0CO,MAAM,iBAAiB,CAAC,UAAU,UAAU,WAAW,OAAO;AAyD9D,SAAS,iBAAiB,OAA0B;AACzD,QAAM,SAAmB,CAAC;AAC1B,MAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,WAAO,CAAC,4BAA4B;AAAA,EACtC;AACA,QAAM,IAAI;AACV,MAAI,OAAO,EAAE,OAAO,YAAY,EAAE,GAAG,WAAW;AAC9C,WAAO,KAAK,kCAAkC;
|
|
4
|
+
"sourcesContent": ["/* eslint-disable @typescript-eslint/no-explicit-any */\n/**\n * Plugin manifest schema \u2014 the single source of truth shared by the front end\n * (nexus reads it) and the back end (rby2 mirrors it for validation).\n *\n * Evolve additively only.\n */\n\n/** Known contribution points the host understands. */\nexport const CONTRIBUTION_POINTS = [\n \"toolbar\",\n \"lnb\",\n \"move.panel\",\n \"program.panel\",\n] as const;\n\nexport type ContributionPoint = (typeof CONTRIBUTION_POINTS)[number];\n\n/** How a header.toolbar contribution opens when clicked. */\nexport type ContributionOpens = \"popover\" | \"panel\" | \"page\";\n\nexport interface PluginContribution {\n /** Where this contribution mounts. */\n point: ContributionPoint | (string & {});\n /** UI entry name relative to ui/ (without extension). Defaults to \"content\". */\n ui?: string;\n /** Icon path relative to the plugin root (e.g. \"assets/icon.svg\"). */\n icon?:\n | string\n | {\n \"16\"?: string;\n \"48\"?: string;\n \"126\"?: string;\n };\n /** Display label. */\n label?: string | Record<string, string>;\n /** For header.toolbar: how the contribution surfaces. */\n opens?: ContributionOpens;\n commandInfo?: {\n name: string;\n icon: string;\n funcName: string;\n args?: Record<string, any>;\n };\n}\n\nexport interface PluginAction {\n /** Action id referenced by `rb.action(id, payload)`. */\n id: string;\n /** Transport topic (Zenoh / ROS2) the back end routes to. */\n topic: string;\n /** Optional payload schema hint (field \u2192 type name). */\n schema?: Record<string, string>;\n}\n\n/** Allowed variable value types exposed to pyfm. */\nexport const VARIABLE_TYPES = [\"number\", \"string\", \"boolean\", \"array\"] as const;\n\nexport type PluginVariableType = (typeof VARIABLE_TYPES)[number];\n\n/** A variable the plugin pre-declares for use in pyfm programs. */\nexport interface PluginVariable {\n /** Identifier, must start with \"RBP_\" (then letters, digits, underscore). Unique per plugin. */\n name: `RBP_${string}`;\n type: PluginVariableType;\n /** Initial value \u2014 must match `type`. */\n init: number | string | boolean | any[];\n /** Optional. Without it the UI cannot explain what the variable is for. */\n description?: string | Record<string, string>;\n}\n\nexport type PluginRuntime =\n | { type: \"none\" }\n | { type: \"python\"; entry: string }\n | {\n type: \"ros2\";\n package: string;\n launch: string;\n rosClient: \"rclpy\" | \"rclcpp\";\n };\n\nexport interface PluginManifest {\n /** Reverse-DNS unique id, e.g. \"com.rainbow.gripper-tool\". */\n id: string;\n /** Human-readable name. */\n name: string;\n /** Plugin version (semver). */\n version: string;\n /** Minimum host (Muscat) version this plugin supports. */\n minAppVersion?: string;\n /** Icon path relative to plugin root. */\n icons:\n | string\n | {\n \"16\"?: string;\n \"48\"?: string;\n \"128\"?: string;\n };\n /** UI / chrome contributions. */\n contributes?: PluginContribution[];\n /** Host actions the plugin can invoke. */\n actions?: PluginAction[];\n /** Variables pre-declared for pyfm; the variable picker UI lists these. */\n variables?: PluginVariable[];\n /** Back-end runtime declaration. */\n runtime?: PluginRuntime;\n}\n\n/**\n * Lightweight structural check. Returns the list of problems (empty = valid).\n * Kept dependency-free so both front and back ends can reuse it; richer\n * validation (zod) can layer on top later without breaking this contract.\n */\nexport function validateManifest(value: unknown): string[] {\n const errors: string[] = [];\n if (typeof value !== \"object\" || value === null) {\n return [\"manifest must be an object\"];\n }\n const m = value as Record<string, unknown>;\n if (typeof m.id !== \"string\" || m.id.length === 0)\n errors.push(\"manifest.id is required (string)\");\n else if (!/^[a-z][a-z0-9]*(-[a-z0-9]+)*(\\.[a-z][a-z0-9]*(-[a-z0-9]+)*)*$/.test(m.id))\n errors.push('manifest.id must be \".\"-separated parts of lowercase letters/digits and single \"-\", each starting with a letter (e.g. com.example.my-plugin)');\n if (typeof m.name !== \"string\" || m.name.length === 0)\n errors.push(\"manifest.name is required (string)\");\n if (typeof m.version !== \"string\" || m.version.length === 0)\n errors.push(\"manifest.version is required (string)\");\n if (m.contributes !== undefined && !Array.isArray(m.contributes))\n errors.push(\"manifest.contributes must be an array\");\n if (m.actions !== undefined && !Array.isArray(m.actions))\n errors.push(\"manifest.actions must be an array\");\n if (m.variables !== undefined && !Array.isArray(m.variables))\n errors.push(\"manifest.variables must be an array\");\n return errors;\n}\n"],
|
|
5
|
+
"mappings": "AASO,MAAM,sBAAsB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AA0CO,MAAM,iBAAiB,CAAC,UAAU,UAAU,WAAW,OAAO;AAyD9D,SAAS,iBAAiB,OAA0B;AACzD,QAAM,SAAmB,CAAC;AAC1B,MAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,WAAO,CAAC,4BAA4B;AAAA,EACtC;AACA,QAAM,IAAI;AACV,MAAI,OAAO,EAAE,OAAO,YAAY,EAAE,GAAG,WAAW;AAC9C,WAAO,KAAK,kCAAkC;AAAA,WACvC,CAAC,gEAAgE,KAAK,EAAE,EAAE;AACjF,WAAO,KAAK,8IAA8I;AAC5J,MAAI,OAAO,EAAE,SAAS,YAAY,EAAE,KAAK,WAAW;AAClD,WAAO,KAAK,oCAAoC;AAClD,MAAI,OAAO,EAAE,YAAY,YAAY,EAAE,QAAQ,WAAW;AACxD,WAAO,KAAK,uCAAuC;AACrD,MAAI,EAAE,gBAAgB,UAAa,CAAC,MAAM,QAAQ,EAAE,WAAW;AAC7D,WAAO,KAAK,uCAAuC;AACrD,MAAI,EAAE,YAAY,UAAa,CAAC,MAAM,QAAQ,EAAE,OAAO;AACrD,WAAO,KAAK,mCAAmC;AACjD,MAAI,EAAE,cAAc,UAAa,CAAC,MAAM,QAAQ,EAAE,SAAS;AACzD,WAAO,KAAK,qCAAqC;AACnD,SAAO;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/react.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
|
2
2
|
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
3
3
|
import { VariantProps } from 'class-variance-authority';
|
|
4
4
|
import * as React$1 from 'react';
|
|
5
|
-
import React__default, { ReactNode, MouseEvent, InputHTMLAttributes, MouseEventHandler, ReactElement, ChangeEvent, CSSProperties, TextareaHTMLAttributes, ComponentType, ComponentProps, Dispatch, SetStateAction, HTMLInputAutoCompleteAttribute, RefObject, HTMLAttributes } from 'react';
|
|
5
|
+
import React__default, { ReactNode, MouseEvent, InputHTMLAttributes, MouseEventHandler, ReactElement, ChangeEvent, CSSProperties, TextareaHTMLAttributes, ComponentType, ComponentProps, Dispatch, SetStateAction, HTMLInputAutoCompleteAttribute, RefObject, SVGAttributes, HTMLAttributes } from 'react';
|
|
6
6
|
import { Matcher, DayPicker } from 'react-day-picker';
|
|
7
7
|
import * as ShadDialogPrimitive from '@radix-ui/react-dialog';
|
|
8
8
|
import { Command } from 'cmdk';
|
|
@@ -65,7 +65,7 @@ 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?: "
|
|
68
|
+
size?: "xs" | "xs2" | "sm" | "md" | "3xs" | "2xs";
|
|
69
69
|
variant?: "transparent" | "outlined" | "soft" | "solid" | "inverse" | "inverseSoft" | "brand" | "brandSoft" | "danger" | "dangerSoft" | "success" | "successSoft";
|
|
70
70
|
disabled?: boolean;
|
|
71
71
|
} & class_variance_authority_types.ClassProp) => string;
|
|
@@ -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
|
|
|
@@ -541,6 +541,14 @@ interface ModalPropsType<T = AllowAny> {
|
|
|
541
541
|
hideCloseButton?: boolean;
|
|
542
542
|
}
|
|
543
543
|
|
|
544
|
+
type KeypadOption = {
|
|
545
|
+
initTab?: string;
|
|
546
|
+
};
|
|
547
|
+
type VariableModalProps = ModalPropsType<string> & {
|
|
548
|
+
keypadOption?: KeypadOption;
|
|
549
|
+
byKeypad?: boolean;
|
|
550
|
+
};
|
|
551
|
+
|
|
544
552
|
interface Props$e extends TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
545
553
|
className?: string;
|
|
546
554
|
type?: "text" | "program";
|
|
@@ -550,7 +558,7 @@ interface Props$e extends TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
|
550
558
|
readOnly?: boolean;
|
|
551
559
|
maxLength?: number;
|
|
552
560
|
error?: boolean;
|
|
553
|
-
variableModal?: ComponentType<
|
|
561
|
+
variableModal?: ComponentType<VariableModalProps>;
|
|
554
562
|
handleChange?: (value: string, name?: string) => void;
|
|
555
563
|
}
|
|
556
564
|
/**
|
|
@@ -574,7 +582,7 @@ type Props$d = {
|
|
|
574
582
|
/**
|
|
575
583
|
* 선택된 날짜 값
|
|
576
584
|
*/
|
|
577
|
-
value?:
|
|
585
|
+
value?: Date | [Date | undefined, Date | undefined];
|
|
578
586
|
/**
|
|
579
587
|
* 날짜 입력 필드의 이름
|
|
580
588
|
*/
|
|
@@ -607,13 +615,35 @@ type Props$d = {
|
|
|
607
615
|
/**
|
|
608
616
|
* 날짜 값 변경 함수
|
|
609
617
|
*/
|
|
610
|
-
handleChange?: (startValue:
|
|
618
|
+
handleChange?: (startValue: Date | undefined, endValue: Date | undefined, name: string) => void;
|
|
611
619
|
};
|
|
612
620
|
/**
|
|
613
621
|
* 날짜 입력 컴포넌트 입니다.
|
|
614
622
|
*/
|
|
615
623
|
declare const DateInput: ({ className, showType, value, name, disabled, readOnly, size, variant, error, showPresets, showFooter, placeholder, handleChange, }: Props$d) => react_jsx_runtime.JSX.Element;
|
|
616
624
|
|
|
625
|
+
declare const RANGE_DATE_PRESETS: readonly [{
|
|
626
|
+
readonly key: "last-24-hours";
|
|
627
|
+
readonly label: "Last 24 hours";
|
|
628
|
+
}, {
|
|
629
|
+
readonly key: "last-3-days";
|
|
630
|
+
readonly label: "Last 3 days";
|
|
631
|
+
}, {
|
|
632
|
+
readonly key: "last-week";
|
|
633
|
+
readonly label: "Last week";
|
|
634
|
+
}, {
|
|
635
|
+
readonly key: "last-month";
|
|
636
|
+
readonly label: "Last month";
|
|
637
|
+
}, {
|
|
638
|
+
readonly key: "last-3-month";
|
|
639
|
+
readonly label: "Last 3 month";
|
|
640
|
+
}];
|
|
641
|
+
type RangePresetKey = (typeof RANGE_DATE_PRESETS)[number]["key"];
|
|
642
|
+
declare const getRangePresetDates: (key: RangePresetKey, now?: Date) => {
|
|
643
|
+
from: Date;
|
|
644
|
+
to: Date;
|
|
645
|
+
};
|
|
646
|
+
|
|
617
647
|
type Props$c = {
|
|
618
648
|
className?: string;
|
|
619
649
|
calendarPanelClassName?: string;
|
|
@@ -624,7 +654,7 @@ type Props$c = {
|
|
|
624
654
|
/**
|
|
625
655
|
* 선택된 날짜 값
|
|
626
656
|
*/
|
|
627
|
-
value?: [
|
|
657
|
+
value?: [Date | undefined, Date | undefined];
|
|
628
658
|
/**
|
|
629
659
|
* 날짜 입력 필드의 이름
|
|
630
660
|
*/
|
|
@@ -632,11 +662,15 @@ type Props$c = {
|
|
|
632
662
|
/**
|
|
633
663
|
* 최소 날짜 값
|
|
634
664
|
*/
|
|
635
|
-
min?:
|
|
665
|
+
min?: Date;
|
|
636
666
|
/**
|
|
637
667
|
* 최대 날짜 값
|
|
638
668
|
*/
|
|
639
|
-
max?:
|
|
669
|
+
max?: Date;
|
|
670
|
+
/**
|
|
671
|
+
* 선택 가능한 최대 일수
|
|
672
|
+
*/
|
|
673
|
+
maxDays?: number;
|
|
640
674
|
/**
|
|
641
675
|
* 날짜 입력 필드의 플레이스홀더
|
|
642
676
|
*/
|
|
@@ -672,17 +706,16 @@ type Props$c = {
|
|
|
672
706
|
/**
|
|
673
707
|
* 날짜 값 변경 함수
|
|
674
708
|
*/
|
|
675
|
-
handleChange?: (startValue:
|
|
709
|
+
handleChange?: (startValue: Date | undefined, endValue: Date | undefined, name: string) => void;
|
|
676
710
|
/**
|
|
677
711
|
* 표시할 프리셋 키 목록. 미지정 시 전체 표시.
|
|
678
712
|
*/
|
|
679
713
|
presets?: RangePresetKey[];
|
|
680
714
|
};
|
|
681
|
-
type RangePresetKey = "last-24-hours" | "last-3-days" | "last-week" | "last-month" | "last-3-month";
|
|
682
715
|
/**
|
|
683
716
|
* 범위 날짜 입력 컴포넌트 입니다.
|
|
684
717
|
*/
|
|
685
|
-
declare const RangeDateInput: ({ className, calendarPanelClassName, showType, value, name, min, max, placeholder, disabled, disabledDates, readOnly, size, variant, error, showPresets, showFooter, handleChange, presets, }: Props$c) => react_jsx_runtime.JSX.Element;
|
|
718
|
+
declare const RangeDateInput: ({ className, calendarPanelClassName, showType, value, name, min, max, maxDays, placeholder, disabled, disabledDates, readOnly, size, variant, error, showPresets, showFooter, handleChange, presets, }: Props$c) => react_jsx_runtime.JSX.Element;
|
|
686
719
|
|
|
687
720
|
interface Props$b extends ModalPropsType<boolean> {
|
|
688
721
|
className?: string;
|
|
@@ -720,7 +753,7 @@ declare const RBAlert: ({ className, containerId, open, onOpenChange, title, con
|
|
|
720
753
|
interface Props$a extends ModalPropsType<boolean> {
|
|
721
754
|
className?: string;
|
|
722
755
|
/** confirm 타이틀 */
|
|
723
|
-
title?: string;
|
|
756
|
+
title?: string | ReactElement;
|
|
724
757
|
/** confirm 설명 */
|
|
725
758
|
description?: string | ReactElement;
|
|
726
759
|
/** confirm 내용 */
|
|
@@ -743,7 +776,7 @@ interface Props$a extends ModalPropsType<boolean> {
|
|
|
743
776
|
/** 모달 dialog일때 컨테이너 id */
|
|
744
777
|
containerId?: string;
|
|
745
778
|
/** 확인 버튼 클릭 시 실행할 함수 */
|
|
746
|
-
onConfirm?: () => void;
|
|
779
|
+
onConfirm?: () => Promise<void> | void;
|
|
747
780
|
/** 취소 버튼 클릭 시 실행할 함수 */
|
|
748
781
|
onCancel?: () => void;
|
|
749
782
|
}
|
|
@@ -951,6 +984,10 @@ interface PropsType$p extends Pick<InputHTMLAttributes<HTMLInputElement>, "name"
|
|
|
951
984
|
* SelectBox의 크기
|
|
952
985
|
*/
|
|
953
986
|
size?: SelectSize;
|
|
987
|
+
/**
|
|
988
|
+
* SelectBox DropBox의 최대 높이
|
|
989
|
+
*/
|
|
990
|
+
maxDropBoxHeight?: number;
|
|
954
991
|
/**
|
|
955
992
|
* option list를 찾는 search 기능 활성화 여부
|
|
956
993
|
*/
|
|
@@ -2018,6 +2055,8 @@ type Props$3 = {
|
|
|
2018
2055
|
stickColor?: string;
|
|
2019
2056
|
/** 조이스틱 영역의 색상 */
|
|
2020
2057
|
stickAreaColor?: string;
|
|
2058
|
+
/** 방향 아크·아이콘 강조 색상 */
|
|
2059
|
+
arcColor?: string;
|
|
2021
2060
|
/**
|
|
2022
2061
|
* 스로틀 임계치
|
|
2023
2062
|
* (이동 비율 차이가 throttleThreshold 이상일 때 onMove 호출)
|
|
@@ -2050,7 +2089,7 @@ type Props$3 = {
|
|
|
2050
2089
|
/** 조이스틱 종료 시 호출 */
|
|
2051
2090
|
onEnd?: (data: RbJoyStickEventData) => void;
|
|
2052
2091
|
};
|
|
2053
|
-
declare const RBJoyStick: ({ className, type, size, stickColor, stickAreaColor, throttleThreshold, throttleDuration, xOnly, yOnly, disableDiagonal, disabled, keepMoveEventInterval, paddingSize, style, onStart, onMove, onEnd, }: Props$3) => react_jsx_runtime.JSX.Element;
|
|
2092
|
+
declare const RBJoyStick: ({ className, type, size, stickColor, arcColor, stickAreaColor, throttleThreshold, throttleDuration, xOnly, yOnly, disableDiagonal, disabled, keepMoveEventInterval, paddingSize, style, onStart, onMove, onEnd, }: Props$3) => react_jsx_runtime.JSX.Element;
|
|
2054
2093
|
|
|
2055
2094
|
type PlaceType = "top" | "right" | "bottom" | "left";
|
|
2056
2095
|
interface PropsType$6 {
|
|
@@ -2173,6 +2212,8 @@ declare function RBCardFooter({ className, size, ...props }: RBCardFooterProps):
|
|
|
2173
2212
|
|
|
2174
2213
|
declare function ShadDialog({ ...props }: React$1.ComponentProps<typeof ShadDialogPrimitive.Root>): react_jsx_runtime.JSX.Element;
|
|
2175
2214
|
|
|
2215
|
+
declare function Calendar$1({ className, classNames, showOutsideDays, ...props }: React$1.ComponentProps<typeof DayPicker>): react_jsx_runtime.JSX.Element;
|
|
2216
|
+
|
|
2176
2217
|
declare function RBCommand({ className, ...props }: React.ComponentProps<typeof Command>): react_jsx_runtime.JSX.Element;
|
|
2177
2218
|
declare function RBCommandDialog({ title, description, children, ...props }: React.ComponentProps<typeof ShadDialog> & {
|
|
2178
2219
|
title?: string;
|
|
@@ -2285,7 +2326,7 @@ interface PropsType$5 {
|
|
|
2285
2326
|
declare function RBSwitchField({ children, className, controlPlacement, description, descriptionClassName, invalid, label, labelClassName, size, type, }: PropsType$5): react_jsx_runtime.JSX.Element;
|
|
2286
2327
|
|
|
2287
2328
|
declare const toggleRootVariants: (props?: {
|
|
2288
|
-
size?: "sm" | "
|
|
2329
|
+
size?: "sm" | "lg" | "default";
|
|
2289
2330
|
variant?: "default" | "outline";
|
|
2290
2331
|
} & class_variance_authority_types.ClassProp) => string;
|
|
2291
2332
|
type ToggleVariantProps = VariantProps<typeof toggleRootVariants>;
|
|
@@ -2336,7 +2377,7 @@ declare function RBResizable({ className, direction, ...props }: RBResizableProp
|
|
|
2336
2377
|
declare function RBResizablePanel({ className, unstyled, ...props }: RBResizablePanelProps): react_jsx_runtime.JSX.Element;
|
|
2337
2378
|
declare function RBResizableHandle({ className, hideThumb, onPointerDownCapture, onPointerUpCapture, onPointerCancelCapture, ...props }: RBResizableHandleProps): react_jsx_runtime.JSX.Element;
|
|
2338
2379
|
|
|
2339
|
-
declare function Calendar({ classNames, showOutsideDays, ...props }: React.ComponentProps<typeof
|
|
2380
|
+
declare function Calendar({ className, classNames, showOutsideDays, ...props }: React.ComponentProps<typeof Calendar$1>): react_jsx_runtime.JSX.Element;
|
|
2340
2381
|
|
|
2341
2382
|
interface PropsType$4 {
|
|
2342
2383
|
label: string;
|
|
@@ -2348,32 +2389,22 @@ interface DatePickerTimeValue {
|
|
|
2348
2389
|
hour: string;
|
|
2349
2390
|
minute: string;
|
|
2350
2391
|
}
|
|
2392
|
+
interface DatePickerValue {
|
|
2393
|
+
from: Date | undefined;
|
|
2394
|
+
to: Date | undefined;
|
|
2395
|
+
timeEnabled: boolean;
|
|
2396
|
+
}
|
|
2351
2397
|
interface PropsType$3 {
|
|
2352
2398
|
pickerType?: DatePickerType;
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
endTime?: DatePickerTimeValue;
|
|
2356
|
-
onEndTimeChange?: (nextValue: DatePickerTimeValue) => void;
|
|
2357
|
-
onSetTimeChange?: (checked: boolean) => void;
|
|
2358
|
-
onStartTimeChange?: (nextValue: DatePickerTimeValue) => void;
|
|
2359
|
-
selectedDate?: {
|
|
2360
|
-
from: Date | undefined;
|
|
2361
|
-
to: Date | undefined;
|
|
2362
|
-
};
|
|
2363
|
-
setSelectedDate?: (nextValue: {
|
|
2364
|
-
from: Date | undefined;
|
|
2365
|
-
to: Date | undefined;
|
|
2366
|
-
}) => void;
|
|
2367
|
-
enforceTimeOrder?: boolean;
|
|
2368
|
-
min?: number;
|
|
2369
|
-
max?: number;
|
|
2399
|
+
selectedDate: DatePickerValue;
|
|
2400
|
+
onChange: (value: DatePickerValue) => void;
|
|
2370
2401
|
labels?: {
|
|
2371
2402
|
from?: string;
|
|
2372
2403
|
to?: string;
|
|
2373
2404
|
setTime?: string;
|
|
2374
2405
|
};
|
|
2375
2406
|
}
|
|
2376
|
-
declare function RBDatePickerFooterTime({ pickerType,
|
|
2407
|
+
declare function RBDatePickerFooterTime({ pickerType, selectedDate, onChange, labels, }: PropsType$3): react_jsx_runtime.JSX.Element;
|
|
2377
2408
|
|
|
2378
2409
|
interface PropsType$2 {
|
|
2379
2410
|
className?: string;
|
|
@@ -2398,6 +2429,10 @@ declare const checkIconButtonVariants: (props?: {
|
|
|
2398
2429
|
disabled?: boolean;
|
|
2399
2430
|
} & class_variance_authority_types.ClassProp) => string;
|
|
2400
2431
|
|
|
2432
|
+
declare const Svg25DBold: (props?: React__default.SVGAttributes<SVGSVGElement> & {
|
|
2433
|
+
title?: string;
|
|
2434
|
+
description?: string;
|
|
2435
|
+
}) => react_jsx_runtime.JSX.Element;
|
|
2401
2436
|
declare const SvgAMR: (props?: React__default.SVGAttributes<SVGSVGElement> & {
|
|
2402
2437
|
title?: string;
|
|
2403
2438
|
description?: string;
|
|
@@ -2466,6 +2501,10 @@ declare const SvgBulletSolid: (props?: React__default.SVGAttributes<SVGSVGElemen
|
|
|
2466
2501
|
title?: string;
|
|
2467
2502
|
description?: string;
|
|
2468
2503
|
}) => react_jsx_runtime.JSX.Element;
|
|
2504
|
+
declare const SvgCANBus: (props?: React__default.SVGAttributes<SVGSVGElement> & {
|
|
2505
|
+
title?: string;
|
|
2506
|
+
description?: string;
|
|
2507
|
+
}) => react_jsx_runtime.JSX.Element;
|
|
2469
2508
|
declare const SvgCalendarBlankBold: (props?: React__default.SVGAttributes<SVGSVGElement> & {
|
|
2470
2509
|
title?: string;
|
|
2471
2510
|
description?: string;
|
|
@@ -2967,6 +3006,7 @@ declare const SvgWifiCog: (props?: React__default.SVGAttributes<SVGSVGElement> &
|
|
|
2967
3006
|
description?: string;
|
|
2968
3007
|
}) => react_jsx_runtime.JSX.Element;
|
|
2969
3008
|
|
|
3009
|
+
declare const index_Svg25DBold: typeof Svg25DBold;
|
|
2970
3010
|
declare const index_SvgAMR: typeof SvgAMR;
|
|
2971
3011
|
declare const index_SvgArrowArcLeftBold: typeof SvgArrowArcLeftBold;
|
|
2972
3012
|
declare const index_SvgArrowArcRightBold: typeof SvgArrowArcRightBold;
|
|
@@ -2985,6 +3025,7 @@ declare const index_SvgBot: typeof SvgBot;
|
|
|
2985
3025
|
declare const index_SvgBox3D: typeof SvgBox3D;
|
|
2986
3026
|
declare const index_SvgBugBeetleSolid: typeof SvgBugBeetleSolid;
|
|
2987
3027
|
declare const index_SvgBulletSolid: typeof SvgBulletSolid;
|
|
3028
|
+
declare const index_SvgCANBus: typeof SvgCANBus;
|
|
2988
3029
|
declare const index_SvgCalendarBlankBold: typeof SvgCalendarBlankBold;
|
|
2989
3030
|
declare const index_SvgCameraBold: typeof SvgCameraBold;
|
|
2990
3031
|
declare const index_SvgCameraResetBold: typeof SvgCameraResetBold;
|
|
@@ -3111,6 +3152,7 @@ declare const index_SvgWifiNone: typeof SvgWifiNone;
|
|
|
3111
3152
|
declare const index_SvgXBold: typeof SvgXBold;
|
|
3112
3153
|
declare namespace index {
|
|
3113
3154
|
export {
|
|
3155
|
+
index_Svg25DBold as Svg25DBold,
|
|
3114
3156
|
index_SvgAMR as SvgAMR,
|
|
3115
3157
|
index_SvgArrowArcLeftBold as SvgArrowArcLeftBold,
|
|
3116
3158
|
index_SvgArrowArcRightBold as SvgArrowArcRightBold,
|
|
@@ -3129,6 +3171,7 @@ declare namespace index {
|
|
|
3129
3171
|
index_SvgBox3D as SvgBox3D,
|
|
3130
3172
|
index_SvgBugBeetleSolid as SvgBugBeetleSolid,
|
|
3131
3173
|
index_SvgBulletSolid as SvgBulletSolid,
|
|
3174
|
+
index_SvgCANBus as SvgCANBus,
|
|
3132
3175
|
index_SvgCalendarBlankBold as SvgCalendarBlankBold,
|
|
3133
3176
|
index_SvgCameraBold as SvgCameraBold,
|
|
3134
3177
|
index_SvgCameraResetBold as SvgCameraResetBold,
|
|
@@ -3256,6 +3299,16 @@ declare namespace index {
|
|
|
3256
3299
|
};
|
|
3257
3300
|
}
|
|
3258
3301
|
|
|
3302
|
+
type StatusIconProps = SVGAttributes<SVGSVGElement>;
|
|
3303
|
+
/**
|
|
3304
|
+
* 채워진 원 + 흰색 글리프로 상태를 나타내는 아이콘 한 쌍이다. 이 폴더의
|
|
3305
|
+
* 나머지 아이콘(`index.tsx`)은 auto-svg-component-generator가 `.svg`
|
|
3306
|
+
* 소스로부터 자동 생성하므로, 손으로 그린 이 두 아이콘은 별도 파일로 둔다
|
|
3307
|
+
* — index.tsx는 빌드/dev 스크립트가 돌 때마다 통째로 덮어써진다.
|
|
3308
|
+
*/
|
|
3309
|
+
declare const GreenCheckCircleIcon: ({ className, ...props }: StatusIconProps) => react_jsx_runtime.JSX.Element;
|
|
3310
|
+
declare const AmberWarningCircleIcon: ({ className, ...props }: StatusIconProps) => react_jsx_runtime.JSX.Element;
|
|
3311
|
+
|
|
3259
3312
|
declare const lightColors: readonly ["red", "gray", "blue", "green", "orange", "yellow"];
|
|
3260
3313
|
type LightColor = (typeof lightColors)[number];
|
|
3261
3314
|
declare const lightSizes: readonly ["sm", "md", "lg"];
|
|
@@ -3575,6 +3628,16 @@ interface RbSdk {
|
|
|
3575
3628
|
* unregister fn. Prefer the `useRbSave()` hook.
|
|
3576
3629
|
*/
|
|
3577
3630
|
onSave: (getPayload: () => unknown) => () => void;
|
|
3631
|
+
/**
|
|
3632
|
+
* PCs allowed to talk to this plugin's ROS 2 nodes. ROS discovery is robot-local
|
|
3633
|
+
* (ROS_AUTOMATIC_DISCOVERY_RANGE=LOCALHOST); Muscat keeps this list per plugin, starts
|
|
3634
|
+
* the plugin with ROS_STATIC_PEERS set to it, and restarts the plugin when it changes.
|
|
3635
|
+
*/
|
|
3636
|
+
rosPeers: {
|
|
3637
|
+
get: () => Promise<string[]>;
|
|
3638
|
+
/** Saves the list and restarts the plugin backend. Rejects on an invalid IP. */
|
|
3639
|
+
set: (peers: string[]) => Promise<string[]>;
|
|
3640
|
+
};
|
|
3578
3641
|
}
|
|
3579
3642
|
/** Access the host SDK from within a plugin component. */
|
|
3580
3643
|
declare function useRb(): RbSdk;
|
|
@@ -3595,5 +3658,5 @@ declare function useRbFormData<T = unknown>(): T | undefined;
|
|
|
3595
3658
|
*/
|
|
3596
3659
|
declare function useRbSave<T = unknown>(getPayload: () => T): void;
|
|
3597
3660
|
|
|
3598
|
-
export { HStack, RBSelectOption as Option, Accordion as RBAccordion, AccordionGroup as RBAccordionGroup, RBAlert, RBBadge, RBBattery,
|
|
3599
|
-
export type { PropsType$t as AccordionProps, AccordionVariantProps, PropsType$a as BadgeProps, BadgeVariantProps, BatteryProps, BatteryVariantProps, CardVariantProps, Props$h as CheckBoxProps, CheckBoxVariantProps, Props$g as CheckIconButtonProps, PropsType$h as ChipItemProps, PropsType$i as ChipTabProps, ChipVariantProps, PropsType$f as ChipsBoxItemProps, PropsType$g as ChipsBoxTabProps, Props$d as DateInputProps, PropsType$2 as DatePickerFooterButtonProps, PropsType$4 as DatePickerPresetProps, DatePickerTimeValue, PropsType$q as DimProps, DividerProps, DividerVariantProps, DrawerSide, DropdownItemVariant, PropsType$9 as EmptyViewProps, EmptyViewVariantProps, PropsType$b as ExpendProps, PropsType$1 as HStackProps, HelperTone, IconButtonProps, IconButtonVariantProps, InlineLabelButtonProps, InlineLabelButtonVariantProps, Props$3 as JoyStickProps, LabelButtonProps, LabelButtonVariantProps, LightProps, LightVariantProps, MenubarItemVariant, Props$4 as MultiRangeInputProps, Props$7 as PaginationProps, Props$b as RBAlertProps, RBCardContentProps, RBCardFooterProps, RBCardHeaderProps, RBCardHeaderTextProps, RBCardPreviewSlotProps, RBCardProps, RBCardTitleProps, Props$a as RBConfirmProps, RBDialogContentProps, RBDrawerContentProps, RBDrawerProps, RBPopoverContentProps, RBPopoverPlacement, Props$9 as RBPromptProps, PropsType$l as RBRadioBoxProps, RBResizableHandleProps, RBResizablePanelProps, RBResizableProps, PropsType$p as RBSelectBoxProps, RBSelectBoxRef, RBSheetContentProps, Props$1 as RBSimpleToastProps, RBSliderProps, PropsType$5 as RBSwitchFieldProps, RBSwitchProps, RBTabsContentProps, RBTabsListProps, RBTabsProps, RBTabsTriggerProps, Props$e as RBTextAreaProps, RBToggleGroupItemProps, RBToggleGroupProps, RBToggleProps, Props$5 as RadioButtonProps, RadioVariantProps, Props$c as RangeDateInputProps, RbJoyStickEventData, RbSdk, PropsType$o as SelectOptionProps, SelectTriggerVariantProps, SheetSide, Props$6 as SideNumberInputProps, SideNumberInputVariantProps, SwitchControlPlacement, SwitchSize, SwitchTone, SwitchType, PropsType$j as TabItemProps, PropsType$k as TabProps, TabVariantProps, PropsType$e as TableProps, PropsType$d as TableRowProps, TabsSize, TabsVariant, PropsType$c as TdColumnProps, TextAreaVariantProps, PropsType$7 as TextFieldProps, PropsType$r as TextHelperTextProps, PropsType$s as TextInputProps, TextInputVariantProps, PropsType$8 as TextLabelProps, ToggleGroupOrientation, ToggleVariantProps, PropsType$6 as TooltipProps, PropsType as VStackProps };
|
|
3661
|
+
export { AmberWarningCircleIcon, GreenCheckCircleIcon, HStack, RBSelectOption as Option, RANGE_DATE_PRESETS, Accordion as RBAccordion, AccordionGroup as RBAccordionGroup, RBAlert, RBBadge, RBBattery, RBCard, RBCardAction, RBCardContent, RBCardDescription, RBCardFooter, RBCardHeader, RBCardHeaderText, RBCardPreviewSlot, RBCardTitle, CheckBox as RBCheckBox, RBCheckIconButton, RBCheckLabelButton, RBChipItem, RBChipTab, RBChipsBoxItem, RBChipsBoxTab, RBCommand, RBCommandDialog, RBCommandEmpty, RBCommandGroup, RBCommandInput, RBCommandItem, RBCommandList, RBCommandSelected, RBCommandSeparator, RBCommandShortcut, RBConfirm, DateInput as RBDateInput, RBDatePickerFooterButton, RBDatePickerFooterTime, RBDatePickerPreset, RBDialog, RBDialogClose, RBDialogContent, RBDialogDescription, RBDialogFooter, RBDialogHeader, RBDialogOverlay, RBDialogPortal, RBDialogTitle, RBDialogTrigger, RBDim, RBDivider, RBDrawer, RBDrawerBody, RBDrawerClose, RBDrawerContent, RBDrawerDescription, RBDrawerFooter, RBDrawerHeader, RBDrawerOverlay, RBDrawerPortal, RBDrawerTitle, RBDrawerTrigger, RBDropdownMenu, RBDropdownMenuCheckboxItem, RBDropdownMenuContent, RBDropdownMenuGroup, RBDropdownMenuItem, RBDropdownMenuLabel, RBDropdownMenuPortal, RBDropdownMenuRadioGroup, RBDropdownMenuRadioItem, RBDropdownMenuSeparator, RBDropdownMenuShortcut, RBDropdownMenuSub, RBDropdownMenuSubContent, RBDropdownMenuSubTrigger, RBDropdownMenuTrigger, RBEmptyView, RBIconButton, index as RBIcons, RBInlineLabelButton, RBJoyStick, RBLabelButton, RBLight, RBMenubar, RBMenubarCheckboxItem, RBMenubarContent, RBMenubarGroup, RBMenubarItem, RBMenubarLabel, RBMenubarMenu, RBMenubarPortal, RBMenubarRadioGroup, RBMenubarRadioItem, RBMenubarSeparator, RBMenubarShortcut, RBMenubarSub, RBMenubarSubContent, RBMenubarSubTrigger, RBMenubarTrigger, RBModal, MultiRangeInput as RBMultiRangeInput, RBNoData, RBPagination, RBPopover, RBPopoverAnchor, RBPopoverContent, RBPopoverPlacements, RBPopoverTrigger, RBPrompt, RBQrBarCodeGuideModal, RBRadioBox, RBRadioButton, RangeDateInput as RBRangeDateInput, RBResizable, RBResizableHandle, RBResizablePanel, RBResizable as RBResizablePanelGroup, RBSelectBox, RBSelectDropBox, RBSelectOption, Calendar as RBShadCalendar, RBSheet, RBSheetClose, RBSheetContent, RBSheetDescription, RBSheetFooter, RBSheetHeader, RBSheetOverlay, RBSheetPortal, RBSheetTitle, RBSheetTrigger, SideNumberInput as RBSideNumberInput, RBSimpleToast, RBSlider, RBSpinner, RBSwitch, RBSwitchField, RBTab, RBTabItem, RBTable, RBTableRow, RBTabs, RBTabsContent, RBTabsList, RBTabsTrigger, RBTdColumn, RBTdExpend, RBTextArea, RBTextField, RBTextHelperText, RBTextInput, RBTextLabel, RBToggle, RBToggleGroup, RBToggleGroupItem, RBTooltip, VStack, accordionVariants, badgeRootVariants, batteryFillVariants, cardRootVariants, checkBoxRootVariants, checkIconButtonVariants, checkLabelButtonVariants, chipRootVariants, dividerVariants, emptyViewRootVariants, getRangePresetDates, iconButtonVariants, inlineLabelButtonVariants, labelButtonVariants, lightColors, lightSizes, radioRootVariants, selectTriggerVariants, sideNumberInputVariants, tabRootVariants, textAreaVariants, textInputVariants, useRb, useRbFormData, useRbSave };
|
|
3662
|
+
export type { PropsType$t as AccordionProps, AccordionVariantProps, PropsType$a as BadgeProps, BadgeVariantProps, BatteryProps, BatteryVariantProps, CardVariantProps, Props$h as CheckBoxProps, CheckBoxVariantProps, Props$g as CheckIconButtonProps, PropsType$h as ChipItemProps, PropsType$i as ChipTabProps, ChipVariantProps, PropsType$f as ChipsBoxItemProps, PropsType$g as ChipsBoxTabProps, Props$d as DateInputProps, PropsType$2 as DatePickerFooterButtonProps, PropsType$4 as DatePickerPresetProps, DatePickerTimeValue, DatePickerValue, PropsType$q as DimProps, DividerProps, DividerVariantProps, DrawerSide, DropdownItemVariant, PropsType$9 as EmptyViewProps, EmptyViewVariantProps, PropsType$b as ExpendProps, PropsType$1 as HStackProps, HelperTone, IconButtonProps, IconButtonVariantProps, InlineLabelButtonProps, InlineLabelButtonVariantProps, Props$3 as JoyStickProps, LabelButtonProps, LabelButtonVariantProps, LightProps, LightVariantProps, MenubarItemVariant, Props$4 as MultiRangeInputProps, Props$7 as PaginationProps, Props$b as RBAlertProps, RBCardContentProps, RBCardFooterProps, RBCardHeaderProps, RBCardHeaderTextProps, RBCardPreviewSlotProps, RBCardProps, RBCardTitleProps, Props$a as RBConfirmProps, RBDialogContentProps, RBDrawerContentProps, RBDrawerProps, RBPopoverContentProps, RBPopoverPlacement, Props$9 as RBPromptProps, PropsType$l as RBRadioBoxProps, RBResizableHandleProps, RBResizablePanelProps, RBResizableProps, PropsType$p as RBSelectBoxProps, RBSelectBoxRef, RBSheetContentProps, Props$1 as RBSimpleToastProps, RBSliderProps, PropsType$5 as RBSwitchFieldProps, RBSwitchProps, RBTabsContentProps, RBTabsListProps, RBTabsProps, RBTabsTriggerProps, Props$e as RBTextAreaProps, RBToggleGroupItemProps, RBToggleGroupProps, RBToggleProps, Props$5 as RadioButtonProps, RadioVariantProps, Props$c as RangeDateInputProps, RangePresetKey, RbJoyStickEventData, RbSdk, PropsType$o as SelectOptionProps, SelectTriggerVariantProps, SheetSide, Props$6 as SideNumberInputProps, SideNumberInputVariantProps, SwitchControlPlacement, SwitchSize, SwitchTone, SwitchType, PropsType$j as TabItemProps, PropsType$k as TabProps, TabVariantProps, PropsType$e as TableProps, PropsType$d as TableRowProps, TabsSize, TabsVariant, PropsType$c as TdColumnProps, TextAreaVariantProps, PropsType$7 as TextFieldProps, PropsType$r as TextHelperTextProps, PropsType$s as TextInputProps, TextInputVariantProps, PropsType$8 as TextLabelProps, ToggleGroupOrientation, ToggleVariantProps, PropsType$6 as TooltipProps, PropsType as VStackProps };
|
package/dist/sdk.d.ts
CHANGED
|
@@ -41,6 +41,16 @@ export interface RbSdk {
|
|
|
41
41
|
* unregister fn. Prefer the `useRbSave()` hook.
|
|
42
42
|
*/
|
|
43
43
|
onSave: (getPayload: () => unknown) => () => void;
|
|
44
|
+
/**
|
|
45
|
+
* PCs allowed to talk to this plugin's ROS 2 nodes. ROS discovery is robot-local
|
|
46
|
+
* (ROS_AUTOMATIC_DISCOVERY_RANGE=LOCALHOST); Muscat keeps this list per plugin, starts
|
|
47
|
+
* the plugin with ROS_STATIC_PEERS set to it, and restarts the plugin when it changes.
|
|
48
|
+
*/
|
|
49
|
+
rosPeers: {
|
|
50
|
+
get: () => Promise<string[]>;
|
|
51
|
+
/** Saves the list and restarts the plugin backend. Rejects on an invalid IP. */
|
|
52
|
+
set: (peers: string[]) => Promise<string[]>;
|
|
53
|
+
};
|
|
44
54
|
}
|
|
45
55
|
export declare const RbContext: import("react").Context<RbSdk>;
|
|
46
56
|
/** Access the host SDK from within a plugin component. */
|
package/dist/sdk.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../src/sdk.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,YAAY,IAAI,UAAU,EAC1B,WAAW,IAAI,SAAS,EACzB,MAAM,MAAM,CAAC;AAGd,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAElD,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAEpE,MAAM,WAAW,KAAK;IACpB;;;;OAIG;IACH,GAAG,EAAE,CAAC,CAAC,GAAG,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAE3D,+DAA+D;IAC/D,GAAG,EAAE,CAAC,CAAC,GAAG,GAAG,EACX,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,KAC1B,OAAO,CAAC,UAAU,CAAC,CAAC;IAEzB;;;OAGG;IACH,MAAM,EAAE,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAErE;;;OAGG;IACH,KAAK,EAAE,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EACtB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,KACrC,OAAO,CAAC,SAAS,CAAC,CAAC;IAExB,qEAAqE;IACrE,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAEjC,gCAAgC;IAChC,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACjC,uDAAuD;IACvD,OAAO,EAAE,gBAAgB,CAAC,SAAS,CAAC,CAAC;IACrC,wFAAwF;IACxF,MAAM,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAEnC;;;;OAIG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;;;OAKG;IACH,MAAM,EAAE,CAAC,UAAU,EAAE,MAAM,OAAO,KAAK,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../src/sdk.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,YAAY,IAAI,UAAU,EAC1B,WAAW,IAAI,SAAS,EACzB,MAAM,MAAM,CAAC;AAGd,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAElD,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAEpE,MAAM,WAAW,KAAK;IACpB;;;;OAIG;IACH,GAAG,EAAE,CAAC,CAAC,GAAG,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAE3D,+DAA+D;IAC/D,GAAG,EAAE,CAAC,CAAC,GAAG,GAAG,EACX,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,KAC1B,OAAO,CAAC,UAAU,CAAC,CAAC;IAEzB;;;OAGG;IACH,MAAM,EAAE,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAErE;;;OAGG;IACH,KAAK,EAAE,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EACtB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,KACrC,OAAO,CAAC,SAAS,CAAC,CAAC;IAExB,qEAAqE;IACrE,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAEjC,gCAAgC;IAChC,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACjC,uDAAuD;IACvD,OAAO,EAAE,gBAAgB,CAAC,SAAS,CAAC,CAAC;IACrC,wFAAwF;IACxF,MAAM,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAEnC;;;;OAIG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;;;OAKG;IACH,MAAM,EAAE,CAAC,UAAU,EAAE,MAAM,OAAO,KAAK,MAAM,IAAI,CAAC;IAElD;;;;OAIG;IACH,QAAQ,EAAE;QACR,GAAG,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QAC7B,gFAAgF;QAChF,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;KAC7C,CAAC;CACH;AAiDD,eAAO,MAAM,SAAS,gCAAgC,CAAC;AAEvD,0DAA0D;AAC1D,wBAAgB,KAAK,IAAI,KAAK,CAE7B;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,CAAC,GAAG,OAAO,KAAK,CAAC,GAAG,SAAS,CAE1D;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,CAAC,GAAG,OAAO,EAAE,UAAU,EAAE,MAAM,CAAC,GAAG,IAAI,CAKhE"}
|
package/dist/sdk.js
CHANGED
|
@@ -33,6 +33,16 @@ const noopSdk = {
|
|
|
33
33
|
},
|
|
34
34
|
data: void 0,
|
|
35
35
|
onSave: () => () => {
|
|
36
|
+
},
|
|
37
|
+
rosPeers: {
|
|
38
|
+
get: async () => {
|
|
39
|
+
console.warn("[plugin-ui] rb.rosPeers called outside of a host context");
|
|
40
|
+
return [];
|
|
41
|
+
},
|
|
42
|
+
set: async () => {
|
|
43
|
+
console.warn("[plugin-ui] rb.rosPeers called outside of a host context");
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
36
46
|
}
|
|
37
47
|
};
|
|
38
48
|
const RbContext = createContext(noopSdk);
|
package/dist/sdk.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/sdk.ts"],
|
|
4
|
-
"sourcesContent": ["/* eslint-disable @typescript-eslint/no-explicit-any */\n/**\n * Plugin runtime SDK (`useRb()`).\n *\n * pub/sub/action/serve talk to the Zenoh router over WebSocket. Topics are\n * namespaced to {pluginId}/... ; a leading \"/\" makes the path absolute.\n * alert/confirm/prompt/toast delegate to the host.\n */\nimport { createContext, useContext, useEffect, useRef } from \"react\";\nimport type {\n WsSubscriber as Subscriber,\n WsQueryable as Queryable,\n} from \"./ws\";\n// Type-only import \u2014 no runtime coupling to host-ui.ts (which itself has no\n// dependency back on sdk.ts, so this stays one-directional).\nimport type { PluginUIServices } from \"./host-ui\";\n\nexport type RbNotifyType = \"info\" | \"success\" | \"warning\" | \"error\";\n\nexport interface RbSdk {\n /**\n * Publish JSON payload to a topic.\n * \"sensor/data\" \u2192 {pluginId}/sensor/data\n * \"/broadcast/x\" \u2192 broadcast/x (absolute)\n */\n pub: <P = any>(topic: string, payload: P) => Promise<void>;\n\n /** Subscribe to a topic. Returns a handle with undeclare(). */\n sub: <P = any>(\n topic: string,\n handler: (payload: P) => void,\n ) => Promise<Subscriber>;\n\n /**\n * Send a request and await the first reply (Zenoh query).\n * Use this to call a backend handler registered with rb.serve() / ZenohRouter.\n */\n action: <R = any, P = any>(topic: string, payload?: P) => Promise<R>;\n\n /**\n * Register a queryable \u2014 the frontend responds to backend queries.\n * Mirror of rb.action() on the other side.\n */\n serve: <P = any, R = any>(\n topic: string,\n handler: (payload?: P) => R | Promise<R>,\n ) => Promise<Queryable>;\n\n /** Show a transient notification through the host's toast system. */\n toast: PluginUIServices[\"toast\"];\n\n /** Show a host alert dialog. */\n alert: PluginUIServices[\"alert\"];\n /** Show a host confirm dialog. Resolves true/false. */\n confirm: PluginUIServices[\"confirm\"];\n /** Show a host prompt dialog. Resolves the entered value, or undefined if cancelled. */\n prompt: PluginUIServices[\"prompt\"];\n\n /**\n * Host-provided initial data for this mount \u2014 e.g. the previously-saved\n * values when the host re-opens a form. `undefined` for a fresh mount.\n * Prefer the `useRbFormData()` hook over reading this directly.\n */\n data?: unknown;\n\n /**\n * Register a getter the host calls (synchronously) to collect this plugin's\n * save payload \u2014 e.g. when the host's Save button is clicked. The returned\n * value must be JSON-serialisable (the host persists it). Returns an\n * unregister fn. Prefer the `useRbSave()` hook.\n */\n onSave: (getPayload: () => unknown) => () => void;\n}\n\nconst noopSdk: RbSdk = {\n pub: async <P = any>(_topic?: string, _payload?: P) => {\n console.warn(\"[plugin-ui] rb.pub called outside of a host context\");\n },\n sub: async <P = any>(_topic?: string, _handler?: (payload: P) => void) => {\n console.warn(\"[plugin-ui] rb.sub called outside of a host context\");\n return { undeclare: async () => {} } as unknown as Subscriber;\n },\n action: async <R = any, P = any>(_topic?: string, _payload?: P) => {\n console.warn(\"[plugin-ui] rb.action called outside of a host context\");\n return undefined as R;\n },\n serve: async <P = any, R = any>(\n _topic?: string,\n _handler?: (payload?: P) => R,\n ) => {\n console.warn(\"[plugin-ui] rb.serve called outside of a host context\");\n return { undeclare: async () => {} } as unknown as Queryable;\n },\n toast: (params) => {\n console.warn(\"[plugin-ui] rb.toast (no host):\", params);\n },\n alert: async (_) => {\n console.warn(\"[plugin-ui] rb.alert called outside of a host context\");\n },\n confirm: async (_) => {\n console.warn(\"[plugin-ui] rb.confirm called outside of a host context\");\n return false;\n },\n prompt: async (_) => {\n console.warn(\"[plugin-ui] rb.prompt called outside of a host context\");\n return undefined;\n },\n data: undefined,\n onSave: () => () => {},\n};\n\nexport const RbContext = createContext<RbSdk>(noopSdk);\n\n/** Access the host SDK from within a plugin component. */\nexport function useRb(): RbSdk {\n return useContext(RbContext);\n}\n\n/**\n * Read the host-provided initial data for this mount (e.g. previously-saved\n * form values). `undefined` for a fresh mount.\n *\n * const saved = useRbFormData<MyArgs>();\n * const methods = useForm({ defaultValues: saved ?? DEFAULTS });\n */\nexport function useRbFormData<T = unknown>(): T | undefined {\n return useRb().data as T | undefined;\n}\n\n/**\n * Register a getter the host calls to collect this plugin's save payload when\n * the host's Save is clicked. The latest `getPayload` is always used, so it can\n * close over current state. The returned value must be JSON-serialisable.\n *\n * useRbSave(() => ({ args: getValues(), summary: buildSummary() }));\n */\nexport function useRbSave<T = unknown>(getPayload: () => T): void {\n const rb = useRb();\n const ref = useRef(getPayload);\n ref.current = getPayload;\n useEffect(() => rb.onSave(() => ref.current()), [rb]);\n}\n"],
|
|
5
|
-
"mappings": "AAQA,SAAS,eAAe,YAAY,WAAW,cAAc;
|
|
4
|
+
"sourcesContent": ["/* eslint-disable @typescript-eslint/no-explicit-any */\n/**\n * Plugin runtime SDK (`useRb()`).\n *\n * pub/sub/action/serve talk to the Zenoh router over WebSocket. Topics are\n * namespaced to {pluginId}/... ; a leading \"/\" makes the path absolute.\n * alert/confirm/prompt/toast delegate to the host.\n */\nimport { createContext, useContext, useEffect, useRef } from \"react\";\nimport type {\n WsSubscriber as Subscriber,\n WsQueryable as Queryable,\n} from \"./ws\";\n// Type-only import \u2014 no runtime coupling to host-ui.ts (which itself has no\n// dependency back on sdk.ts, so this stays one-directional).\nimport type { PluginUIServices } from \"./host-ui\";\n\nexport type RbNotifyType = \"info\" | \"success\" | \"warning\" | \"error\";\n\nexport interface RbSdk {\n /**\n * Publish JSON payload to a topic.\n * \"sensor/data\" \u2192 {pluginId}/sensor/data\n * \"/broadcast/x\" \u2192 broadcast/x (absolute)\n */\n pub: <P = any>(topic: string, payload: P) => Promise<void>;\n\n /** Subscribe to a topic. Returns a handle with undeclare(). */\n sub: <P = any>(\n topic: string,\n handler: (payload: P) => void,\n ) => Promise<Subscriber>;\n\n /**\n * Send a request and await the first reply (Zenoh query).\n * Use this to call a backend handler registered with rb.serve() / ZenohRouter.\n */\n action: <R = any, P = any>(topic: string, payload?: P) => Promise<R>;\n\n /**\n * Register a queryable \u2014 the frontend responds to backend queries.\n * Mirror of rb.action() on the other side.\n */\n serve: <P = any, R = any>(\n topic: string,\n handler: (payload?: P) => R | Promise<R>,\n ) => Promise<Queryable>;\n\n /** Show a transient notification through the host's toast system. */\n toast: PluginUIServices[\"toast\"];\n\n /** Show a host alert dialog. */\n alert: PluginUIServices[\"alert\"];\n /** Show a host confirm dialog. Resolves true/false. */\n confirm: PluginUIServices[\"confirm\"];\n /** Show a host prompt dialog. Resolves the entered value, or undefined if cancelled. */\n prompt: PluginUIServices[\"prompt\"];\n\n /**\n * Host-provided initial data for this mount \u2014 e.g. the previously-saved\n * values when the host re-opens a form. `undefined` for a fresh mount.\n * Prefer the `useRbFormData()` hook over reading this directly.\n */\n data?: unknown;\n\n /**\n * Register a getter the host calls (synchronously) to collect this plugin's\n * save payload \u2014 e.g. when the host's Save button is clicked. The returned\n * value must be JSON-serialisable (the host persists it). Returns an\n * unregister fn. Prefer the `useRbSave()` hook.\n */\n onSave: (getPayload: () => unknown) => () => void;\n\n /**\n * PCs allowed to talk to this plugin's ROS 2 nodes. ROS discovery is robot-local\n * (ROS_AUTOMATIC_DISCOVERY_RANGE=LOCALHOST); Muscat keeps this list per plugin, starts\n * the plugin with ROS_STATIC_PEERS set to it, and restarts the plugin when it changes.\n */\n rosPeers: {\n get: () => Promise<string[]>;\n /** Saves the list and restarts the plugin backend. Rejects on an invalid IP. */\n set: (peers: string[]) => Promise<string[]>;\n };\n}\n\nconst noopSdk: RbSdk = {\n pub: async <P = any>(_topic?: string, _payload?: P) => {\n console.warn(\"[plugin-ui] rb.pub called outside of a host context\");\n },\n sub: async <P = any>(_topic?: string, _handler?: (payload: P) => void) => {\n console.warn(\"[plugin-ui] rb.sub called outside of a host context\");\n return { undeclare: async () => {} } as unknown as Subscriber;\n },\n action: async <R = any, P = any>(_topic?: string, _payload?: P) => {\n console.warn(\"[plugin-ui] rb.action called outside of a host context\");\n return undefined as R;\n },\n serve: async <P = any, R = any>(\n _topic?: string,\n _handler?: (payload?: P) => R,\n ) => {\n console.warn(\"[plugin-ui] rb.serve called outside of a host context\");\n return { undeclare: async () => {} } as unknown as Queryable;\n },\n toast: (params) => {\n console.warn(\"[plugin-ui] rb.toast (no host):\", params);\n },\n alert: async (_) => {\n console.warn(\"[plugin-ui] rb.alert called outside of a host context\");\n },\n confirm: async (_) => {\n console.warn(\"[plugin-ui] rb.confirm called outside of a host context\");\n return false;\n },\n prompt: async (_) => {\n console.warn(\"[plugin-ui] rb.prompt called outside of a host context\");\n return undefined;\n },\n data: undefined,\n onSave: () => () => {},\n rosPeers: {\n get: async () => {\n console.warn(\"[plugin-ui] rb.rosPeers called outside of a host context\");\n return [];\n },\n set: async () => {\n console.warn(\"[plugin-ui] rb.rosPeers called outside of a host context\");\n return [];\n },\n },\n};\n\nexport const RbContext = createContext<RbSdk>(noopSdk);\n\n/** Access the host SDK from within a plugin component. */\nexport function useRb(): RbSdk {\n return useContext(RbContext);\n}\n\n/**\n * Read the host-provided initial data for this mount (e.g. previously-saved\n * form values). `undefined` for a fresh mount.\n *\n * const saved = useRbFormData<MyArgs>();\n * const methods = useForm({ defaultValues: saved ?? DEFAULTS });\n */\nexport function useRbFormData<T = unknown>(): T | undefined {\n return useRb().data as T | undefined;\n}\n\n/**\n * Register a getter the host calls to collect this plugin's save payload when\n * the host's Save is clicked. The latest `getPayload` is always used, so it can\n * close over current state. The returned value must be JSON-serialisable.\n *\n * useRbSave(() => ({ args: getValues(), summary: buildSummary() }));\n */\nexport function useRbSave<T = unknown>(getPayload: () => T): void {\n const rb = useRb();\n const ref = useRef(getPayload);\n ref.current = getPayload;\n useEffect(() => rb.onSave(() => ref.current()), [rb]);\n}\n"],
|
|
5
|
+
"mappings": "AAQA,SAAS,eAAe,YAAY,WAAW,cAAc;AA6E7D,MAAM,UAAiB;AAAA,EACrB,KAAK,OAAgB,QAAiB,aAAiB;AACrD,YAAQ,KAAK,qDAAqD;AAAA,EACpE;AAAA,EACA,KAAK,OAAgB,QAAiB,aAAoC;AACxE,YAAQ,KAAK,qDAAqD;AAClE,WAAO,EAAE,WAAW,YAAY;AAAA,IAAC,EAAE;AAAA,EACrC;AAAA,EACA,QAAQ,OAAyB,QAAiB,aAAiB;AACjE,YAAQ,KAAK,wDAAwD;AACrE,WAAO;AAAA,EACT;AAAA,EACA,OAAO,OACL,QACA,aACG;AACH,YAAQ,KAAK,uDAAuD;AACpE,WAAO,EAAE,WAAW,YAAY;AAAA,IAAC,EAAE;AAAA,EACrC;AAAA,EACA,OAAO,CAAC,WAAW;AACjB,YAAQ,KAAK,mCAAmC,MAAM;AAAA,EACxD;AAAA,EACA,OAAO,OAAO,MAAM;AAClB,YAAQ,KAAK,uDAAuD;AAAA,EACtE;AAAA,EACA,SAAS,OAAO,MAAM;AACpB,YAAQ,KAAK,yDAAyD;AACtE,WAAO;AAAA,EACT;AAAA,EACA,QAAQ,OAAO,MAAM;AACnB,YAAQ,KAAK,wDAAwD;AACrE,WAAO;AAAA,EACT;AAAA,EACA,MAAM;AAAA,EACN,QAAQ,MAAM,MAAM;AAAA,EAAC;AAAA,EACrB,UAAU;AAAA,IACR,KAAK,YAAY;AACf,cAAQ,KAAK,0DAA0D;AACvE,aAAO,CAAC;AAAA,IACV;AAAA,IACA,KAAK,YAAY;AACf,cAAQ,KAAK,0DAA0D;AACvE,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AACF;AAEO,MAAM,YAAY,cAAqB,OAAO;AAG9C,SAAS,QAAe;AAC7B,SAAO,WAAW,SAAS;AAC7B;AASO,SAAS,gBAA4C;AAC1D,SAAO,MAAM,EAAE;AACjB;AASO,SAAS,UAAuB,YAA2B;AAChE,QAAM,KAAK,MAAM;AACjB,QAAM,MAAM,OAAO,UAAU;AAC7B,MAAI,UAAU;AACd,YAAU,MAAM,GAAG,OAAO,MAAM,IAAI,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;AACtD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/ws.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export declare class RbWs {
|
|
|
10
10
|
private readonly _subHandlers;
|
|
11
11
|
private readonly _serveHandlers;
|
|
12
12
|
private constructor();
|
|
13
|
+
get pluginId(): string;
|
|
13
14
|
static connect(url: string, pluginId: string): Promise<RbWs>;
|
|
14
15
|
private _key;
|
|
15
16
|
pub<P = any>(topic: string, payload: P): Promise<void>;
|
|
@@ -69,5 +70,9 @@ export declare function createLazyRbSdk(wsPromise: Promise<RbWs>, services: Plug
|
|
|
69
70
|
}) => Promise<string | undefined>;
|
|
70
71
|
data: unknown;
|
|
71
72
|
onSave: (getPayload: () => unknown) => () => void;
|
|
73
|
+
rosPeers: {
|
|
74
|
+
get: () => Promise<string[]>;
|
|
75
|
+
set: (peers: string[]) => Promise<string[]>;
|
|
76
|
+
};
|
|
72
77
|
};
|
|
73
78
|
//# sourceMappingURL=ws.d.ts.map
|
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;
|
|
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;IAsCP,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,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;;aArNtB,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;;;;;;yBAsMuB,MAAM,OAAO,KAAK,MAAM,IAAI;;mBAqCnC,OAAO,CAAC,MAAM,EAAE,CAAC;qBACb,MAAM,EAAE,KAAG,OAAO,CAAC,MAAM,EAAE,CAAC;;EAPlD"}
|
package/dist/ws.js
CHANGED
|
@@ -10,6 +10,10 @@ class RbWs {
|
|
|
10
10
|
constructor(socket, pluginId) {
|
|
11
11
|
this._socket = socket;
|
|
12
12
|
this._pluginId = pluginId;
|
|
13
|
+
socket.io.on("reconnect", () => {
|
|
14
|
+
for (const topic of this._subHandlers.keys()) socket.emit("sub", { topic });
|
|
15
|
+
for (const topic of this._serveHandlers.keys()) socket.emit("serve_register", { topic });
|
|
16
|
+
});
|
|
13
17
|
socket.on(
|
|
14
18
|
"msg",
|
|
15
19
|
({ topic, payload }) => {
|
|
@@ -29,6 +33,9 @@ class RbWs {
|
|
|
29
33
|
}
|
|
30
34
|
);
|
|
31
35
|
}
|
|
36
|
+
get pluginId() {
|
|
37
|
+
return this._pluginId;
|
|
38
|
+
}
|
|
32
39
|
static connect(url, pluginId) {
|
|
33
40
|
return new Promise((resolve, reject) => {
|
|
34
41
|
const socket = io(url, { transports: ["websocket"] });
|
|
@@ -131,9 +138,23 @@ function createLazyRbSdk(wsPromise, services, mount) {
|
|
|
131
138
|
prompt: services.prompt,
|
|
132
139
|
data: mount?.data,
|
|
133
140
|
onSave: mount?.onSave ?? (() => () => {
|
|
141
|
+
}),
|
|
142
|
+
rosPeers: rosPeersApi(async (op, payload) => {
|
|
143
|
+
const ws = await wsPromise;
|
|
144
|
+
return ws.action(`/muscat/plugin/${ws.pluginId}/ros_peers/${op}`, payload);
|
|
134
145
|
})
|
|
135
146
|
};
|
|
136
147
|
}
|
|
148
|
+
function rosPeersApi(call) {
|
|
149
|
+
return {
|
|
150
|
+
get: async () => (await call("get"))?.peers ?? [],
|
|
151
|
+
set: async (peers) => {
|
|
152
|
+
const r = await call("set", { peers });
|
|
153
|
+
if (!r?.ok) throw new Error(r?.error ?? "rb.rosPeers.set failed");
|
|
154
|
+
return r.peers;
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
}
|
|
137
158
|
export {
|
|
138
159
|
RbWs,
|
|
139
160
|
createLazyRbSdk
|
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 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": "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;
|
|
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 // The gateway keeps subscriptions/serve registrations in memory \u2014 re-send them after\n // a reconnect (e.g. the gateway restarted), or they silently stop working.\n socket.io.on(\"reconnect\", () => {\n for (const topic of this._subHandlers.keys()) socket.emit(\"sub\", { topic });\n for (const topic of this._serveHandlers.keys()) socket.emit(\"serve_register\", { topic });\n });\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 get pluginId(): string {\n return this._pluginId;\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 rosPeers: rosPeersApi(async (op, payload) => {\n const ws = await wsPromise;\n return ws.action(`/muscat/plugin/${ws.pluginId}/ros_peers/${op}`, payload);\n }),\n };\n}\n\n// Answered by Muscat (common) on the robot and by `muscat-pk dev` locally.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction rosPeersApi(call: (op: \"get\" | \"set\", payload?: any) => Promise<any>) {\n return {\n get: async (): Promise<string[]> => (await call(\"get\"))?.peers ?? [],\n set: async (peers: string[]): Promise<string[]> => {\n const r = await call(\"set\", { peers });\n if (!r?.ok) throw new Error(r?.error ?? \"rb.rosPeers.set failed\");\n return r.peers;\n },\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": "1.3.
|
|
3
|
+
"version": "1.3.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=20"
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"build": "node build-dist.mjs",
|
|
48
48
|
"build-plugin-ui": "node build-dist.mjs",
|
|
49
49
|
"lint": "node ../../node_modules/eslint/bin/eslint.js .",
|
|
50
|
-
"check-types": "tsc --noEmit"
|
|
50
|
+
"check-types": "tsc --noEmit -p tsconfig.check.json"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@repo/eslint-config": "*",
|
package/src/manifest.ts
CHANGED
|
@@ -119,6 +119,8 @@ export function validateManifest(value: unknown): string[] {
|
|
|
119
119
|
const m = value as Record<string, unknown>;
|
|
120
120
|
if (typeof m.id !== "string" || m.id.length === 0)
|
|
121
121
|
errors.push("manifest.id is required (string)");
|
|
122
|
+
else if (!/^[a-z][a-z0-9]*(-[a-z0-9]+)*(\.[a-z][a-z0-9]*(-[a-z0-9]+)*)*$/.test(m.id))
|
|
123
|
+
errors.push('manifest.id must be "."-separated parts of lowercase letters/digits and single "-", each starting with a letter (e.g. com.example.my-plugin)');
|
|
122
124
|
if (typeof m.name !== "string" || m.name.length === 0)
|
|
123
125
|
errors.push("manifest.name is required (string)");
|
|
124
126
|
if (typeof m.version !== "string" || m.version.length === 0)
|
package/src/sdk.ts
CHANGED
|
@@ -70,6 +70,17 @@ export interface RbSdk {
|
|
|
70
70
|
* unregister fn. Prefer the `useRbSave()` hook.
|
|
71
71
|
*/
|
|
72
72
|
onSave: (getPayload: () => unknown) => () => void;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* PCs allowed to talk to this plugin's ROS 2 nodes. ROS discovery is robot-local
|
|
76
|
+
* (ROS_AUTOMATIC_DISCOVERY_RANGE=LOCALHOST); Muscat keeps this list per plugin, starts
|
|
77
|
+
* the plugin with ROS_STATIC_PEERS set to it, and restarts the plugin when it changes.
|
|
78
|
+
*/
|
|
79
|
+
rosPeers: {
|
|
80
|
+
get: () => Promise<string[]>;
|
|
81
|
+
/** Saves the list and restarts the plugin backend. Rejects on an invalid IP. */
|
|
82
|
+
set: (peers: string[]) => Promise<string[]>;
|
|
83
|
+
};
|
|
73
84
|
}
|
|
74
85
|
|
|
75
86
|
const noopSdk: RbSdk = {
|
|
@@ -107,6 +118,16 @@ const noopSdk: RbSdk = {
|
|
|
107
118
|
},
|
|
108
119
|
data: undefined,
|
|
109
120
|
onSave: () => () => {},
|
|
121
|
+
rosPeers: {
|
|
122
|
+
get: async () => {
|
|
123
|
+
console.warn("[plugin-ui] rb.rosPeers called outside of a host context");
|
|
124
|
+
return [];
|
|
125
|
+
},
|
|
126
|
+
set: async () => {
|
|
127
|
+
console.warn("[plugin-ui] rb.rosPeers called outside of a host context");
|
|
128
|
+
return [];
|
|
129
|
+
},
|
|
130
|
+
},
|
|
110
131
|
};
|
|
111
132
|
|
|
112
133
|
export const RbContext = createContext<RbSdk>(noopSdk);
|
package/src/ws.ts
CHANGED
|
@@ -40,6 +40,13 @@ export class RbWs {
|
|
|
40
40
|
this._socket = socket;
|
|
41
41
|
this._pluginId = pluginId;
|
|
42
42
|
|
|
43
|
+
// The gateway keeps subscriptions/serve registrations in memory — re-send them after
|
|
44
|
+
// a reconnect (e.g. the gateway restarted), or they silently stop working.
|
|
45
|
+
socket.io.on("reconnect", () => {
|
|
46
|
+
for (const topic of this._subHandlers.keys()) socket.emit("sub", { topic });
|
|
47
|
+
for (const topic of this._serveHandlers.keys()) socket.emit("serve_register", { topic });
|
|
48
|
+
});
|
|
49
|
+
|
|
43
50
|
// Deliver incoming pub/sub messages to registered handlers
|
|
44
51
|
socket.on(
|
|
45
52
|
"msg",
|
|
@@ -67,6 +74,10 @@ export class RbWs {
|
|
|
67
74
|
);
|
|
68
75
|
}
|
|
69
76
|
|
|
77
|
+
get pluginId(): string {
|
|
78
|
+
return this._pluginId;
|
|
79
|
+
}
|
|
80
|
+
|
|
70
81
|
static connect(url: string, pluginId: string): Promise<RbWs> {
|
|
71
82
|
return new Promise((resolve, reject) => {
|
|
72
83
|
const socket = io(url, { transports: ["websocket"] });
|
|
@@ -223,5 +234,22 @@ export function createLazyRbSdk(
|
|
|
223
234
|
prompt: services.prompt,
|
|
224
235
|
data: mount?.data,
|
|
225
236
|
onSave: mount?.onSave ?? (() => () => {}),
|
|
237
|
+
rosPeers: rosPeersApi(async (op, payload) => {
|
|
238
|
+
const ws = await wsPromise;
|
|
239
|
+
return ws.action(`/muscat/plugin/${ws.pluginId}/ros_peers/${op}`, payload);
|
|
240
|
+
}),
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Answered by Muscat (common) on the robot and by `muscat-pk dev` locally.
|
|
245
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
246
|
+
function rosPeersApi(call: (op: "get" | "set", payload?: any) => Promise<any>) {
|
|
247
|
+
return {
|
|
248
|
+
get: async (): Promise<string[]> => (await call("get"))?.peers ?? [],
|
|
249
|
+
set: async (peers: string[]): Promise<string[]> => {
|
|
250
|
+
const r = await call("set", { peers });
|
|
251
|
+
if (!r?.ok) throw new Error(r?.error ?? "rb.rosPeers.set failed");
|
|
252
|
+
return r.peers;
|
|
253
|
+
},
|
|
226
254
|
};
|
|
227
255
|
}
|