@rainbow-robotics/muscat-plugin-kit 0.1.2-dev.9 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. package/README.md +113 -49
  2. package/cli/README.md +191 -5
  3. package/cli/bin/create-muscat-plugin.mjs +1 -1
  4. package/cli/bin/muscat-pk.mjs +25 -14
  5. package/cli/bundle-templates.mjs +28 -0
  6. package/cli/docker/Dockerfile.bridge +17 -0
  7. package/cli/docker/Dockerfile.cpp +22 -0
  8. package/cli/docker/Dockerfile.dev +1 -0
  9. package/cli/docker/Dockerfile.python +20 -0
  10. package/cli/docker/bridge-package.json +8 -0
  11. package/cli/prepack.mjs +10 -0
  12. package/cli/src/__pycache__/host_agent.cpython-37.pyc +0 -0
  13. package/cli/src/bridge.mjs +95 -0
  14. package/cli/src/build.mjs +345 -85
  15. package/cli/src/check-package.mjs +8 -23
  16. package/cli/src/cpp.mjs +61 -0
  17. package/cli/src/dev.mjs +156 -4
  18. package/cli/src/devBack.mjs +505 -57
  19. package/cli/src/devUi.mjs +167 -22
  20. package/cli/src/esbuild.mjs +27 -10
  21. package/cli/src/host_agent.py +162 -0
  22. package/cli/src/manifest.mjs +208 -0
  23. package/cli/src/mcp.mjs +617 -0
  24. package/cli/src/new.mjs +170 -67
  25. package/cli/src/pluginDevServer.mjs +112 -0
  26. package/cli/src/ports.mjs +160 -0
  27. package/cli/src/python.mjs +109 -0
  28. package/cli/src/ros2.mjs +43 -18
  29. package/cli/src/sdk.mjs +11 -9
  30. package/cli/src/socketBridge.mjs +133 -0
  31. package/cli/src/storybook.mjs +168 -0
  32. package/cli/src/type.mjs +52 -29
  33. package/cli/src/uninstall.mjs +98 -0
  34. package/cli/src/update.mjs +259 -30
  35. package/cli/src/util.mjs +123 -9
  36. package/cli/src/zenohDev.mjs +75 -0
  37. package/cli/storybook-dist/_design-tokens.css +4957 -0
  38. package/cli/storybook-dist/_stories-src/NxAvatar.stories.tsx +27 -0
  39. package/cli/storybook-dist/_stories-src/NxPagination.stories.tsx +28 -0
  40. package/cli/storybook-dist/_stories-src/RBAccordion/RBAccordion.stories.tsx +46 -0
  41. package/cli/storybook-dist/_stories-src/RBAccordion/RBAccordionGroup.stories.tsx +73 -0
  42. package/cli/storybook-dist/_stories-src/RBBadge/RBBadge.stories.tsx +93 -0
  43. package/cli/storybook-dist/_stories-src/RBBattery/RBBattery.stories.tsx +74 -0
  44. package/cli/storybook-dist/_stories-src/RBButton/RBCheckIconButton.stories.tsx +28 -0
  45. package/cli/storybook-dist/_stories-src/RBButton/RBCheckLabelButton.stories.tsx +84 -0
  46. package/cli/storybook-dist/_stories-src/RBButton/RBIconButton.stories.tsx +57 -0
  47. package/cli/storybook-dist/_stories-src/RBButton/RBInlineLabelButton.stories.tsx +41 -0
  48. package/cli/storybook-dist/_stories-src/RBButton/RBLabelButton.stories.tsx +60 -0
  49. package/cli/storybook-dist/_stories-src/RBDatePicker/RBDateInput.stories.tsx +44 -0
  50. package/cli/storybook-dist/_stories-src/RBDatePicker/RBRangeDateInput.stories.tsx +44 -0
  51. package/cli/storybook-dist/_stories-src/RBDialog/RBAlert.stories.tsx +51 -0
  52. package/cli/storybook-dist/_stories-src/RBDialog/RBConfirm.stories.tsx +55 -0
  53. package/cli/storybook-dist/_stories-src/RBDialog/RBPrompt.stories.tsx +52 -0
  54. package/cli/storybook-dist/_stories-src/RBDivider/RBDivider.stories.tsx +93 -0
  55. package/cli/storybook-dist/_stories-src/RBEmptyView/RBEmptyView.stories.tsx +49 -0
  56. package/cli/storybook-dist/_stories-src/RBIcons/RBColorIcons.stories.tsx +108 -0
  57. package/cli/storybook-dist/_stories-src/RBIcons/RBIcons.stories.tsx +106 -0
  58. package/cli/storybook-dist/_stories-src/RBIcons/RBMenuTreeIcons.stories.tsx +108 -0
  59. package/cli/storybook-dist/_stories-src/RBIcons/RBProgramIcons.stories.tsx +116 -0
  60. package/cli/storybook-dist/_stories-src/RBInput/RBCheckBox.stories.tsx +89 -0
  61. package/cli/storybook-dist/_stories-src/RBInput/RBMultiRangeInput.stories.tsx +64 -0
  62. package/cli/storybook-dist/_stories-src/RBInput/RBRadioBox.stories.tsx +65 -0
  63. package/cli/storybook-dist/_stories-src/RBInput/RBRadioButton.stories.tsx +68 -0
  64. package/cli/storybook-dist/_stories-src/RBInput/RBSelectBox.stories.tsx +119 -0
  65. package/cli/storybook-dist/_stories-src/RBInput/RBSideNumberInput.stories.tsx +90 -0
  66. package/cli/storybook-dist/_stories-src/RBInput/RBText/RBTextArea.stories.tsx +45 -0
  67. package/cli/storybook-dist/_stories-src/RBInput/RBText/RBTextInput.stories.tsx +50 -0
  68. package/cli/storybook-dist/_stories-src/RBInput/RBTextField.stories.tsx +102 -0
  69. package/cli/storybook-dist/_stories-src/RBJoyStick/RBJoyStick.stories.tsx +31 -0
  70. package/cli/storybook-dist/_stories-src/RBLight/RBLight.stories.tsx +36 -0
  71. package/cli/storybook-dist/_stories-src/RBPagination/RBPagination.stories.tsx +49 -0
  72. package/cli/storybook-dist/_stories-src/RBShadcn/RBCard/RBCard.stories.tsx +89 -0
  73. package/cli/storybook-dist/_stories-src/RBShadcn/RBCommand/RBCommand.stories.tsx +116 -0
  74. package/cli/storybook-dist/_stories-src/RBShadcn/RBDialog/RBDialog.stories.tsx +74 -0
  75. package/cli/storybook-dist/_stories-src/RBShadcn/RBDrawer/RBDrawer.stories.tsx +99 -0
  76. package/cli/storybook-dist/_stories-src/RBShadcn/RBDropdownMenu/RBDropdownMenu.stories.tsx +123 -0
  77. package/cli/storybook-dist/_stories-src/RBShadcn/RBMenubar/RBMenubar.stories.tsx +135 -0
  78. package/cli/storybook-dist/_stories-src/RBShadcn/RBPopover/RBPopover.stories.tsx +69 -0
  79. package/cli/storybook-dist/_stories-src/RBShadcn/RBResizable/RBResizable.stories.tsx +49 -0
  80. package/cli/storybook-dist/_stories-src/RBShadcn/RBSheet/RBSheet.stories.tsx +91 -0
  81. package/cli/storybook-dist/_stories-src/RBShadcn/RBSlider/RBSlider.stories.tsx +38 -0
  82. package/cli/storybook-dist/_stories-src/RBShadcn/RBSwitch/RBSwitch.stories.tsx +112 -0
  83. package/cli/storybook-dist/_stories-src/RBShadcn/RBTabs/RBTabs.stories.tsx +75 -0
  84. package/cli/storybook-dist/_stories-src/RBShadcn/RBToggle/RBToggle.stories.tsx +51 -0
  85. package/cli/storybook-dist/_stories-src/RBShadcn/RBToggleGroup/RBToggleGroup.stories.tsx +84 -0
  86. package/cli/storybook-dist/_stories-src/RBTab/RBChipTab.stories.tsx +102 -0
  87. package/cli/storybook-dist/_stories-src/RBTab/RBChipsBoxTab.stories.tsx +78 -0
  88. package/cli/storybook-dist/_stories-src/RBTab/RBTab.stories.tsx +57 -0
  89. package/cli/storybook-dist/_stories-src/RBTable/RBTable.stories.tsx +177 -0
  90. package/cli/storybook-dist/_stories-src/RBTable/RBTableRow.stories.tsx +64 -0
  91. package/cli/storybook-dist/_stories-src/RBToast/RBToast.stories.tsx +117 -0
  92. package/cli/storybook-dist/_stories-src/RBTooltip/RBTooltip.stories.tsx +50 -0
  93. package/cli/storybook-dist/_stories-src/loading/RBSpinner.stories.tsx +30 -0
  94. package/cli/storybook-dist/_stories-src/nx-button/NxButton.stories.tsx +54 -0
  95. package/cli/storybook-dist/_stories-src/nx-button/NxPointerHoldButton.stories.tsx +57 -0
  96. package/cli/storybook-dist/_stories-src/nx-icons/NxIcons.stories.tsx +48 -0
  97. package/cli/storybook-dist/_stories-src/nx-inputs/NxNumberInput.stories.tsx +103 -0
  98. package/cli/storybook-dist/_stories-src/nx-inputs/NxRangeInput.stories.tsx +27 -0
  99. package/cli/storybook-dist/_stories-src/nx-inputs/NxSelectBox.stories.tsx +67 -0
  100. package/cli/storybook-dist/_stories-src/nx-inputs/NxSlideRange.stories.tsx +45 -0
  101. package/cli/storybook-dist/_stories-src/nx-inputs/NxSwitch.stories.tsx +27 -0
  102. package/cli/storybook-dist/_stories-src/nx-inputs/NxTextArea.stories.tsx +48 -0
  103. package/cli/storybook-dist/_stories-src/nx-inputs/NxTextInput.stories.tsx +46 -0
  104. package/cli/storybook-dist/assets/Color-YHDXOIA2-ttZJDaKn.js +1 -0
  105. package/cli/storybook-dist/assets/Docs-B1jnlXHd.js +12 -0
  106. package/cli/storybook-dist/assets/Docs-B7dRsHh7.js +8 -0
  107. package/cli/storybook-dist/assets/Docs-C68t3mnn.js +20 -0
  108. package/cli/storybook-dist/assets/Docs-D7rPMMhK.js +7 -0
  109. package/cli/storybook-dist/assets/Docs-DM98U-ZG.js +7 -0
  110. package/cli/storybook-dist/assets/Docs-guWsANnn.js +8 -0
  111. package/cli/storybook-dist/assets/DocsRenderer-CFRXHY34-t12O9QfY.js +2 -0
  112. package/cli/storybook-dist/assets/NxAvatar.stories-ikdLTtys.js +13 -0
  113. package/cli/storybook-dist/assets/NxButton.stories-DL3S2KEV.js +20 -0
  114. package/cli/storybook-dist/assets/NxIcons.stories-Ca7qpAaO.js +3 -0
  115. package/cli/storybook-dist/assets/NxNumberInput.stories-D6NVMTos.js +26 -0
  116. package/cli/storybook-dist/assets/NxPagination.stories-BDKSlHiQ.js +11 -0
  117. package/cli/storybook-dist/assets/NxPointerHoldButton.stories-BI544zqB.js +14 -0
  118. package/cli/storybook-dist/assets/NxRangeInput.stories-BigBBkE7.js +17 -0
  119. package/cli/storybook-dist/assets/NxSelectBox.stories-Ci0IyUWd.js +17 -0
  120. package/cli/storybook-dist/assets/NxSlideRange.stories-sjnPAjFy.js +14 -0
  121. package/cli/storybook-dist/assets/NxSwitch.stories-CRxPDlJm.js +11 -0
  122. package/cli/storybook-dist/assets/NxTextArea.stories-Jh09n24O.js +11 -0
  123. package/cli/storybook-dist/assets/NxTextInput.stories-BeGCjxES.js +11 -0
  124. package/cli/storybook-dist/assets/PretendardVariable-CJuje-Rk.woff2 +0 -0
  125. package/cli/storybook-dist/assets/RBAccordion.stories-CnC95OwM.js +17 -0
  126. package/cli/storybook-dist/assets/RBAccordionGroup.stories-og-6ePox.js +33 -0
  127. package/cli/storybook-dist/assets/RBAlert.stories-BcE9-UOT.js +17 -0
  128. package/cli/storybook-dist/assets/RBBadge.stories-DYZMXb-e.js +1 -0
  129. package/cli/storybook-dist/assets/RBBattery.stories-B7YAUvlM.js +10 -0
  130. package/cli/storybook-dist/assets/RBCard.stories-DTL_7n6I.js +45 -0
  131. package/cli/storybook-dist/assets/RBCheckBox.stories-D9KC6hTg.js +1 -0
  132. package/cli/storybook-dist/assets/RBCheckIconButton.stories-BR8aIkOC.js +1 -0
  133. package/cli/storybook-dist/assets/RBCheckLabelButton.stories-C1ABYhEr.js +24 -0
  134. package/cli/storybook-dist/assets/RBChipTab.stories-Ceo_vnNi.js +21 -0
  135. package/cli/storybook-dist/assets/RBChipsBoxTab.stories-CB_bfK8M.js +1 -0
  136. package/cli/storybook-dist/assets/RBColorIcons.stories-DHZKezld.js +3 -0
  137. package/cli/storybook-dist/assets/RBCommand.stories-DjuCDn5h.js +73 -0
  138. package/cli/storybook-dist/assets/RBConfirm.stories-HMmUPkB1.js +20 -0
  139. package/cli/storybook-dist/assets/RBDateInput.stories-BRfpYtJ4.js +15 -0
  140. package/cli/storybook-dist/assets/RBDialog.stories-Ln23Gyhk.js +38 -0
  141. package/cli/storybook-dist/assets/RBDivider.stories-Bhc_D5Cm.js +1 -0
  142. package/cli/storybook-dist/assets/RBDrawer.stories-CEEhamr_.js +14 -0
  143. package/cli/storybook-dist/assets/RBDropdownMenu.stories-DBvwDCnf.js +86 -0
  144. package/cli/storybook-dist/assets/RBEmptyView.stories-BY5QtXVc.js +12 -0
  145. package/cli/storybook-dist/assets/RBIconButton.stories-DyMFWpIV.js +1 -0
  146. package/cli/storybook-dist/assets/RBIcons.stories-DisU8G0d.js +3 -0
  147. package/cli/storybook-dist/assets/RBInlineLabelButton.stories-Cx0KJqxD.js +1 -0
  148. package/cli/storybook-dist/assets/RBJoyStick.stories-D1THM1Du.js +11 -0
  149. package/cli/storybook-dist/assets/RBLabelButton.stories-BDktlyzc.js +1 -0
  150. package/cli/storybook-dist/assets/RBLight.stories-C5qpnM6J.js +1 -0
  151. package/cli/storybook-dist/assets/RBMenuTreeIcons.stories-DUCeQPKJ.js +3 -0
  152. package/cli/storybook-dist/assets/RBMenubar.stories-ClyM9v37.js +3 -0
  153. package/cli/storybook-dist/assets/RBMultiRangeInput.stories--xSn64ww.js +14 -0
  154. package/cli/storybook-dist/assets/RBPagination.stories-C_uTj83z.js +17 -0
  155. package/cli/storybook-dist/assets/RBPopover.stories-DWmb9Por.js +11 -0
  156. package/cli/storybook-dist/assets/RBProgramIcons.stories-B8ZjEtvw.js +3 -0
  157. package/cli/storybook-dist/assets/RBPrompt.stories-BFKfyYtl.js +20 -0
  158. package/cli/storybook-dist/assets/RBRadioBox.stories-DUz8a27t.js +1 -0
  159. package/cli/storybook-dist/assets/RBRadioButton.stories-DKF61xvC.js +11 -0
  160. package/cli/storybook-dist/assets/RBRangeDateInput.stories-GhMVDyMr.js +15 -0
  161. package/cli/storybook-dist/assets/RBResizable.stories-V0iisR-x.js +20 -0
  162. package/cli/storybook-dist/assets/RBSelectBox.stories-DhvooAcg.js +6 -0
  163. package/cli/storybook-dist/assets/RBSheet.stories-C93Hpmvn.js +39 -0
  164. package/cli/storybook-dist/assets/RBSideNumberInput.stories-Y547mUsB.js +5 -0
  165. package/cli/storybook-dist/assets/RBSlider.stories-BJZyHiLO.js +5 -0
  166. package/cli/storybook-dist/assets/RBSpinner.stories-6wkbu-bU.js +14 -0
  167. package/cli/storybook-dist/assets/RBSwitch.stories-T9InolEP.js +65 -0
  168. package/cli/storybook-dist/assets/RBTab.stories-CjGzZODx.js +1 -0
  169. package/cli/storybook-dist/assets/RBTable.stories-CaolKsVG.js +62 -0
  170. package/cli/storybook-dist/assets/RBTableRow.stories-D13urGfs.js +11 -0
  171. package/cli/storybook-dist/assets/RBTabs.stories-Cv2iIxvP.js +21 -0
  172. package/cli/storybook-dist/assets/RBTextArea.stories-CmrPeit2.js +5 -0
  173. package/cli/storybook-dist/assets/RBTextField.stories-ByW-EKCD.js +45 -0
  174. package/cli/storybook-dist/assets/RBTextInput.stories-DSPlr3uh.js +5 -0
  175. package/cli/storybook-dist/assets/RBToast.stories-DJNZ9tku.js +11 -0
  176. package/cli/storybook-dist/assets/RBToggle.stories-BDQW2WO1.js +12 -0
  177. package/cli/storybook-dist/assets/RBToggleGroup.stories-CjwH6YbI.js +17 -0
  178. package/cli/storybook-dist/assets/RBTooltip.stories-Cp1oL68L.js +9 -0
  179. package/cli/storybook-dist/assets/RainbowUiProvider-BYlbsa69.css +1 -0
  180. package/cli/storybook-dist/assets/RainbowUiProvider-B_piZBLb.js +31 -0
  181. package/cli/storybook-dist/assets/browserUtils-DeopUpDy.js +1 -0
  182. package/cli/storybook-dist/assets/chunk-XP5HYGXS-BHXC9YeQ.js +1 -0
  183. package/cli/storybook-dist/assets/client-DTf7SId2.js +1 -0
  184. package/cli/storybook-dist/assets/createConveyorBeltLayer-769P8deX.js +6257 -0
  185. package/cli/storybook-dist/assets/createConveyorBeltLayer-Ds5AOper.css +21 -0
  186. package/cli/storybook-dist/assets/entry-preview-CNdFC92L.js +2 -0
  187. package/cli/storybook-dist/assets/entry-preview-docs-b8bpVBXy.js +46 -0
  188. package/cli/storybook-dist/assets/iframe-CLUgOMp4.js +211 -0
  189. package/cli/storybook-dist/assets/index-B4FGfHer.js +1 -0
  190. package/cli/storybook-dist/assets/index-BRq5TIsn.js +24 -0
  191. package/cli/storybook-dist/assets/index-C1OzuK6-.js +240 -0
  192. package/cli/storybook-dist/assets/index-CT3U1fyk.js +1 -0
  193. package/cli/storybook-dist/assets/index-ChsGqxH_.js +9 -0
  194. package/cli/storybook-dist/assets/index-DKFBREX_.js +1 -0
  195. package/cli/storybook-dist/assets/index-DgH-xKnr.js +11 -0
  196. package/cli/storybook-dist/assets/index-Dmy2JGbm.js +1 -0
  197. package/cli/storybook-dist/assets/index-DrFu-skq.js +6 -0
  198. package/cli/storybook-dist/assets/index-mT46TdHs.js +575 -0
  199. package/cli/storybook-dist/assets/jsx-runtime-BTJTZTIL.js +9 -0
  200. package/cli/storybook-dist/assets/keymapTileIndex.worker-BsNYNbqs.js +1 -0
  201. package/cli/storybook-dist/assets/preview-B8lJiyuQ.js +34 -0
  202. package/cli/storybook-dist/assets/preview-BBWR9nbA.js +1 -0
  203. package/cli/storybook-dist/assets/preview-BWzBA1C2.js +396 -0
  204. package/cli/storybook-dist/assets/preview-CILRoSnB.js +1 -0
  205. package/cli/storybook-dist/assets/preview-CVgpLj3b.js +1 -0
  206. package/cli/storybook-dist/assets/preview-Com4scch.css +1 -0
  207. package/cli/storybook-dist/assets/preview-CvbIS5ZJ.js +1 -0
  208. package/cli/storybook-dist/assets/preview-CvycU0Br.js +2 -0
  209. package/cli/storybook-dist/assets/preview-CzhIqee1.js +3 -0
  210. package/cli/storybook-dist/assets/preview-DD_OYowb.js +1 -0
  211. package/cli/storybook-dist/assets/preview-DGUiP6tS.js +7 -0
  212. package/cli/storybook-dist/assets/preview-DHQbi4pV.js +1 -0
  213. package/cli/storybook-dist/assets/react-18-hB3smqWJ.js +1 -0
  214. package/cli/storybook-dist/assets/react-resizable-panels.browser-CvAJUcrm.js +72 -0
  215. package/cli/storybook-dist/assets/test-utils-BQudk2xt.js +9 -0
  216. package/cli/storybook-dist/favicon.svg +1 -0
  217. package/cli/storybook-dist/iframe.html +666 -0
  218. package/cli/storybook-dist/index.html +190 -0
  219. package/cli/storybook-dist/index.json +1 -0
  220. package/cli/storybook-dist/nunito-sans-bold-italic.woff2 +0 -0
  221. package/cli/storybook-dist/nunito-sans-bold.woff2 +0 -0
  222. package/cli/storybook-dist/nunito-sans-italic.woff2 +0 -0
  223. package/cli/storybook-dist/nunito-sans-regular.woff2 +0 -0
  224. package/cli/storybook-dist/project.json +1 -0
  225. package/cli/storybook-dist/sb-addons/chromatic-com-storybook-10/manager-bundle.js +331 -0
  226. package/cli/storybook-dist/sb-addons/chromatic-com-storybook-10/manager-bundle.js.LEGAL.txt +51 -0
  227. package/cli/storybook-dist/sb-addons/essentials-actions-3/manager-bundle.js +3 -0
  228. package/cli/storybook-dist/sb-addons/essentials-backgrounds-5/manager-bundle.js +12 -0
  229. package/cli/storybook-dist/sb-addons/essentials-controls-2/manager-bundle.js +405 -0
  230. package/cli/storybook-dist/sb-addons/essentials-docs-4/manager-bundle.js +245 -0
  231. package/cli/storybook-dist/sb-addons/essentials-measure-8/manager-bundle.js +3 -0
  232. package/cli/storybook-dist/sb-addons/essentials-outline-9/manager-bundle.js +3 -0
  233. package/cli/storybook-dist/sb-addons/essentials-toolbars-7/manager-bundle.js +3 -0
  234. package/cli/storybook-dist/sb-addons/essentials-viewport-6/manager-bundle.js +3 -0
  235. package/cli/storybook-dist/sb-addons/links-1/manager-bundle.js +3 -0
  236. package/cli/storybook-dist/sb-addons/storybook-13/manager-bundle.js +3 -0
  237. package/cli/storybook-dist/sb-addons/storybook-core-core-server-presets-0/common-manager-bundle.js +3 -0
  238. package/cli/storybook-dist/sb-addons/storybook-experimental-addon-test-12/manager-bundle.js +223 -0
  239. package/cli/storybook-dist/sb-addons/themes-11/manager-bundle.js +3 -0
  240. package/cli/storybook-dist/sb-common-assets/favicon.svg +1 -0
  241. package/cli/storybook-dist/sb-common-assets/nunito-sans-bold-italic.woff2 +0 -0
  242. package/cli/storybook-dist/sb-common-assets/nunito-sans-bold.woff2 +0 -0
  243. package/cli/storybook-dist/sb-common-assets/nunito-sans-italic.woff2 +0 -0
  244. package/cli/storybook-dist/sb-common-assets/nunito-sans-regular.woff2 +0 -0
  245. package/cli/storybook-dist/sb-manager/globals-module-info.js +1052 -0
  246. package/cli/storybook-dist/sb-manager/globals-runtime.js +41775 -0
  247. package/cli/storybook-dist/sb-manager/globals.js +48 -0
  248. package/cli/storybook-dist/sb-manager/runtime.js +12048 -0
  249. package/cli/templates/common/.mcp.json +8 -0
  250. package/cli/templates/common/README.md +644 -81
  251. package/cli/templates/common/assets/icon.svg +44 -2
  252. package/cli/templates/common/manifest.json +13 -13
  253. package/cli/templates/common/package.json +11 -4
  254. package/cli/templates/common/py/main.py +98 -0
  255. package/cli/templates/common/requirements.txt +4 -3
  256. package/cli/templates/common/tsconfig.json +1 -1
  257. package/cli/templates/common/ui/content.tsx +216 -0
  258. package/cli/templates/common/ui/style.css +6 -0
  259. package/cli/templates/common/vite.config.ts +6 -25
  260. package/cli/templates/cpp/cpp/CMakeLists.txt +14 -0
  261. package/cli/templates/cpp/cpp/main.cpp +26 -0
  262. package/cli/templates/ros2/ros2_ws/src/__PLUGIN_SLUG__/__PLUGIN_SLUG__/node.py +1 -1
  263. package/cli/templates/ros2-cpp/ros2_ws/src/__PLUGIN_SLUG__/CMakeLists.txt +17 -0
  264. package/cli/templates/ros2-cpp/ros2_ws/src/__PLUGIN_SLUG__/package.xml +11 -0
  265. package/cli/templates/ros2-cpp/ros2_ws/src/__PLUGIN_SLUG__/src/node.cpp +22 -0
  266. package/cli/vite.d.mts +25 -0
  267. package/cli/vite.mjs +292 -0
  268. package/package.json +32 -5
  269. package/cli/src/pack.mjs +0 -61
  270. package/cli/src/preview.mjs +0 -91
  271. package/cli/templates/common/main.py +0 -114
  272. package/cli/templates/common/src/content.tsx +0 -33
@@ -8,7 +8,7 @@ A Muscat plugin project. Build, develop, and ship with `muscat-pk` CLI.
8
8
  muscat-pk dev # UI preview + backend together
9
9
  muscat-pk dev:ui # UI preview only
10
10
  muscat-pk dev:back # backend only (Python watchfiles / ROS2 Docker)
11
- muscat-pk pack # build a distributable ZIP
11
+ muscat-pk build # build + distributable ZIP
12
12
  ```
13
13
 
14
14
  ---
@@ -17,20 +17,170 @@ muscat-pk pack # build a distributable ZIP
17
17
 
18
18
  ```
19
19
  __PLUGIN_DIR__/
20
- ├─ src/
21
- │ └─ content.tsx # plugin UI entry point
20
+ ├─ ui/
21
+ │ ├─ content.tsx # plugin UI entry point
22
+ │ └─ style.css # plugin styles auto-loaded by muscat()
23
+ ├─ py/
24
+ │ └─ main.py # Python backend entry point
22
25
  ├─ assets/
23
26
  │ └─ icon.svg # icon referenced from manifest
24
27
  ├─ manifest.json # metadata, contribution points, runtime config
25
- ├─ main.py # backend (Python or ROS2)
26
- └─ requirements.txt # Python deps (auto-pinned to latest on pack)
28
+ ├─ README.md # this file (copied to dist/ on build)
29
+ └─ requirements.txt # Python deps (auto-pinned to latest on build)
27
30
  ```
28
31
 
29
32
  > ROS2 plugins additionally include a `ros2_ws/` directory.
30
33
 
31
34
  ---
32
35
 
33
- ## Frontend (`src/content.tsx`)
36
+ ## Frontend (`ui/content.tsx`)
37
+
38
+ ### JavaScript dependencies
39
+
40
+ `muscat-pk build` runs this plugin project's own Vite build. Vite resolves
41
+ imports from installed `node_modules`; it does not care whether a package is
42
+ listed under `dependencies` or `devDependencies`, but the package must be
43
+ installed.
44
+
45
+ Do not run a production-only install such as `npm install --omit=dev` before
46
+ building the plugin UI, because build tools like `vite`, `typescript`,
47
+ `tailwindcss`, `@tailwindcss/vite`, `sass`, or `less` are usually
48
+ `devDependencies`.
49
+
50
+ The scaffold keeps this split:
51
+
52
+ | package kind | examples | why |
53
+ |---|---|---|
54
+ | `dependencies` | `@rainbow-robotics/plugin-ui`, UI libraries imported by plugin source | Author-facing APIs used by `ui/*.tsx` |
55
+ | `devDependencies` | `vite`, `typescript`, `tailwindcss`, `@tailwindcss/vite`, `sass`, `less` | Build/dev tools needed before `dist/*.js` is produced |
56
+
57
+ The built plugin ZIP does not include `package.json` or `node_modules`. For
58
+ production, Muscat provides `@rainbow-robotics/plugin-ui`, React, and ReactDOM
59
+ through the host import map; plugin UI build output imports those as host
60
+ externals.
61
+
62
+ ### Styles
63
+
64
+ `muscat()` automatically loads `ui/style.{css,scss,sass,less}` (or
65
+ `ui/styles.{css,scss,sass,less}`) for every UI entry, plus
66
+ `ui/{entry}.{css,scss,sass,less}` for a matching entry-specific stylesheet. You
67
+ do not need to import CSS from every `.tsx` file.
68
+
69
+ The scaffold uses Tailwind's theme/utilities layers only, so plugin utilities
70
+ are generated from files under `ui/` without applying Tailwind preflight resets
71
+ to the host page.
72
+
73
+ Built CSS selectors are scoped under the current plugin id
74
+ (`[data-muscat-plugin-id="..."]`). CSS-in-JS libraries such as Emotion work
75
+ normally, but global styles should be scoped by the author.
76
+
77
+ #### Tailwind
78
+
79
+ This template enables Tailwind in `vite.config.ts`:
80
+
81
+ ```ts
82
+ import { defineConfig } from "vite";
83
+ import tailwindcss from "@tailwindcss/vite";
84
+ import { muscat } from "@rainbow-robotics/muscat-plugin-kit/vite";
85
+
86
+ export default defineConfig({
87
+ plugins: [tailwindcss(), muscat()],
88
+ });
89
+ ```
90
+
91
+ The default `ui/style.css` uses Tailwind theme/utilities only, not preflight:
92
+
93
+ ```css
94
+ @layer theme, utilities;
95
+
96
+ @import "tailwindcss/theme.css" layer(theme);
97
+ @import "tailwindcss/utilities.css" layer(utilities);
98
+
99
+ @source "./**/*.{ts,tsx}";
100
+ ```
101
+
102
+ #### SCSS, Sass, and Less
103
+
104
+ Install the preprocessor package, then create one of the auto-loaded style files.
105
+
106
+ ```bash
107
+ npm i -D sass
108
+ ```
109
+
110
+ ```scss
111
+ /* ui/style.scss */
112
+ $accent: #3b82f6;
113
+
114
+ .panel {
115
+ border: 1px solid color-mix(in srgb, $accent 40%, transparent);
116
+
117
+ &__title {
118
+ color: $accent;
119
+ }
120
+ }
121
+ ```
122
+
123
+ For Less, install `less` and use `ui/style.less`.
124
+
125
+ #### Emotion
126
+
127
+ Emotion dependencies may be in `dependencies` or `devDependencies`; Vite can
128
+ bundle them when they are installed and imported by the plugin.
129
+
130
+ For `styled`, no Vite config change is needed:
131
+
132
+ ```bash
133
+ npm i @emotion/react @emotion/styled
134
+ ```
135
+
136
+ ```tsx
137
+ import styled from "@emotion/styled";
138
+
139
+ const Panel = styled.div({
140
+ display: "grid",
141
+ gap: 12,
142
+ });
143
+ ```
144
+
145
+ For Emotion's `css` prop, pass React plugin options through `muscat()` and add
146
+ `jsxImportSource` to `tsconfig.json`:
147
+
148
+ ```bash
149
+ npm i @emotion/react @emotion/styled
150
+ npm i -D @emotion/babel-plugin
151
+ ```
152
+
153
+ ```ts
154
+ // vite.config.ts
155
+ import { defineConfig } from "vite";
156
+ import tailwindcss from "@tailwindcss/vite";
157
+ import { muscat } from "@rainbow-robotics/muscat-plugin-kit/vite";
158
+
159
+ export default defineConfig({
160
+ plugins: [
161
+ tailwindcss(),
162
+ muscat({
163
+ react: {
164
+ jsxImportSource: "@emotion/react",
165
+ babel: {
166
+ plugins: ["@emotion/babel-plugin"],
167
+ },
168
+ },
169
+ }),
170
+ ],
171
+ });
172
+ ```
173
+
174
+ ```json
175
+ {
176
+ "compilerOptions": {
177
+ "jsxImportSource": "@emotion/react"
178
+ }
179
+ }
180
+ ```
181
+
182
+ Emotion `Global` writes global CSS at runtime, so scope it manually or avoid it
183
+ inside plugins.
34
184
 
35
185
  ### useRb() hook
36
186
 
@@ -46,13 +196,19 @@ function App() {
46
196
 
47
197
  ### rb API
48
198
 
49
- | Method | Description |
50
- |---|---|
51
- | `rb.pub(topic, payload)` | Publish a message to a topic |
52
- | `rb.sub(topic, handler)` | Subscribe to a topic — returns `{ undeclare() }` |
53
- | `rb.action(topic, payload?)` | Send a request and await a reply |
54
- | `rb.serve(topic, handler)` | Handle incoming requests from the backend |
55
- | `rb.toast(message, type?)` | Host toast notification (`"info" · "success" · "warning" · "error"`) |
199
+ | Method | Description |
200
+ | ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
201
+ | `rb.pub(topic, payload)` | Publish a message to a topic |
202
+ | `rb.sub(topic, handler)` | Subscribe to a topic — returns `{ undeclare() }` |
203
+ | `rb.action(topic, payload?)` | Send a request and await a reply |
204
+ | `rb.serve(topic, handler)` | Handle incoming requests from the backend |
205
+ | `rb.toast({ title?, message, type?, actionLabel?, handleActionClick?, closeOnActionClick?, duration?, onClose? })` | Show a host toast notification. `message` accepts a string or `ReactElement`. `type` can be `"info"`, `"danger"`, `"warning"`, `"caution"`, or `"success"` |
206
+ | `rb.alert({ title?, message, buttonText? })` | Show a host alert dialog. Resolves when the dialog is closed |
207
+ | `rb.confirm({ title?, message, buttonText? })` | Show a host confirmation dialog. `buttonText` accepts `{ yes, no }`. Resolves to `true` or `false` |
208
+ | `rb.prompt({ title?, message, value?, placeholder?, buttonText? })` | Show a host prompt dialog. `buttonText` accepts `{ yes, no }`. Resolves to the entered string or `undefined` |
209
+
210
+ Topic names passed to `pub`, `sub`, `action`, and `serve` are isolated per plugin.
211
+
56
212
 
57
213
  All methods support Generic Types:
58
214
 
@@ -66,91 +222,306 @@ const sub = await rb.sub<{ value: number }>("sensor/data", (data) => {
66
222
  console.log(data.value);
67
223
  });
68
224
  sub.undeclare(); // cleanup
225
+
226
+ const ok = await rb.confirm({
227
+ title: "Run",
228
+ message: "Start the backend action?",
229
+ });
230
+ if (ok) {
231
+ await rb.action("run");
232
+ }
69
233
  ```
70
234
 
71
- ### Topic namespace
235
+ ### Saving and restoring form data (`useRbFormData` / `useRbSave`)
72
236
 
73
- Topics are automatically namespaced by plugin ID to prevent collisions across plugins.
237
+ If the host embeds your plugin in a form it owns (e.g. a program step editor
238
+ with its own Save button), it can hand your plugin previously-saved values and
239
+ pull the current values back out when the user saves. The host — not the
240
+ plugin — persists the data (IndexedDB, backend, etc.).
74
241
 
242
+ ```tsx
243
+ import { useState } from "react";
244
+ import { useRbFormData, useRbSave } from "@rainbow-robotics/plugin-ui/react";
245
+
246
+ type GripArgs = { targetName: string; width: number };
247
+ const DEFAULTS: GripArgs = { targetName: "", width: 20 };
248
+
249
+ export default function App() {
250
+ // Previously-saved values, or undefined for a brand-new instance.
251
+ const saved = useRbFormData<GripArgs>();
252
+ const [args, setArgs] = useState<GripArgs>({ ...DEFAULTS, ...saved });
253
+
254
+ // Registers a getter the host calls when its Save button is clicked.
255
+ // Always returns the CURRENT values — re-registering on every render keeps
256
+ // the closure fresh, which useRbSave already handles for you.
257
+ useRbSave(() => ({
258
+ args,
259
+ summary: `${args.targetName || "target"} · ${args.width}mm`,
260
+ }));
261
+
262
+ return (/* ...form UI reading/writing `args`... */);
263
+ }
75
264
  ```
76
- "sensor/data" → plugin/{pluginId}/sensor/data (auto-namespaced)
77
- "/broadcast/x" → broadcast/x (leading / = absolute)
78
- ```
265
+
266
+ The value returned from `useRbSave` must be JSON-serialisable — the host
267
+ persists it as-is. Not every host embeds plugins in a save/restore form; if
268
+ yours doesn't, you can skip these hooks entirely.
79
269
 
80
270
  ### UI components
81
271
 
82
- Import Muscat components from `@rainbow-robotics/plugin-ui/react`. The plugin emits
83
- `rb-*` tags; the host renders the actual components.
272
+ Import Muscat components from `@rainbow-robotics/plugin-ui/react` and export a
273
+ React component as the default export. The host mounts that component directly
274
+ and provides `useRb()` through context.
84
275
 
85
276
  ```tsx
86
- import { connect } from "@rainbow-robotics/plugin-ui/connect";
87
- import { Button, Slider, Text, Switch } from "@rainbow-robotics/plugin-ui/react";
277
+ import { useState } from "react";
278
+ import { RBLabelButton, RBSlider, RBSwitch } from "@rainbow-robotics/plugin-ui/react";
88
279
  import { useRb } from "@rainbow-robotics/plugin-ui/sdk";
89
280
 
90
- function App() {
281
+ export default function App() {
91
282
  const rb = useRb();
92
- return <Button label="Run" onPress={() => rb.action("run")} />;
283
+ const [width, setWidth] = useState(20);
284
+ const [enabled, setEnabled] = useState(true);
285
+
286
+ return (
287
+ <div className="flex flex-col gap-4">
288
+ <RBSlider
289
+ value={[width]}
290
+ min={0}
291
+ max={100}
292
+ onValueChange={(values: number[]) => setWidth(values[0] ?? 0)}
293
+ />
294
+ <RBSwitch checked={enabled} onCheckedChange={setEnabled} />
295
+ <RBLabelButton
296
+ disabled={!enabled}
297
+ onClick={() => rb.action("grip", { width })}
298
+ >
299
+ Grip
300
+ </RBLabelButton>
301
+ </div>
302
+ );
93
303
  }
304
+ ```
94
305
 
95
- connect(App);
306
+ ---
307
+
308
+ ## Developing directly on the robot PC
309
+
310
+ If you're running `muscat-pk dev` **on the robot PC itself**, Muscat is likely already
311
+ running its own Socket.IO bridge on port 10000. In that case the CLI detects the port
312
+ conflict and asks:
313
+
314
+ ```
315
+ ⚠ port 10000 is already in use — can't start Socket.IO bridge.
316
+ Proceed without Socket.IO bridge? [y/N]:
96
317
  ```
97
318
 
319
+ Answer **y**. Your plugin backend will connect to the Muscat bridge that's already
320
+ running instead of starting a new one — exactly what you want when developing on the
321
+ robot.
322
+
323
+ > If you answer **n** (or just press Enter), the CLI exits so you can free the port
324
+ > manually and retry.
325
+
98
326
  ---
99
327
 
100
- ## Backend (`main.py`)
328
+ ## Backend runtimes
329
+
330
+ `manifest.json` chooses one backend runtime. `muscat-pk dev` runs backend code in
331
+ a Docker container matching the robot environment. Backend code should contain
332
+ plugin logic only; it should not start the Muscat bridge or its own copy of the
333
+ plugin gateway.
334
+
335
+ ### Which backend pattern? (depends on the contribution point)
336
+
337
+ **How you control the robot depends on what your plugin contributes** (see
338
+ `manifest.json` → `contributes[].point`). Don't mix these up:
339
+
340
+ | Contribution point | What it is | Backend pattern |
341
+ | --- | --- | --- |
342
+ | `program.panel` (with `commandInfo`) | Defines a **custom command** the plugin adds to the Program page's rule-based sequence builder. The operator places it into the program tree like any built-in command (Move J, Point, Wait, …); it runs when the program reaches that step. | **`PluginProgramContext`** — integrated with the program's stop/pause and variables. See _Program command backend_ below. |
343
+ | `toolbar` / `lnb` / `move.panel` | A live UI panel/popover the operator interacts with directly | Call the robot **SDK directly** (`RBManipulateSDK`, `RBAmrSDK`) from `py/main.py`, or `rb.action(...)` → your backend → SDK. **Not** a program step — there is no stop/pause/variable context. |
344
+
345
+ Rule of thumb: use `PluginProgramContext` **only** for a `program.panel` command
346
+ that runs as part of a program. To move the robot live from a toolbar/move panel,
347
+ use the SDK — a program-step context does not apply.
348
+
349
+ ### Frontend-only (`none`)
350
+
351
+ Use this when the plugin only has UI and calls host services:
352
+
353
+ ```json
354
+ { "runtime": { "type": "none" } }
355
+ ```
356
+
357
+ ### Python
358
+
359
+ Python plugins use `py/main.py` and can communicate with the UI through
360
+ `rainbow.rb_sdk.RBPluginCall`.
361
+
362
+ ```python
363
+ import time
364
+ from rainbow.rb_sdk import RBAmrSDK, RBManipulateSDK, RBPluginCall
365
+
366
+ plugin = RBPluginCall()
367
+
368
+
369
+ plugin.publish("state", {"ready": True})
101
370
 
102
- The backend communicates with the frontend using `rainbow_rb_zenoh`.
103
- Set the `prefix` to `plugin/{manifest.id}` to match the frontend's auto-namespace.
104
371
 
105
- ### Python plugin
372
+ @plugin.subscribe("event")
373
+ def on_ui_event(payload=None):
374
+ print(payload)
375
+
376
+
377
+ @plugin.serve("tool/echo")
378
+ def echo(payload=None):
379
+ return {"echo": payload}
380
+
381
+
382
+ def call_frontend():
383
+ res = plugin.call("tool/ping", payload={"hello": "plugin"})
384
+ print(res)
385
+
386
+
387
+ def move_j_example():
388
+ amr = RBAmrSDK()
389
+ manipulate = RBManipulateSDK()
390
+ return manipulate.move.call_move_j(
391
+ robot_model="C500920",
392
+ target={"tar_values": [0, 0, 0, 0, 0, 0], "tar_frame": 0, "tar_unit": 0},
393
+ )
394
+
395
+
396
+ if __name__ == "__main__":
397
+ while True:
398
+ time.sleep(1)
399
+ ```
400
+
401
+ #### Program command backend (`program.panel`)
402
+
403
+ A `program.panel` contribution defines a **custom command** your plugin adds to
404
+ the Muscat **Program page**, where the operator builds a robot program as a
405
+ rule-based sequence (a program tree). Your command appears alongside the
406
+ built-ins (Move J, Point, Wait, …); the operator places it into the tree, and
407
+ each time the program executes that step the host calls your backend through
408
+ `PluginProgramContext` — receiving the step args, checking for stop/pause,
409
+ reading/writing program variables, and signalling completion. This pattern is
410
+ **only** for `program.panel`; a toolbar/move-panel plugin uses the SDK directly
411
+ instead (see the table above).
106
412
 
107
413
  ```python
414
+ """
415
+ Plugin backend main.py example (program.panel command).
416
+
417
+ manifest.json:
418
+ "runtime": {"type": "python", "entry": "main.py"},
419
+ "contributes": [{
420
+ "point": "program.panel",
421
+ "ui": "program",
422
+ "label": {"en": "My Plugin Step"},
423
+ "commandInfo": {
424
+ "name": "My Plugin Step",
425
+ "icon": "assets/icon.svg",
426
+ "funcName": "rb_program_sdk.plugin_execute",
427
+ "args": {"message": "hello"} # plugin-author-defined args
428
+ }
429
+ }]
430
+
431
+ The host invokes commandInfo.funcName (rb_program_sdk.plugin_execute) with
432
+ args.plugin_id + your args when the program reaches this step.
433
+ """
108
434
  import asyncio
109
- from rainbow_rb_zenoh import ZenohRouter, ZenohClient
435
+ from rainbow.rb_sdk import PluginProgramContext
110
436
 
111
- router = ZenohRouter(prefix="plugin/__PLUGIN_ID__")
112
- client = ZenohClient()
437
+ ctx = PluginProgramContext(plugin_id="my-plugin")
113
438
 
114
439
 
115
- @router.subscribe("sensor/data")
116
- async def on_sensor(*, dict_payload, **_):
117
- print("received:", dict_payload)
440
+ @ctx.on_execute
441
+ async def handle(args: dict) -> None:
442
+ message = args.get("message", "")
443
+ print(f"[my-plugin] execute called: message={message}", flush=True)
118
444
 
445
+ # check stop/pause during long-running work
446
+ for _ in range(10):
447
+ await ctx.check_stop()
448
+ await asyncio.sleep(0.1)
119
449
 
120
- @router.queryable("get-status")
121
- async def get_status(req=None, **_):
122
- return {"ok": True}
450
+ # read a program variable
451
+ counter = await ctx.get_variable("counter")
452
+ print(f"[my-plugin] counter={counter}", flush=True)
123
453
 
454
+ # update a variable
455
+ await ctx.set_variable("result", f"done: {message}")
124
456
 
125
- async def main():
126
- await router.startup()
127
- await asyncio.Event().wait() # keep alive
457
+ # finish the step
458
+ await ctx.done()
128
459
 
129
460
 
130
461
  if __name__ == "__main__":
131
- asyncio.run(main())
462
+ asyncio.run(ctx.start())
132
463
  ```
133
464
 
134
- ### ROS2 plugin
465
+ ### C++
466
+
467
+ C++ plugins use `cpp/` and are built with CMake inside Docker. The current C++
468
+ template is a build/run skeleton; wire it to the plugin gateway when the C++ SDK
469
+ is available.
470
+
471
+ ```cpp
472
+ #include <chrono>
473
+ #include <cstdlib>
474
+ #include <iostream>
475
+ #include <string>
476
+ #include <thread>
477
+
478
+ int main() {
479
+ const char* plugin_id = std::getenv("RB_PLUGIN_ID");
480
+ std::cout << "[__PLUGIN_NAME__] backend ready"
481
+ << (plugin_id ? std::string(" (id=") + plugin_id + ")" : "")
482
+ << std::endl;
483
+
484
+ while (true) {
485
+ std::this_thread::sleep_for(std::chrono::seconds(1));
486
+ }
487
+ return 0;
488
+ }
489
+ ```
135
490
 
136
- The node is launched with `--ros-args -r __ns:=/{slug}` automatically by `dev:back`.
137
- ROS2 topics are bridged to the plugin communication layer — no extra code needed.
491
+ ### ROS2 (rclpy)
492
+
493
+ ROS2 Python plugins use `ros2_ws/` with `runtime.rosClient: "rclpy"`. The node
494
+ is launched with `--ros-args -r __ns:=/{slug}` automatically by `dev:back`.
138
495
 
139
496
  ```python
140
497
  # ros2_ws/src/__PLUGIN_SLUG__/__PLUGIN_SLUG__/node.py
141
498
  import rclpy
142
499
  from rclpy.node import Node
143
- from std_msgs.msg import Float32
500
+ from rainbow.rb_sdk import RBManipulateSDK
501
+
502
+ sdk = RBManipulateSDK()
503
+
144
504
 
145
505
  class __PLUGIN_CLASS__Node(Node):
146
506
  def __init__(self):
147
507
  super().__init__("__PLUGIN_SLUG___node")
148
- # relative topic name — __ns prefix applied automatically
149
- self.pub = self.create_publisher(Float32, "sensor_data", 10)
150
- self.create_timer(1.0, self.timer_cb)
508
+ self.get_logger().info("__PLUGIN_NAME__ node started")
509
+ # use sdk alongside ROS2 publishers/subscribers/services as needed
510
+ ```
151
511
 
152
- def timer_cb(self):
153
- self.pub.publish(Float32(data=42.0))
512
+ ### ROS2 (rclcpp)
513
+
514
+ ROS2 C++ plugins use `ros2_ws/` with `runtime.rosClient: "rclcpp"`.
515
+
516
+ ```cpp
517
+ #include "rclcpp/rclcpp.hpp"
518
+
519
+ class __PLUGIN_CLASS__Node : public rclcpp::Node {
520
+ public:
521
+ __PLUGIN_CLASS__Node() : Node("__PLUGIN_SLUG___node") {
522
+ RCLCPP_INFO(this->get_logger(), "__PLUGIN_NAME__ node started");
523
+ }
524
+ };
154
525
  ```
155
526
 
156
527
  ---
@@ -159,30 +530,156 @@ class __PLUGIN_CLASS__Node(Node):
159
530
 
160
531
  ```jsonc
161
532
  {
162
- "id": "com.example.my-plugin", // unique plugin ID (reverse-domain recommended)
533
+ // unique plugin ID — reverse-domain format recommended
534
+ "id": "com.example.my-plugin",
163
535
  "name": "My Plugin",
164
536
  "version": "1.0.0",
165
- "minAppVersion": "2.0.0",
166
- "icon": "assets/icon.svg",
537
+
538
+ // minimum Muscat version required to load this plugin
539
+ "minMuscatVersion": "1.0.0",
540
+
541
+ // plugin author (empty string is fine)
542
+ "author": "Your Name",
543
+
544
+ // displayed in the Muscat plugin store / info panel
545
+ // string or { en (required), ko, ja, zh, vi, th, es }
546
+ "description": { "en": "Plugin description", "ko": "플러그인 설명" },
547
+
548
+ // SVG string (single file) or PNG/JPG size map (all three sizes required)
549
+ "icons": "assets/icon.svg",
550
+ // "icons": { "16": "assets/icon-16.png", "48": "assets/icon-48.png", "128": "assets/icon-128.png" },
551
+
552
+ // targetModels and/or targetCategorys — at least one is required
553
+ // "all" = every target, or list specific values
554
+ "targetModels": "all",
555
+ // "targetModels": ["C500920", "C500930"],
556
+ "targetCategorys": "all",
557
+ // "targetCategorys": ["manipulate", "amr", "rby1", "vision"],
167
558
 
168
559
  "contributes": [
169
- { "point": "program.panel", "label": "My Plugin", "icon": "assets/icon.svg" },
170
- { "point": "lnb", "label": "My Plugin", "icon": "assets/icon.svg" },
171
- { "point": "header.toolbar","label": "My Plugin", "icon": "assets/icon.svg", "opens": "popover" }
560
+ // program.panel — defines a custom command for the Program page sequence
561
+ // builder; runs via PluginProgramContext when the program reaches it
562
+ {
563
+ "point": "program.panel",
564
+ "icon": "assets/icon.svg",
565
+ "label": { "ko": "내 플러그인", "en": "My Plugin" },
566
+ "ui": "program", // must be unique across all contributions
567
+ "commandInfo": {
568
+ "name": "My Plugin",
569
+ "icon": "assets/icon.svg",
570
+ "funcName": "rb_program_sdk.plugin_execute",
571
+ "args": {}
572
+ }
573
+ },
574
+ // toolbar — adds a button to the top toolbar (required)
575
+ {
576
+ "point": "toolbar",
577
+ "icon": "assets/icon.svg",
578
+ "label": { "ko": "내 플러그인", "en": "My Plugin" },
579
+ "ui": "content", // must be unique across all contributions
580
+ "opens": "popover" // popover | panel | page
581
+ },
582
+ // move.panel — adds a panel to the Move screen
583
+ {
584
+ "point": "move.panel",
585
+ "icon": "assets/icon.svg",
586
+ "label": { "ko": "내 플러그인", "en": "My Plugin" },
587
+ "ui": "move" // must be unique across all contributions
588
+ }
172
589
  ],
173
590
 
174
- // "python" | "ros2"
175
- "runtime": { "type": "python", "entry": "main.py" }
591
+ // runtime — one of:
592
+ // { "type": "none" }
593
+ // { "type": "python", "entry": "main.py" }
594
+ // { "type": "cpp", "entry": "<executable name>" }
595
+ // { "type": "ros2", "rosClient": "rclpy" | "rclcpp", "package": "...", "launch": "..._node" }
596
+ "runtime": { "type": "python", "entry": "main.py" },
597
+
598
+ // optional: TCP ports opened by this plugin backend
599
+ // only number[] is allowed; ranges/objects are not accepted
600
+ // external plugin ports must be within 28600-28999
601
+ "ports": [28600]
176
602
  }
177
603
  ```
178
604
 
605
+ **Backend types:**
606
+
607
+ | type | source dir | build / run | entry |
608
+ |---|---|---|---|
609
+ | `python` | `py/` | pip + run in Docker | `main.py` |
610
+ | `cpp` | `cpp/` | CMake in Docker | built executable name |
611
+ | `ros2` (rclpy) | `ros2_ws/` | colcon (ament_python) in Docker | — |
612
+ | `ros2` (rclcpp) | `ros2_ws/` | colcon (ament_cmake) in Docker | — |
613
+
614
+ > Every back end runs in a Docker container matching the robot (Ubuntu 24.04 + Python 3.12), so dev behaves like production and compiled binaries are ABI-compatible. **Docker is required** for all backend types (only a frontend-only `none` plugin skips it). No local Python/toolchain setup is needed.
615
+
179
616
  **Contribution points:**
180
617
 
181
- | point | Where it appears |
182
- |---|---|
183
- | `program.panel` | Program panel tab |
184
- | `lnb` | Left navigation bar |
185
- | `header.toolbar` | Header toolbar button |
618
+ | point | Where it appears | Backend | `commandInfo` |
619
+ |---|---|---|:---:|
620
+ | `toolbar` | Top toolbar button (must have at least one) | SDK / `rb.action` | — |
621
+ | `lnb` | Left navigation entry | SDK / `rb.action` | — |
622
+ | `move.panel` | Move screen panel | SDK / `rb.action` | — |
623
+ | `program.panel` | Custom command in the Program page sequence builder | `PluginProgramContext` | ✓ |
624
+
625
+ **`commandInfo` fields** (for `program.panel`):
626
+
627
+ | field | type | description |
628
+ |---|---|---|
629
+ | `name` | `string` | Display name of the command in the program tree |
630
+ | `icon` | `string` | Icon path |
631
+ | `iconClassName` | `string?` | Optional CSS class for the icon |
632
+ | `funcName` | `string?` | Backend function name to call |
633
+ | `args` | `Record<string, any>?` | Default arguments |
634
+ | `disabled` | `boolean?` | Whether the node is disabled |
635
+ | `allowChildren` | `string[]?` | Allowed child commands |
636
+ | `allowParents` | `string[]?` | Allowed parent commands |
637
+ | `notAllowChildren` | `string[]?` | Disallowed child commands |
638
+ | `syncGroupId` | `boolean?` | Sync group ID across children |
639
+ | `disableCopy` | `boolean?` | Prevent copy |
640
+ | `disableDelete` | `boolean?` | Prevent delete |
641
+ | `requireChildren` | `boolean?` | Whether a child command is required |
642
+ | `requireParent` | `boolean?` | Whether a parent command is required |
643
+ | `childrenCommand` | `string?` | Default command type for children |
644
+
645
+ **Validation rules:**
646
+ - `id`, `name`, `version`, `minMuscatVersion` — required non-empty strings
647
+ - `author` — required non-empty string
648
+ - `description` — required (`string` or `{ en (required), ko?, ja?, zh?, vi?, th?, es? }`)
649
+ - `icons` — SVG: single string path ending in `.svg`; PNG/JPG: object with `"16"`, `"48"`, `"128"` all present
650
+ - `targetModels` — `"all"` or a non-empty array of robot model ID strings (at least one of `targetModels` / `targetCategorys` required)
651
+ - `targetCategorys` — `"all"` or a non-empty array of `"manipulate" | "amr" | "rby1" | "vision"` (at least one of `targetModels` / `targetCategorys` required)
652
+ - `contributes` — required array; must contain at least one `toolbar` entry
653
+ - `ui` — each contribution must have a unique value
654
+ - `commandInfo` — required when `point` is `"program.panel"`
655
+ - `ports` — optional `number[]`; every port must be within `28600-28999`
656
+ and must not be a reserved robot/industrial/internal port
657
+
658
+ `muscat-pk build` throws on any validation error. `muscat-pk dev` prints warnings.
659
+
660
+ **Backend port rules:**
661
+ - Declare a port only if your backend opens a TCP server/listener.
662
+ - `muscat-pk dev` and `muscat-pk dev:back` stop the backend if it opens an
663
+ undeclared managed port.
664
+ - Reserved ports include `502`, `2222`, `4840`, `5170-5179`, `8000-8999`,
665
+ `10000`, `30004`, `34964`, `44818`, `50000-50005`, and `50051`.
666
+
667
+ ---
668
+
669
+ ## Localized documentation
670
+
671
+ You can ship per-language README files alongside the main `README.md`. Place them in the project root:
672
+
673
+ ```
674
+ README.md # default / English fallback
675
+ README.ko.md # Korean
676
+ README.en.md # explicit English (optional)
677
+ README.ja.md # Japanese
678
+ ```
679
+
680
+ All `README*.md` files found at the project root are automatically copied to `dist/` on
681
+ `muscat-pk build`. Muscat displays the file matching the user's locale,
682
+ falling back to `README.md` if none matches.
186
683
 
187
684
  ---
188
685
 
@@ -197,37 +694,96 @@ numpy==1.26.4
197
694
  opencv-python
198
695
  ```
199
696
 
200
- Then just run `muscat-pk dev` or `muscat-pk dev:back` — dependencies are installed automatically on every startup:
697
+ Run `muscat-pk dev` or `muscat-pk dev:back` — dependencies are installed automatically
698
+ inside the dev container (`pip install -r requirements.txt`) on every startup. No
699
+ local Python is required for this.
201
700
 
202
- - **Python plugin**: installed into `.venv` via `pip install -r requirements.txt`
203
- - **ROS2 plugin**: installed inside the Docker container via `pip install -r requirements.txt`
701
+ `muscat-pk dev:back` also best-effort creates/refreshes a local `.venv` in the
702
+ background (via `uv` if available, else your system Python) with the same deps —
703
+ purely so your editor (pylint, pyright, mypy, ...) can resolve imports and lint
704
+ correctly. It's not used to run the backend; Docker is the actual execution
705
+ environment, so this step is silently skipped if no local Python toolchain is
706
+ available. Point your editor's Python interpreter at `.venv`.
204
707
 
205
- No extra steps needed.
708
+ `muscat-pk sdk update` also refreshes `.venv` in place if one already exists, so
709
+ your editor picks up the new `rainbow-rb-sdk` version immediately.
206
710
 
207
- > `muscat-pk build` / `muscat-pk pack` only **pins** versions in `requirements.txt` — it does not run `pip install`.
711
+ > `muscat-pk build` only **pins** versions in `requirements.txt` — it does not run `pip install` (the robot installs them at load time).
208
712
 
209
713
  ---
210
714
 
211
715
  ## Switching backend type
212
716
 
213
717
  ```bash
214
- muscat-pk type change # interactive
215
- muscat-pk type change --to ros2 # python → ros2
216
- muscat-pk type change --to python # ros2 → python
718
+ muscat-pk type change # interactive
719
+ muscat-pk type change --to cpp # → C++
720
+ muscat-pk type change --to ros2 # → ROS2 (rclpy / Python)
721
+ muscat-pk type change --to ros2-cpp # → ROS2 (rclcpp / C++)
722
+ muscat-pk type change --to python # → Python
723
+ ```
724
+
725
+ > Existing backend dirs (`ros2_ws/`, `cpp/`) are not deleted when switching away. Remove them manually if no longer needed.
726
+
727
+ ---
728
+
729
+ ## AI coding assistants (MCP)
730
+
731
+ If you build your plugin with an AI assistant (Claude Code, Codex, …), give it
732
+ grounded knowledge of the Muscat UI components, design tokens, and SDK instead of
733
+ letting it guess. `muscat-pk mcp` is an MCP server exposing:
734
+
735
+ - `list_components` / `get_component_props` / `get_component_example` — every
736
+ `@rainbow-robotics/plugin-ui/react` component with its real props and a working
737
+ Storybook example.
738
+ - `list_design_tokens` / `search_design_tokens` — the Tailwind classes to use
739
+ (`bg-bg-*`, `text-fg-*`, `typo-*`, …) so your UI matches the host theme.
740
+ - `search_rb_sdk` — how to use `useRb()`, `rb.*`, `useRbFormData`/`useRbSave`, and
741
+ the backend runtimes.
742
+ - `list_rb_sdk` / `get_rb_sdk_api` — the installed `rainbow-rb-sdk` (Python
743
+ backend) classes and signatures.
744
+
745
+ **Browse the components live** with `muscat-pk storybook`.
746
+
747
+ ### Register the server
748
+
749
+ **Claude Code** — a `.mcp.json` is already scaffolded at your plugin root, so
750
+ it works out of the box:
751
+
752
+ ```json
753
+ {
754
+ "mcpServers": {
755
+ "muscat": {
756
+ "command": "muscat-pk",
757
+ "args": ["mcp"]
758
+ }
759
+ }
760
+ }
761
+ ```
762
+
763
+ **Codex** — add to `~/.codex/config.toml`:
764
+
765
+ ```toml
766
+ [mcp_servers.muscat]
767
+ command = "muscat-pk"
768
+ args = ["mcp"]
217
769
  ```
218
770
 
219
- > `ros2_ws/` is not deleted when switching back to python. Remove it manually if no longer needed.
771
+ Any other MCP client works too — it's a standard stdio MCP server started with
772
+ `muscat-pk mcp`. If `muscat-pk` isn't on the client's PATH, use its absolute path
773
+ (`which muscat-pk`) as `command`.
220
774
 
221
775
  ---
222
776
 
223
777
  ## Shipping
224
778
 
225
779
  ```bash
226
- muscat-pk pack
780
+ muscat-pk build # build + ZIP
781
+ muscat-pk build --no-zip # build only, no ZIP
227
782
  ```
228
783
 
229
- - Builds `src/content.tsx` → `dist/content.js`
784
+ - Builds `ui/content.tsx` → `dist/content.js` (minified)
230
785
  - Pins `requirements.txt` to latest versions (stable-only on `main` branch)
786
+ - Copies `assets/`, `manifest.json`, `README*.md`, `py/`, and `requirements.txt` into `dist/`
231
787
  - ROS2: runs `colcon build` inside the Jazzy container; includes `ros2_ws/install/`
232
788
  - Output: `__PLUGIN_DIR__-{version}.zip`
233
789
 
@@ -235,10 +791,15 @@ ZIP layout:
235
791
 
236
792
  ```
237
793
  __PLUGIN_DIR__-1.0.0.zip
238
- ├─ dist/ # built UI
239
- ├─ main.py
240
- ├─ requirements.txt # pinned
241
- └─ ros2_ws/install/ # ROS2 only
794
+ ├─ dist/
795
+ │ ├─ content.js # built UI
796
+ │ ├─ manifest.json
797
+ │ ├─ assets/
798
+ │ ├─ README.md
799
+ │ ├─ README.ko.md # if present
800
+ │ ├─ main.py # from py/
801
+ │ └─ requirements.txt # pinned
802
+ └─ ros2_ws/install/ # ROS2 only
242
803
  ```
243
804
 
244
805
  ---
@@ -249,8 +810,9 @@ __PLUGIN_DIR__-1.0.0.zip
249
810
  muscat-pk dev # UI + backend
250
811
  muscat-pk dev:ui # UI preview only (preview-host)
251
812
  muscat-pk dev:back # backend only (Python: watchfiles / ROS2: Docker)
252
- muscat-pk build # UI build only → dist/
253
- muscat-pk pack # build + ZIP
813
+ muscat-pk build # build → dist/ + distributable ZIP (--no-zip to skip)
814
+ muscat-pk storybook # browse the rb-components UI library in a browser
815
+ muscat-pk mcp # MCP server for AI assistants (components/tokens/SDK docs)
254
816
  ```
255
817
 
256
818
  ```bash
@@ -258,4 +820,5 @@ muscat-pk type change # switch backend type
258
820
  muscat-pk sdk list # show current Python SDK version
259
821
  muscat-pk sdk update # update Python SDK to latest
260
822
  muscat-pk update # update muscat-plugin-kit itself
823
+ muscat-pk uninstall # remove muscat-pk itself
261
824
  ```