@babylonjs/inspector 9.0.0 → 9.2.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 (293) hide show
  1. package/bin/inspector-bridge.mjs +4734 -0
  2. package/bin/inspector-cli.mjs +4931 -0
  3. package/lib/cli/protocol.d.ts +180 -0
  4. package/lib/components/curveEditor/bottomBar/rangeSelector.d.ts +6 -0
  5. package/lib/components/curveEditor/bottomBar.d.ts +6 -0
  6. package/lib/components/curveEditor/canvas/canvas.d.ts +6 -0
  7. package/lib/components/curveEditor/canvas/curve.d.ts +14 -0
  8. package/lib/components/curveEditor/canvas/curveData.d.ts +40 -0
  9. package/lib/components/curveEditor/canvas/frameBar.d.ts +12 -0
  10. package/lib/components/curveEditor/canvas/graph.d.ts +11 -0
  11. package/lib/components/curveEditor/canvas/keyPoint.d.ts +34 -0
  12. package/lib/components/curveEditor/canvas/playHead.d.ts +12 -0
  13. package/lib/components/curveEditor/canvas/rangeFrameBar.d.ts +10 -0
  14. package/lib/components/curveEditor/curveEditor.d.ts +29 -0
  15. package/lib/components/curveEditor/curveEditorButton.d.ts +31 -0
  16. package/lib/components/curveEditor/curveEditorColors.d.ts +56 -0
  17. package/lib/components/curveEditor/curveEditorContext.d.ts +267 -0
  18. package/lib/components/curveEditor/rangeSelector.d.ts +6 -0
  19. package/lib/components/curveEditor/sideBar/addAnimationPanel.d.ts +10 -0
  20. package/lib/components/curveEditor/sideBar/animationList.d.ts +6 -0
  21. package/lib/components/curveEditor/sideBar/editAnimationPanel.d.ts +12 -0
  22. package/lib/components/curveEditor/sideBar/loadAnimationPanel.d.ts +10 -0
  23. package/lib/components/curveEditor/sideBar/saveAnimationPanel.d.ts +10 -0
  24. package/lib/components/curveEditor/sideBar.d.ts +6 -0
  25. package/lib/components/curveEditor/topBar.d.ts +6 -0
  26. package/lib/components/debug/debugPane.d.ts +6 -0
  27. package/lib/components/errorBoundary.d.ts +31 -0
  28. package/lib/components/extensibleAccordion.d.ts +67 -0
  29. package/lib/components/gizmoToolbar.d.ts +7 -0
  30. package/lib/components/pane.d.ts +4 -0
  31. package/lib/components/performanceViewer/canvasGraph.d.ts +16 -0
  32. package/lib/components/performanceViewer/canvasGraphService.d.ts +238 -0
  33. package/lib/components/performanceViewer/graphSupportingTypes.d.ts +90 -0
  34. package/lib/components/performanceViewer/performanceSidebar.d.ts +10 -0
  35. package/lib/components/performanceViewer/performanceViewer.d.ts +15 -0
  36. package/lib/components/pickingToolbar.d.ts +11 -0
  37. package/lib/components/properties/animation/animationGroupProperties.d.ts +8 -0
  38. package/lib/components/properties/animation/animationsProperties.d.ts +21 -0
  39. package/lib/components/properties/animation/targetedAnimationProperties.d.ts +7 -0
  40. package/lib/components/properties/atmosphereProperties.d.ts +20 -0
  41. package/lib/components/properties/audio/soundProperties.d.ts +8 -0
  42. package/lib/components/properties/boundProperty.d.ts +80 -0
  43. package/lib/components/properties/cameras/arcRotateCameraProperties.d.ts +17 -0
  44. package/lib/components/properties/cameras/cameraProperties.d.ts +10 -0
  45. package/lib/components/properties/cameras/followCameraProperties.d.ts +8 -0
  46. package/lib/components/properties/cameras/freeCameraProperties.d.ts +11 -0
  47. package/lib/components/properties/cameras/geospatialCameraProperties.d.ts +11 -0
  48. package/lib/components/properties/cameras/targetCameraProperties.d.ts +8 -0
  49. package/lib/components/properties/commonGeneralProperties.d.ts +16 -0
  50. package/lib/components/properties/frameGraph/frameGraphProperties.d.ts +8 -0
  51. package/lib/components/properties/generateCopyString.d.ts +7 -0
  52. package/lib/components/properties/lights/areaLightProperties.d.ts +5 -0
  53. package/lib/components/properties/lights/clusteredLightContainerProperties.d.ts +10 -0
  54. package/lib/components/properties/lights/directionalLightProperties.d.ts +8 -0
  55. package/lib/components/properties/lights/hemisphericLightProperties.d.ts +5 -0
  56. package/lib/components/properties/lights/lightProperties.d.ts +7 -0
  57. package/lib/components/properties/lights/pointLightProperties.d.ts +5 -0
  58. package/lib/components/properties/lights/shadowGeneratorProperties.d.ts +6 -0
  59. package/lib/components/properties/lights/shadowLightProperties.d.ts +5 -0
  60. package/lib/components/properties/lights/spotLightProperties.d.ts +5 -0
  61. package/lib/components/properties/linkToEntityPropertyLine.d.ts +27 -0
  62. package/lib/components/properties/materials/materialProperties.d.ts +11 -0
  63. package/lib/components/properties/materials/multiMaterialProperties.d.ts +7 -0
  64. package/lib/components/properties/materials/nodeMaterialProperties.d.ts +8 -0
  65. package/lib/components/properties/materials/normalMapProperties.d.ts +19 -0
  66. package/lib/components/properties/materials/openpbrMaterialProperties.d.ts +64 -0
  67. package/lib/components/properties/materials/pbrBaseMaterialProperties.d.ts +285 -0
  68. package/lib/components/properties/materials/skyMaterialProperties.d.ts +5 -0
  69. package/lib/components/properties/materials/standardMaterialProperties.d.ts +29 -0
  70. package/lib/components/properties/metadataProperties.d.ts +12 -0
  71. package/lib/components/properties/nodes/abstractMeshProperties.d.ts +27 -0
  72. package/lib/components/properties/nodes/gaussianSplattingProperties.d.ts +5 -0
  73. package/lib/components/properties/nodes/meshProperties.d.ts +11 -0
  74. package/lib/components/properties/nodes/nodeProperties.d.ts +7 -0
  75. package/lib/components/properties/particles/attractor.d.ts +23 -0
  76. package/lib/components/properties/particles/attractorAdapter.d.ts +56 -0
  77. package/lib/components/properties/particles/attractorList.d.ts +15 -0
  78. package/lib/components/properties/particles/attractorProperties.d.ts +11 -0
  79. package/lib/components/properties/particles/colorProperties.d.ts +11 -0
  80. package/lib/components/properties/particles/commandsProperties.d.ts +13 -0
  81. package/lib/components/properties/particles/emissionProperties.d.ts +11 -0
  82. package/lib/components/properties/particles/emitterProperties.d.ts +13 -0
  83. package/lib/components/properties/particles/lifetimeProperties.d.ts +11 -0
  84. package/lib/components/properties/particles/nodeEditorProperties.d.ts +12 -0
  85. package/lib/components/properties/particles/rotationProperties.d.ts +11 -0
  86. package/lib/components/properties/particles/sizeProperties.d.ts +11 -0
  87. package/lib/components/properties/particles/spritesheetProperties.d.ts +11 -0
  88. package/lib/components/properties/particles/systemProperties.d.ts +13 -0
  89. package/lib/components/properties/physics/physicsProperties.d.ts +6 -0
  90. package/lib/components/properties/postProcesses/postProcessProperties.d.ts +10 -0
  91. package/lib/components/properties/propertiesPane.d.ts +2 -0
  92. package/lib/components/properties/renderingPipelines/defaultRenderingPipelineProperties.d.ts +23 -0
  93. package/lib/components/properties/renderingPipelines/iblShadowsRenderPipelineProperties.d.ts +11 -0
  94. package/lib/components/properties/renderingPipelines/lensRenderingPipelineProperties.d.ts +8 -0
  95. package/lib/components/properties/renderingPipelines/postProcessRenderPipelineProperties.d.ts +5 -0
  96. package/lib/components/properties/renderingPipelines/ssao2RenderingPipelineProperties.d.ts +8 -0
  97. package/lib/components/properties/renderingPipelines/ssaoRenderingPipelineProperties.d.ts +5 -0
  98. package/lib/components/properties/renderingPipelines/ssrRenderingPipelineProperties.d.ts +17 -0
  99. package/lib/components/properties/skeleton/boneProperties.d.ts +7 -0
  100. package/lib/components/properties/skeleton/skeletonProperties.d.ts +8 -0
  101. package/lib/components/properties/sprites/spriteManagerProperties.d.ts +25 -0
  102. package/lib/components/properties/sprites/spriteProperties.d.ts +19 -0
  103. package/lib/components/properties/textures/advancedDynamicTextureProperties.d.ts +11 -0
  104. package/lib/components/properties/textures/baseTextureProperties.d.ts +16 -0
  105. package/lib/components/properties/textures/cubeTextureProperties.d.ts +5 -0
  106. package/lib/components/properties/textures/multiRenderTargetProperties.d.ts +5 -0
  107. package/lib/components/properties/textures/renderTargetTextureProperties.d.ts +5 -0
  108. package/lib/components/properties/textures/textureFormatUtils.d.ts +48 -0
  109. package/lib/components/properties/textures/texturePreview.d.ts +17 -0
  110. package/lib/components/properties/textures/textureProperties.d.ts +11 -0
  111. package/lib/components/properties/textures/thinTextureProperties.d.ts +8 -0
  112. package/lib/components/properties/transformProperties.d.ts +11 -0
  113. package/lib/components/scene/sceneExplorer.d.ts +182 -0
  114. package/lib/components/scene/sceneExplorerDragDrop.d.ts +38 -0
  115. package/lib/components/scene/sceneProperties.d.ts +19 -0
  116. package/lib/components/stats/countStats.d.ts +5 -0
  117. package/lib/components/stats/frameStepStats.d.ts +8 -0
  118. package/lib/components/stats/performanceStats.d.ts +6 -0
  119. package/lib/components/stats/statsPane.d.ts +3 -0
  120. package/lib/components/stats/systemStats.d.ts +5 -0
  121. package/lib/components/teachingMoment.d.ts +20 -0
  122. package/lib/components/textureEditor/canvasManager.d.ts +113 -0
  123. package/lib/components/textureEditor/canvasShader.d.ts +10 -0
  124. package/lib/components/textureEditor/channels.d.ts +25 -0
  125. package/lib/components/textureEditor/properties.d.ts +23 -0
  126. package/lib/components/textureEditor/status.d.ts +13 -0
  127. package/lib/components/textureEditor/textureEditor.d.ts +113 -0
  128. package/lib/components/textureEditor/tools.d.ts +20 -0
  129. package/lib/components/theme.d.ts +10 -0
  130. package/lib/components/tools/capture/equirectangularCaptureTool.d.ts +5 -0
  131. package/lib/components/tools/capture/gifCaptureTool.d.ts +7 -0
  132. package/lib/components/tools/capture/sceneReplayTool.d.ts +5 -0
  133. package/lib/components/tools/capture/screenshotTool.d.ts +5 -0
  134. package/lib/components/tools/capture/videoCaptureTool.d.ts +5 -0
  135. package/lib/components/tools/exportTools.d.ts +11 -0
  136. package/lib/components/tools/import/gltfAnimationImportTool.d.ts +5 -0
  137. package/lib/components/tools/import/gltfLoaderOptionsTool.d.ts +8 -0
  138. package/lib/components/tools/import/gltfValidationTool.d.ts +5 -0
  139. package/lib/components/tools/reflectorTools.d.ts +5 -0
  140. package/lib/components/tools/toolsPane.d.ts +3 -0
  141. package/lib/components/uxContextProvider.d.ts +2 -0
  142. package/lib/contexts/extensionManagerContext.d.ts +6 -0
  143. package/lib/contexts/propertyContext.d.ts +30 -0
  144. package/lib/contexts/settingsContext.d.ts +3 -0
  145. package/lib/contexts/watcherContext.d.ts +3 -0
  146. package/lib/extensibility/builtInsExtensionFeed.d.ts +21 -0
  147. package/lib/extensibility/defaultInspectorExtensionFeed.d.ts +5 -0
  148. package/lib/extensibility/extensionFeed.d.ts +113 -0
  149. package/lib/extensibility/extensionManager.d.ts +111 -0
  150. package/lib/extensions/quickCreate/cameras.d.ts +14 -0
  151. package/lib/extensions/quickCreate/frameGraphs.d.ts +14 -0
  152. package/lib/extensions/quickCreate/lights.d.ts +14 -0
  153. package/lib/extensions/quickCreate/materials.d.ts +14 -0
  154. package/lib/extensions/quickCreate/meshes.d.ts +10 -0
  155. package/lib/extensions/quickCreate/particles.d.ts +15 -0
  156. package/lib/extensions/quickCreate/quickCreateLayout.d.ts +44 -0
  157. package/lib/extensions/quickCreate/quickCreateToolsService.d.ts +11 -0
  158. package/lib/extensions/quickCreate/renderingPipelines.d.ts +14 -0
  159. package/lib/extensions/quickCreate/settingsPopover.d.ts +14 -0
  160. package/lib/extensions/quickCreate/spriteManagers.d.ts +14 -0
  161. package/lib/{extensionsListService-zYdKn8uM.js → extensionsListService-eRZtqcfj.js} +8 -8
  162. package/lib/extensionsListService-eRZtqcfj.js.map +1 -0
  163. package/lib/hooks/compoundPropertyHooks.d.ts +24 -0
  164. package/lib/hooks/instrumentationHooks.d.ts +9 -0
  165. package/lib/hooks/observableHooks.d.ts +35 -0
  166. package/lib/hooks/pollingHooks.d.ts +7 -0
  167. package/lib/hooks/resourceHooks.d.ts +20 -0
  168. package/lib/hooks/settingsHooks.d.ts +13 -0
  169. package/lib/hooks/teachingMomentHooks.d.ts +34 -0
  170. package/lib/hooks/themeHooks.d.ts +17 -0
  171. package/lib/hooks/useObservableArray.d.ts +11 -0
  172. package/lib/hooks/useResizeHandle.d.ts +35 -0
  173. package/lib/{index-DuVF9zYN.js → index-FWuITINA.js} +1932 -1031
  174. package/lib/index-FWuITINA.js.map +1 -0
  175. package/lib/index.d.ts +111 -74767
  176. package/lib/index.js +6 -6
  177. package/lib/inspectable.d.ts +67 -0
  178. package/lib/inspector.d.ts +41 -0
  179. package/lib/instrumentation/functionInstrumentation.d.ts +20 -0
  180. package/lib/instrumentation/propertyInstrumentation.d.ts +33 -0
  181. package/lib/legacy/debugLayer.d.ts +24 -0
  182. package/lib/legacy/inspectableCustomPropertiesService.d.ts +3 -0
  183. package/lib/legacy/inspector.d.ts +39 -0
  184. package/lib/legacy/legacy.d.ts +9 -0
  185. package/lib/legacy/propertiesSectionMapping.d.ts +99 -0
  186. package/lib/misc/arrayUtils.d.ts +4 -0
  187. package/lib/misc/assert.d.ts +5 -0
  188. package/lib/misc/defaultPerfStrategies.d.ts +16 -0
  189. package/lib/misc/graphUtils.d.ts +44 -0
  190. package/lib/misc/nodeGeometryEditor.d.ts +3 -0
  191. package/lib/misc/nodeMaterialEditor.d.ts +2 -0
  192. package/lib/misc/nodeParticleEditor.d.ts +2 -0
  193. package/lib/misc/nodeRenderGraphEditor.d.ts +2 -0
  194. package/lib/misc/observableCollection.d.ts +23 -0
  195. package/lib/misc/snippetUtils.d.ts +74 -0
  196. package/lib/misc/textureTools.d.ts +34 -0
  197. package/lib/modularTool.d.ts +42 -0
  198. package/lib/modularity/serviceContainer.d.ts +64 -0
  199. package/lib/modularity/serviceDefinition.d.ts +64 -0
  200. package/lib/{quickCreateToolsService-BtsSLeMY.js → quickCreateToolsService-MzZbVrvr.js} +7 -7
  201. package/lib/quickCreateToolsService-MzZbVrvr.js.map +1 -0
  202. package/lib/{reflectorService-Bzs-Ykos.js → reflectorService-DdPEZLjO.js} +7 -7
  203. package/lib/reflectorService-DdPEZLjO.js.map +1 -0
  204. package/lib/services/cli/cliConnectionStatus.d.ts +19 -0
  205. package/lib/services/cli/entityQueryService.d.ts +8 -0
  206. package/lib/services/cli/inspectableBridgeService.d.ts +22 -0
  207. package/lib/services/cli/inspectableCommandRegistry.d.ts +58 -0
  208. package/lib/services/cli/perfTraceCommandService.d.ts +9 -0
  209. package/lib/services/cli/screenshotCommandService.d.ts +8 -0
  210. package/lib/services/cli/shaderCommandService.d.ts +7 -0
  211. package/lib/services/cli/statsCommandService.d.ts +9 -0
  212. package/lib/services/cliConnectionStatusService.d.ts +4 -0
  213. package/lib/services/defaultToolbarMetadata.d.ts +6 -0
  214. package/lib/services/extensionsListService.d.ts +3 -0
  215. package/lib/services/gizmoService.d.ts +84 -0
  216. package/lib/services/gizmoToolbarService.d.ts +5 -0
  217. package/lib/services/globalSettings.d.ts +5 -0
  218. package/lib/services/highlightService.d.ts +8 -0
  219. package/lib/services/miniStatsService.d.ts +4 -0
  220. package/lib/services/panes/debugService.d.ts +25 -0
  221. package/lib/services/panes/properties/animationGroupPropertiesService.d.ts +4 -0
  222. package/lib/services/panes/properties/animationPropertiesService.d.ts +5 -0
  223. package/lib/services/panes/properties/atmospherePropertiesService.d.ts +5 -0
  224. package/lib/services/panes/properties/audioPropertiesService.d.ts +3 -0
  225. package/lib/services/panes/properties/cameraPropertiesService.d.ts +7 -0
  226. package/lib/services/panes/properties/commonPropertiesService.d.ts +3 -0
  227. package/lib/services/panes/properties/effectLayerPropertiesService.d.ts +3 -0
  228. package/lib/services/panes/properties/frameGraphPropertiesService.d.ts +3 -0
  229. package/lib/services/panes/properties/lightPropertiesServices.d.ts +4 -0
  230. package/lib/services/panes/properties/materialPropertiesService.d.ts +4 -0
  231. package/lib/services/panes/properties/metadataPropertiesService.d.ts +3 -0
  232. package/lib/services/panes/properties/nodePropertiesService.d.ts +4 -0
  233. package/lib/services/panes/properties/particleSystemPropertiesService.d.ts +4 -0
  234. package/lib/services/panes/properties/physicsPropertiesService.d.ts +3 -0
  235. package/lib/services/panes/properties/postProcessPropertiesService.d.ts +3 -0
  236. package/lib/services/panes/properties/propertiesService.d.ts +53 -0
  237. package/lib/services/panes/properties/renderingPipelinePropertiesService.d.ts +3 -0
  238. package/lib/services/panes/properties/scenePropertiesService.d.ts +4 -0
  239. package/lib/services/panes/properties/skeletonPropertiesService.d.ts +4 -0
  240. package/lib/services/panes/properties/spritePropertiesService.d.ts +4 -0
  241. package/lib/services/panes/properties/texturePropertiesService.d.ts +4 -0
  242. package/lib/services/panes/properties/transformPropertiesService.d.ts +3 -0
  243. package/lib/services/panes/scene/animationGroupExplorerService.d.ts +5 -0
  244. package/lib/services/panes/scene/atmosphereExplorerService.d.ts +5 -0
  245. package/lib/services/panes/scene/defaultSectionsMetadata.d.ts +33 -0
  246. package/lib/services/panes/scene/disposableCommandService.d.ts +4 -0
  247. package/lib/services/panes/scene/effectLayersExplorerService.d.ts +5 -0
  248. package/lib/services/panes/scene/frameGraphExplorerService.d.ts +5 -0
  249. package/lib/services/panes/scene/guiExplorerService.d.ts +5 -0
  250. package/lib/services/panes/scene/materialExplorerService.d.ts +5 -0
  251. package/lib/services/panes/scene/nodeExplorerService.d.ts +7 -0
  252. package/lib/services/panes/scene/particleSystemExplorerService.d.ts +5 -0
  253. package/lib/services/panes/scene/postProcessExplorerService.d.ts +5 -0
  254. package/lib/services/panes/scene/renderingPipelinesExplorerService.d.ts +5 -0
  255. package/lib/services/panes/scene/sceneExplorerService.d.ts +34 -0
  256. package/lib/services/panes/scene/skeletonExplorerService.d.ts +5 -0
  257. package/lib/services/panes/scene/soundExplorerService.d.ts +5 -0
  258. package/lib/services/panes/scene/spriteManagerExplorerService.d.ts +6 -0
  259. package/lib/services/panes/scene/texturesExplorerService.d.ts +5 -0
  260. package/lib/services/panes/settingsService.d.ts +25 -0
  261. package/lib/services/panes/statsService.d.ts +28 -0
  262. package/lib/services/panes/tools/captureService.d.ts +3 -0
  263. package/lib/services/panes/tools/exportService.d.ts +3 -0
  264. package/lib/services/panes/tools/import/gltfAnimationImportService.d.ts +3 -0
  265. package/lib/services/panes/tools/import/gltfLoaderOptionsDefaults.d.ts +46 -0
  266. package/lib/services/panes/tools/import/gltfLoaderOptionsService.d.ts +4 -0
  267. package/lib/services/panes/tools/import/gltfValidationService.d.ts +3 -0
  268. package/lib/services/panes/tools/reflectorService.d.ts +7 -0
  269. package/lib/services/panes/toolsService.d.ts +29 -0
  270. package/lib/services/pickingService.d.ts +7 -0
  271. package/lib/services/reactContextService.d.ts +18 -0
  272. package/lib/services/sceneContext.d.ts +19 -0
  273. package/lib/services/selectionService.d.ts +24 -0
  274. package/lib/services/settingsStore.d.ts +55 -0
  275. package/lib/services/shellService.d.ts +256 -0
  276. package/lib/services/shellSettingsService.d.ts +3 -0
  277. package/lib/services/textureEditor/textureEditorService.d.ts +21 -0
  278. package/lib/services/textureEditor/tools/contrast.d.ts +2 -0
  279. package/lib/services/textureEditor/tools/eyedropper.d.ts +5 -0
  280. package/lib/services/textureEditor/tools/floodfill.d.ts +5 -0
  281. package/lib/services/textureEditor/tools/paintbrush.d.ts +2 -0
  282. package/lib/services/textureEditor/tools/rectangularSelect.d.ts +5 -0
  283. package/lib/services/themeSelectorService.d.ts +3 -0
  284. package/lib/services/themeService.d.ts +60 -0
  285. package/lib/services/userFeedbackService.d.ts +3 -0
  286. package/lib/services/watcherService.d.ts +38 -0
  287. package/lib/themes/babylonTheme.d.ts +3 -0
  288. package/package.json +12 -5
  289. package/readme.md +27 -0
  290. package/lib/extensionsListService-zYdKn8uM.js.map +0 -1
  291. package/lib/index-DuVF9zYN.js.map +0 -1
  292. package/lib/quickCreateToolsService-BtsSLeMY.js.map +0 -1
  293. package/lib/reflectorService-Bzs-Ykos.js.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
- import { createContext, forwardRef, useContext, useState, useCallback, Component, useMemo, useEffect, useRef, useReducer, Children, isValidElement, useLayoutEffect, cloneElement, useImperativeHandle, createElement, Suspense, memo, Fragment as Fragment$1, lazy } from 'react';
3
- import { tokens, makeStyles, Tooltip as Tooltip$1, Button as Button$1, Spinner, Link as Link$1, Caption1, Body1, useFluent, Accordion as Accordion$1, AccordionHeader, Subtitle2Stronger, AccordionPanel, Divider, MessageBar as MessageBar$1, MessageBarBody, AccordionItem, SearchBox as SearchBox$1, Portal, ToggleButton as ToggleButton$1, InfoLabel as InfoLabel$1, Body1Strong, mergeClasses, useId, useToastController, Toast, ToastTitle, FluentProvider, Toaster, Checkbox as Checkbox$1, createLightTheme, createDarkTheme, TeachingPopover, TeachingPopoverSurface, TeachingPopoverHeader, TeachingPopoverBody, Switch as Switch$1, createDOMRenderer, RendererProvider, Menu, MenuTrigger, SplitButton, MenuPopover, MenuList, MenuItem, Toolbar as Toolbar$1, ToolbarRadioButton, MenuGroup, MenuGroupHeader, treeItemLevelToken, FlatTree, FlatTreeItem, TreeItemLayout, MenuDivider, MenuItemCheckbox, useMergedRefs, Input, Dropdown as Dropdown$1, Option, Popover as Popover$1, PopoverTrigger, PopoverSurface, ColorPicker, ColorArea, ColorSlider, AlphaSlider, ColorSwatch, PresenceBadge, Slider as Slider$1, MenuItemRadio, Dialog, DialogSurface, DialogBody, DialogTitle, DialogContent, DialogActions, List as List$1, ListItem, Badge, Label, MessageBarTitle, useComboboxFilter, Combobox, Subtitle2, Textarea as Textarea$1, ToolbarButton, ToolbarDivider, Field } from '@fluentui/react-components';
4
- import { ErrorCircleRegular, EyeFilled, EyeOffRegular, CheckmarkFilled, EditRegular, FilterRegular, PinFilled, PinRegular, ArrowCircleUpRegular, ChevronCircleRight16Regular, ChevronCircleRight20Regular, ChevronCircleDown16Regular, ChevronCircleDown20Regular, Copy16Regular, CopyRegular, PanelLeftExpandRegular, PanelRightExpandRegular, PanelLeftContractRegular, PanelRightContractRegular, PictureInPictureEnterRegular, MoreHorizontalRegular, LayoutColumnTwoFocusLeftFilled, LayoutColumnTwoSplitLeftFocusTopLeftFilled, LayoutColumnTwoSplitLeftFocusBottomLeftFilled, LayoutColumnTwoFocusRightFilled, LayoutColumnTwoSplitRightFocusTopRightFilled, LayoutColumnTwoSplitRightFocusBottomRightFilled, SettingsRegular, DocumentTextRegular, createFluentIcon, TextSortAscendingRegular, GlobeRegular, ArrowExpandAllRegular, ArrowCollapseAllRegular, CubeTreeRegular, BugRegular, ArrowUploadRegular, ArrowBidirectionalUpDownFilled, ArrowDownloadRegular, StopRegular, RecordRegular, DataBarHorizontalRegular, WrenchRegular, ArrowClockwiseRegular, WeatherSunnyRegular, WeatherMoonRegular, ArrowRotateClockwiseRegular, ArrowExpandRegular, SelectObjectRegular, CubeRegular, CameraRegular, AddRegular, DeleteRegular, FullScreenMaximizeRegular, ChevronDownRegular, ChevronRightRegular, CircleSmallFilled, SaveRegular, PreviousRegular, ArrowPreviousRegular, TriangleLeftRegular, RecordStopRegular, PlayRegular, ArrowNextRegular, NextRegular, PauseRegular, LinkDismissRegular, LinkEditRegular, ArrowUndoRegular, BracesRegular, BracesDismiss16Regular, EyeRegular, CloudArrowUpRegular, CloudArrowDownRegular, EyeOffFilled, ArrowMoveFilled, StopFilled, PlayFilled, LockOpenRegular, LockClosedRegular, ResizeRegular, ChevronUpRegular, ArrowResetRegular, CircleHalfFillRegular, EyedropperRegular, PaintBucketRegular, InkStrokeRegular, StackRegular, FilmstripRegular, PauseFilled, WeatherSunnyLowFilled, LayerRegular, FrameRegular, AppGenericRegular, RectangleLandscapeRegular, BorderOutsideRegular, BorderNoneRegular, MyLocationRegular, BubbleMultipleRegular, LightbulbRegular, VideoFilled, VideoRegular, FlashlightRegular, FlashlightOffRegular, DropRegular, BlurRegular, PipelineRegular, PersonWalkingRegular, DataLineRegular, SoundWaveCircleRegular, PersonSquareRegular, LayerDiagonalPersonRegular, ImageEditRegular, ImageRegular, TargetRegular, PersonFeedbackRegular, BranchRegular, DeleteFilled } from '@fluentui/react-icons';
2
+ import { createContext, forwardRef, useContext, useState, useCallback, Component, useMemo, useEffect, useRef, useReducer, Children, isValidElement, useLayoutEffect, useImperativeHandle, cloneElement, createElement, Suspense, memo, Fragment as Fragment$1, lazy } from 'react';
3
+ import { tokens, makeStyles, Tooltip as Tooltip$1, Button as Button$1, Spinner, Link as Link$1, Caption1, Body1, useFluent, Accordion as Accordion$1, AccordionHeader, Subtitle2Stronger, AccordionPanel, Divider, MessageBar as MessageBar$1, MessageBarBody, AccordionItem, SearchBox as SearchBox$1, Portal, ToggleButton as ToggleButton$1, InfoLabel as InfoLabel$1, Body1Strong, mergeClasses, useId, useToastController, Toast, ToastTitle, FluentProvider, Toaster, Checkbox as Checkbox$1, createLightTheme, createDarkTheme, TeachingPopover, TeachingPopoverSurface, TeachingPopoverHeader, TeachingPopoverBody, createDOMRenderer, RendererProvider, Menu, MenuTrigger, SplitButton, MenuPopover, MenuList, MenuItem, Toolbar as Toolbar$1, ToolbarRadioButton, MenuGroup, MenuGroupHeader, Switch as Switch$1, treeItemLevelToken, FlatTree, FlatTreeItem, TreeItemLayout, MenuDivider, MenuItemCheckbox, useMergedRefs, Input, Dropdown as Dropdown$1, Option, Popover as Popover$1, PopoverTrigger, PopoverSurface, ColorPicker, ColorArea, ColorSlider, AlphaSlider, ColorSwatch, PresenceBadge, Slider as Slider$1, MenuItemRadio, Dialog, DialogSurface, DialogBody, DialogTitle, DialogContent, DialogActions, List as List$1, ListItem, Badge, Label, MessageBarTitle, useComboboxFilter, Combobox, Subtitle2, Textarea as Textarea$1, ToolbarButton, ToolbarDivider, Field } from '@fluentui/react-components';
4
+ import { ErrorCircleRegular, EyeFilled, EyeOffRegular, CheckmarkFilled, EditRegular, FilterRegular, PinFilled, PinRegular, ArrowCircleUpRegular, ChevronCircleRight16Regular, ChevronCircleRight20Regular, ChevronCircleDown16Regular, ChevronCircleDown20Regular, Copy16Regular, CopyRegular, PanelLeftExpandRegular, PanelRightExpandRegular, PanelLeftContractRegular, PanelRightContractRegular, PictureInPictureEnterRegular, MoreHorizontalRegular, LayoutColumnTwoFocusLeftFilled, LayoutColumnTwoSplitLeftFocusTopLeftFilled, LayoutColumnTwoSplitLeftFocusBottomLeftFilled, LayoutColumnTwoFocusRightFilled, LayoutColumnTwoSplitRightFocusTopRightFilled, LayoutColumnTwoSplitRightFocusBottomRightFilled, SettingsRegular, DocumentTextRegular, createFluentIcon, TextSortAscendingRegular, GlobeRegular, ArrowExpandAllRegular, ArrowCollapseAllRegular, CubeTreeRegular, BugRegular, ArrowUploadRegular, ArrowBidirectionalUpDownFilled, ArrowDownloadRegular, StopRegular, RecordRegular, DataBarHorizontalRegular, WrenchRegular, ArrowClockwiseRegular, WeatherSunnyRegular, WeatherMoonRegular, PlugConnectedRegular, PlugDisconnectedRegular, ArrowRotateClockwiseRegular, ArrowExpandRegular, SelectObjectRegular, CubeRegular, CameraRegular, AddRegular, DeleteRegular, FullScreenMaximizeRegular, ChevronDownRegular, ChevronRightRegular, CircleSmallFilled, SaveRegular, PreviousRegular, ArrowPreviousRegular, TriangleLeftRegular, RecordStopRegular, PlayRegular, ArrowNextRegular, NextRegular, PauseRegular, LinkDismissRegular, LinkEditRegular, ArrowUndoRegular, BracesRegular, BracesDismiss16Regular, EyeRegular, CloudArrowUpRegular, CloudArrowDownRegular, EyeOffFilled, ArrowMoveFilled, StopFilled, PlayFilled, LockOpenRegular, LockClosedRegular, ResizeRegular, ChevronUpRegular, ArrowResetRegular, CircleHalfFillRegular, EyedropperRegular, PaintBucketRegular, InkStrokeRegular, StackRegular, FilmstripRegular, PauseFilled, WeatherSunnyLowFilled, LayerRegular, FrameRegular, AppGenericRegular, RectangleLandscapeRegular, BorderOutsideRegular, BorderNoneRegular, MyLocationRegular, BubbleMultipleRegular, LightbulbRegular, VideoFilled, VideoRegular, FlashlightRegular, FlashlightOffRegular, DropRegular, BlurRegular, PipelineRegular, PersonWalkingRegular, DataLineRegular, SoundWaveCircleRegular, PersonSquareRegular, LayerDiagonalPersonRegular, ImageEditRegular, ImageRegular, TargetRegular, PersonFeedbackRegular, BranchRegular, DeleteFilled } from '@fluentui/react-icons';
5
5
  import { Color3, Color4 } from '@babylonjs/core/Maths/math.color.js';
6
6
  import { Vector3, Quaternion, Matrix, Vector2, Vector4, TmpVectors } from '@babylonjs/core/Maths/math.vector.js';
7
7
  import { Observable } from '@babylonjs/core/Misc/observable.js';
@@ -45,6 +45,7 @@ import { Light } from '@babylonjs/core/Lights/light.js';
45
45
  import { AbstractMesh } from '@babylonjs/core/Meshes/abstractMesh.js';
46
46
  import { Node as Node$1 } from '@babylonjs/core/node.js';
47
47
  import { createRoot } from 'react-dom/client';
48
+ import { CreateScreenshotUsingRenderTargetAsync } from '@babylonjs/core/Misc/screenshotTools.js';
48
49
  import { SelectionOutlineLayer } from '@babylonjs/core/Layers/selectionOutlineLayer.js';
49
50
  import { GaussianSplattingMesh } from '@babylonjs/core/Meshes/GaussianSplatting/gaussianSplattingMesh.js';
50
51
  import { AnimationGroup, TargetedAnimation } from '@babylonjs/core/Animations/animationGroup.js';
@@ -123,22 +124,21 @@ import { Skeleton } from '@babylonjs/core/Bones/skeleton.js';
123
124
  import { Sprite } from '@babylonjs/core/Sprites/sprite.js';
124
125
  import { SpriteManager } from '@babylonjs/core/Sprites/spriteManager.js';
125
126
  import { GetTextureDataAsync, WhenTextureReadyAsync } from '@babylonjs/core/Misc/textureTools.js';
126
- import { BaseTexture } from '@babylonjs/core/Materials/Textures/baseTexture.js';
127
- import { MultiRenderTarget } from '@babylonjs/core/Materials/Textures/multiRenderTarget.js';
128
- import { RenderTargetTexture } from '@babylonjs/core/Materials/Textures/renderTargetTexture.js';
129
- import { ThinTexture } from '@babylonjs/core/Materials/Textures/thinTexture.js';
130
127
  import { KeyboardEventTypes } from '@babylonjs/core/Events/keyboardEvents.js';
131
128
  import { PointerEventTypes } from '@babylonjs/core/Events/pointerEvents.js';
132
129
  import { HtmlElementTexture } from '@babylonjs/core/Materials/Textures/htmlElementTexture.js';
133
130
  import { ShaderMaterial } from '@babylonjs/core/Materials/shaderMaterial.js';
134
131
  import { CreatePlane } from '@babylonjs/core/Meshes/Builders/planeBuilder.js';
132
+ import { BaseTexture } from '@babylonjs/core/Materials/Textures/baseTexture.js';
133
+ import { MultiRenderTarget } from '@babylonjs/core/Materials/Textures/multiRenderTarget.js';
134
+ import { RenderTargetTexture } from '@babylonjs/core/Materials/Textures/renderTargetTexture.js';
135
+ import { ThinTexture } from '@babylonjs/core/Materials/Textures/thinTexture.js';
135
136
  import '@babylonjs/core/Rendering/boundingBoxRenderer.js';
136
137
  import '@babylonjs/core/PostProcesses/RenderPipeline/postProcessRenderPipelineManagerSceneComponent.js';
137
138
  import '@babylonjs/core/Sprites/spriteSceneComponent.js';
138
139
  import { DynamicTexture } from '@babylonjs/core/Materials/Textures/dynamicTexture.js';
139
140
  import { captureEquirectangularFromScene } from '@babylonjs/core/Misc/equirectangularCapture.js';
140
141
  import { SceneRecorder } from '@babylonjs/core/Misc/sceneRecorder.js';
141
- import { CreateScreenshotUsingRenderTargetAsync } from '@babylonjs/core/Misc/screenshotTools.js';
142
142
  import { VideoRecorder } from '@babylonjs/core/Misc/videoRecorder.js';
143
143
  import { SceneSerializer } from '@babylonjs/core/Misc/sceneSerializer.js';
144
144
  import { EnvironmentTextureTools } from '@babylonjs/core/Misc/environmentTextureTools.js';
@@ -1743,13 +1743,18 @@ const InfoLabel = (props) => {
1743
1743
  };
1744
1744
 
1745
1745
  const ToastContext = createContext({ showToast: () => { } });
1746
- const ToastProvider = ({ children }) => {
1746
+ /**
1747
+ * Provides toast notification functionality to child components via context and an optional imperative ref.
1748
+ * @returns The toast provider component tree.
1749
+ */
1750
+ const ToastProvider = ({ children, imperativeRef }) => {
1747
1751
  const toasterId = useId("toaster");
1748
1752
  const { dispatchToast } = useToastController(toasterId);
1749
1753
  const { targetDocument } = useFluent();
1750
- const showToast = useCallback((message) => {
1751
- dispatchToast(jsx(Toast, { children: jsx(ToastTitle, { children: message }) }), { intent: "info", timeout: 2000 });
1754
+ const showToast = useCallback((message, options) => {
1755
+ dispatchToast(jsx(Toast, { children: jsx(ToastTitle, { children: message }) }), { intent: options?.intent ?? "info", timeout: 2000 });
1752
1756
  }, [dispatchToast]);
1757
+ useImperativeHandle(imperativeRef, () => ({ showToast }), [showToast]);
1753
1758
  return (jsxs(ToastContext.Provider, { value: { showToast }, children: [children, jsx(FluentProvider, { applyStylesToPortals: true, targetDocument: targetDocument, children: jsx(Toaster, { toasterId: toasterId, position: "bottom" }) })] }));
1754
1759
  };
1755
1760
  /**
@@ -1845,9 +1850,7 @@ const PropertyLine = forwardRef((props, ref) => {
1845
1850
  defaultValue: undefined, // Don't pass defaultValue to children as there is no guarantee how this will be used and we can't mix controlled + uncontrolled state
1846
1851
  })
1847
1852
  : children;
1848
- return (jsxs(LineContainer, { ref: ref, uniqueId: uniqueId ?? label, label: label, children: [jsxs("div", { className: classes.baseLine, children: [jsx(InfoLabel, { className: classes.infoLabel, htmlFor: "property", info: description, label: label, flexLabel: true, onContextMenu: onCopy ? handleContextMenu : undefined }), jsxs("div", { className: classes.rightContent, id: "property", children: [expandedContent && (jsx(ToggleButton, { title: "Expand/Collapse property", appearance: "transparent", checkedIcon: size === "small" ? ChevronCircleDown16Regular : ChevronCircleDown20Regular, uncheckedIcon: size === "small" ? ChevronCircleRight16Regular : ChevronCircleRight20Regular, value: expanded === true, onChange: setExpanded })), nullable && !ignoreNullable && (
1849
- // If this is a nullableProperty and ignoreNullable was not sent, display a checkbox used to toggle null ('checked' means 'non null')
1850
- jsx(Tooltip, { content: props.value == null ? "Enable property" : "Disable property (set to null)", children: jsx(Checkbox$1, { className: classes.checkbox, indicator: { className: classes.checkboxIndicator }, checked: !(props.value == null), onChange: (_, data) => {
1853
+ return (jsxs(LineContainer, { ref: ref, uniqueId: uniqueId ?? label, label: label, children: [jsxs("div", { className: classes.baseLine, children: [jsx(InfoLabel, { className: classes.infoLabel, htmlFor: "property", info: description, label: label, flexLabel: true, onContextMenu: onCopy ? handleContextMenu : undefined }), jsxs("div", { className: classes.rightContent, id: "property", children: [expandedContent && (jsx(ToggleButton, { title: "Expand/Collapse property", appearance: "transparent", checkedIcon: size === "small" ? ChevronCircleDown16Regular : ChevronCircleDown20Regular, uncheckedIcon: size === "small" ? ChevronCircleRight16Regular : ChevronCircleRight20Regular, value: expanded === true, onChange: setExpanded })), nullable && !ignoreNullable && (jsx(Tooltip, { content: props.value == null ? "Enable property" : "Disable property (set to null)", children: jsx(Checkbox$1, { className: classes.checkbox, indicator: { className: classes.checkboxIndicator }, checked: !(props.value == null), onChange: (_, data) => {
1851
1854
  if (data.checked) {
1852
1855
  // if checked this means we are returning to non-null, use cached value if exists. If no cached value, use default value
1853
1856
  cachedVal.current != null ? props.onChange(cachedVal.current) : props.onChange(props.defaultValue);
@@ -2191,6 +2194,39 @@ const SidePaneContainer = forwardRef((props, ref) => {
2191
2194
  return (jsx("div", { className: mergeClasses(classes.paneRootDiv, className), ref: ref, ...rest, children: props.children }));
2192
2195
  });
2193
2196
 
2197
+ /**
2198
+ * The unique identity symbol for the settings store service.
2199
+ */
2200
+ const SettingsStoreIdentity = Symbol("SettingsStore");
2201
+ function GetKey(namespace, settingKey) {
2202
+ return `Babylon/${namespace}/${settingKey}`;
2203
+ }
2204
+ /**
2205
+ * Default implementation of {@link ISettingsStore} that persists settings using browser local storage.
2206
+ */
2207
+ class SettingsStore {
2208
+ /**
2209
+ * Creates a new settings store.
2210
+ * @param _namespace A namespace used to scope the settings keys to avoid collisions with other stores.
2211
+ */
2212
+ constructor(_namespace) {
2213
+ this._namespace = _namespace;
2214
+ this._onChanged = new Observable();
2215
+ }
2216
+ get onChanged() {
2217
+ return this._onChanged;
2218
+ }
2219
+ readSetting(descriptor) {
2220
+ const key = GetKey(this._namespace, descriptor.key);
2221
+ return DataStorage.ReadJson(key, descriptor.defaultValue);
2222
+ }
2223
+ writeSetting(descriptor, value) {
2224
+ const key = GetKey(this._namespace, descriptor.key);
2225
+ DataStorage.WriteJson(key, value);
2226
+ this._onChanged.notifyObservers(descriptor.key);
2227
+ }
2228
+ }
2229
+
2194
2230
  // Generated from https://react.fluentui.dev/?path=/docs/theme-theme-designer--docs
2195
2231
  // Key color: #3A94FC
2196
2232
  /* eslint-disable @typescript-eslint/naming-convention */
@@ -2226,39 +2262,6 @@ const DarkTheme = {
2226
2262
  colorNeutralForeground1: BaseDarkTheme.colorNeutralForeground2,
2227
2263
  };
2228
2264
 
2229
- /**
2230
- * The unique identity symbol for the settings store service.
2231
- */
2232
- const SettingsStoreIdentity = Symbol("SettingsStore");
2233
- function GetKey(namespace, settingKey) {
2234
- return `Babylon/${namespace}/${settingKey}`;
2235
- }
2236
- /**
2237
- * Default implementation of {@link ISettingsStore} that persists settings using browser local storage.
2238
- */
2239
- class SettingsStore {
2240
- /**
2241
- * Creates a new settings store.
2242
- * @param _namespace A namespace used to scope the settings keys to avoid collisions with other stores.
2243
- */
2244
- constructor(_namespace) {
2245
- this._namespace = _namespace;
2246
- this._onChanged = new Observable();
2247
- }
2248
- get onChanged() {
2249
- return this._onChanged;
2250
- }
2251
- readSetting(descriptor) {
2252
- const key = GetKey(this._namespace, descriptor.key);
2253
- return DataStorage.ReadJson(key, descriptor.defaultValue);
2254
- }
2255
- writeSetting(descriptor, value) {
2256
- const key = GetKey(this._namespace, descriptor.key);
2257
- DataStorage.WriteJson(key, value);
2258
- this._onChanged.notifyObservers(descriptor.key);
2259
- }
2260
- }
2261
-
2262
2265
  /**
2263
2266
  * The setting descriptor for persisting the theme mode preference.
2264
2267
  */
@@ -2673,83 +2676,6 @@ function ConstructorFactory(constructor) {
2673
2676
  return (...args) => new constructor(...args);
2674
2677
  }
2675
2678
 
2676
- /**
2677
- * A hook that provides a transient state value and a "pulse" function to set it.
2678
- * The transient value is meant to be consumed immediately after being set, and will be cleared on the next render.
2679
- * @typeParam T The type of the transient value.
2680
- * @returns A tuple containing the transient value and a function to "pulse" the state.
2681
- */
2682
- function useImpulse() {
2683
- const impulseRef = useRef(undefined);
2684
- const [, setVersion] = useState(0);
2685
- const pulse = useCallback((value) => {
2686
- impulseRef.current = value;
2687
- setVersion((v) => v + 1);
2688
- }, []);
2689
- // Consume the impulse value and clear it
2690
- const value = impulseRef.current;
2691
- impulseRef.current = undefined;
2692
- return [value, pulse];
2693
- }
2694
-
2695
- const useStyles$S = makeStyles({
2696
- placeholderDiv: {
2697
- padding: `${tokens.spacingVerticalM} ${tokens.spacingHorizontalM}`,
2698
- },
2699
- });
2700
- const PropertiesPane = (props) => {
2701
- const classes = useStyles$S();
2702
- const entity = props.context;
2703
- return entity != null ? (jsx(ExtensibleAccordion, { ...props })) : (jsx("div", { className: classes.placeholderDiv, children: jsx(Body1Strong, { italic: true, children: "No entity selected." }) }));
2704
- };
2705
-
2706
- const useSwitchStyles = makeStyles({
2707
- switch: {
2708
- marginLeft: "auto",
2709
- },
2710
- switchSmall: {
2711
- transform: `scale(.85)`, // workaround since we cannot resize fluent switch
2712
- transformOrigin: "right",
2713
- },
2714
- indicator: {
2715
- margin: 0, // Remove the default right margin so the switch aligns well on the right side inside panels like the properties pane.
2716
- },
2717
- });
2718
- /**
2719
- * This is a primitive fluent boolean switch component whose only knowledge is the shared styling across all tools
2720
- * @param props
2721
- * @returns Switch component
2722
- */
2723
- const Switch = (props) => {
2724
- Switch.displayName = "Switch";
2725
- const { size } = useContext(ToolContext);
2726
- const classes = useSwitchStyles();
2727
- const [checked, setChecked] = useState(() => props.value ?? false);
2728
- useEffect(() => {
2729
- if (props.value != undefined) {
2730
- setChecked(props.value); // Update local state when props.checked changes
2731
- }
2732
- }, [props.value]);
2733
- const onChange = (event, _) => {
2734
- props.onChange && props.onChange(event.target.checked);
2735
- setChecked(event.target.checked);
2736
- };
2737
- return (jsx(Switch$1, { className: mergeClasses(classes.switch, size === "small" && classes.switchSmall), indicator: { className: classes.indicator }, checked: checked, disabled: props.disabled, onChange: onChange }));
2738
- };
2739
-
2740
- /**
2741
- * Wraps a switch in a property line
2742
- * @param props - The properties for the switch and property line
2743
- * @returns A React element representing the property line with a switch
2744
- */
2745
- const SwitchPropertyLine = (props) => {
2746
- SwitchPropertyLine.displayName = "SwitchPropertyLine";
2747
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2748
- const { label, ...switchProps } = props;
2749
- // Ensure the label gets passed to the PropertyLine component and not to the underlying switch
2750
- return (jsx(PropertyLine, { ...props, children: jsx(Switch, { ...switchProps }) }));
2751
- };
2752
-
2753
2679
  /**
2754
2680
  * The unique identity symbol for the scene context service.
2755
2681
  */
@@ -2898,9 +2824,7 @@ const ChildWindow = (props) => {
2898
2824
  return null;
2899
2825
  }
2900
2826
  const { mountNode, renderer } = windowState;
2901
- return (
2902
- // Portal targets the body of the child window.
2903
- jsx(Portal, { mountNode: mountNode, children: jsx(RendererProvider, { renderer: renderer, targetDocument: mountNode.ownerDocument, children: jsx(FluentProvider, { style: {
2827
+ return (jsx(Portal, { mountNode: mountNode, children: jsx(RendererProvider, { renderer: renderer, targetDocument: mountNode.ownerDocument, children: jsx(FluentProvider, { style: {
2904
2828
  display: "flex",
2905
2829
  flexGrow: 1,
2906
2830
  flexDirection: "column",
@@ -3024,7 +2948,7 @@ const RootComponentServiceIdentity = Symbol("RootComponent");
3024
2948
  * The unique identity symbol for the shell service.
3025
2949
  */
3026
2950
  const ShellServiceIdentity = Symbol("ShellService");
3027
- const useStyles$R = makeStyles({
2951
+ const useStyles$S = makeStyles({
3028
2952
  mainView: {
3029
2953
  flex: 1,
3030
2954
  display: "flex",
@@ -3237,14 +3161,14 @@ const DockMenu = (props) => {
3237
3161
  };
3238
3162
  const PaneHeader = (props) => {
3239
3163
  const { id, title, dockOptions } = props;
3240
- const classes = useStyles$R();
3164
+ const classes = useStyles$S();
3241
3165
  return (jsxs("div", { className: classes.paneHeaderDiv, children: [props.icon && (jsx("div", { className: classes.paneHeaderIcon, children: jsx(props.icon, {}) })), jsx(Subtitle2Stronger, { className: mergeClasses(classes.paneHeaderText, !props.icon && classes.paneHeaderTextNoIcon), children: title }), jsx(DockMenu, { sidePaneId: id, dockOptions: dockOptions, children: jsx(Button$1, { className: classes.paneHeaderButton, appearance: "transparent", icon: jsx(MoreHorizontalRegular, {}) }) })] }));
3242
3166
  };
3243
3167
  // This is a wrapper for an item in a toolbar that simply adds a teaching moment, which is useful for dynamically added items, possibly from extensions.
3244
3168
  const ToolbarItem = (props) => {
3245
3169
  // eslint-disable-next-line @typescript-eslint/naming-convention
3246
3170
  const { verticalLocation, horizontalLocation, id, component: Component, displayName } = props;
3247
- const classes = useStyles$R();
3171
+ const classes = useStyles$S();
3248
3172
  const useTeachingMoment = useMemo(() => MakePopoverTeachingMoment(`Bar/${verticalLocation}/${horizontalLocation}/${displayName ?? id}`), [displayName, id]);
3249
3173
  const teachingMoment = useTeachingMoment(props.teachingMoment === false);
3250
3174
  const title = typeof props.teachingMoment === "object" ? props.teachingMoment.title : (displayName ?? id);
@@ -3254,7 +3178,7 @@ const ToolbarItem = (props) => {
3254
3178
  // TODO: Handle overflow, possibly via https://react.fluentui.dev/?path=/docs/components-overflow--docs with priority.
3255
3179
  // This component just renders a toolbar with left aligned toolbar items on the left and right aligned toolbar items on the right.
3256
3180
  const Toolbar = ({ location, components }) => {
3257
- const classes = useStyles$R();
3181
+ const classes = useStyles$S();
3258
3182
  const leftComponents = useMemo(() => components.filter((entry) => entry.horizontalLocation === "left"), [components]);
3259
3183
  const rightComponents = useMemo(() => components.filter((entry) => entry.horizontalLocation === "right"), [components]);
3260
3184
  return (jsx(Fragment, { children: components.length > 0 && (jsxs("div", { className: `${classes.bar} ${location === "top" ? classes.barTop : classes.barBottom}`, children: [jsx("div", { className: classes.barLeft, children: leftComponents.map((entry) => (jsx(ToolbarItem, { verticalLocation: location, horizontalLocation: entry.horizontalLocation, id: entry.key, component: entry.component, displayName: entry.displayName, teachingMoment: entry.teachingMoment }, entry.key))) }), jsx("div", { className: classes.barRight, children: rightComponents.map((entry) => (jsx(ToolbarItem, { verticalLocation: location, horizontalLocation: entry.horizontalLocation, id: entry.key, component: entry.component, displayName: entry.displayName, teachingMoment: entry.teachingMoment }, entry.key))) })] })) }));
@@ -3264,7 +3188,7 @@ const SidePaneTab = (props) => {
3264
3188
  const { location, id, isSelected, isFirst, isLast, dockOptions,
3265
3189
  // eslint-disable-next-line @typescript-eslint/naming-convention
3266
3190
  icon: Icon, title, } = props;
3267
- const classes = useStyles$R();
3191
+ const classes = useStyles$S();
3268
3192
  const useTeachingMoment = useMemo(() => MakePopoverTeachingMoment(`Pane/${location}/${title ?? id}`), [title, id]);
3269
3193
  const teachingMoment = useTeachingMoment(props.teachingMoment === false);
3270
3194
  const tabClass = mergeClasses(classes.tab, isSelected ? classes.selectedTab : classes.unselectedTab, isFirst ? classes.firstTab : undefined, isLast ? classes.lastTab : undefined);
@@ -3276,7 +3200,7 @@ const SidePaneTab = (props) => {
3276
3200
  // In "compact" mode, the tab list is integrated into the pane itself.
3277
3201
  // In "full" mode, the returned tab list is later injected into the toolbar.
3278
3202
  function usePane(location, defaultWidth, minWidth, sidePanes, onSelectSidePane, dockOperations, toolbarMode, topBarItems, bottomBarItems, initialCollapsed) {
3279
- const classes = useStyles$R();
3203
+ const classes = useStyles$S();
3280
3204
  const [topSelectedTab, setTopSelectedTab] = useState();
3281
3205
  const [bottomSelectedTab, setBottomSelectedTab] = useState();
3282
3206
  const [collapsed, setCollapsed] = useState(initialCollapsed);
@@ -3473,7 +3397,7 @@ function MakeShellServiceDefinition({ leftPaneDefaultWidth = 350, leftPaneMinWid
3473
3397
  expand: () => onCollapseChanged.notifyObservers({ location: "right", collapsed: false }),
3474
3398
  };
3475
3399
  const rootComponent = () => {
3476
- const classes = useStyles$R();
3400
+ const classes = useStyles$S();
3477
3401
  const [sidePaneDockOverrides, setSidePaneDockOverrides] = useSetting(SidePaneDockOverridesSettingDescriptor);
3478
3402
  // This function returns a promise that resolves after the dock change takes effect so that
3479
3403
  // we can then select the re-docked pane.
@@ -3693,6 +3617,53 @@ function MakeShellServiceDefinition({ leftPaneDefaultWidth = 350, leftPaneMinWid
3693
3617
  };
3694
3618
  }
3695
3619
 
3620
+ const useSwitchStyles = makeStyles({
3621
+ switch: {
3622
+ marginLeft: "auto",
3623
+ },
3624
+ switchSmall: {
3625
+ transform: `scale(.85)`, // workaround since we cannot resize fluent switch
3626
+ transformOrigin: "right",
3627
+ },
3628
+ indicator: {
3629
+ margin: 0, // Remove the default right margin so the switch aligns well on the right side inside panels like the properties pane.
3630
+ },
3631
+ });
3632
+ /**
3633
+ * This is a primitive fluent boolean switch component whose only knowledge is the shared styling across all tools
3634
+ * @param props
3635
+ * @returns Switch component
3636
+ */
3637
+ const Switch = (props) => {
3638
+ Switch.displayName = "Switch";
3639
+ const { size } = useContext(ToolContext);
3640
+ const classes = useSwitchStyles();
3641
+ const [checked, setChecked] = useState(() => props.value ?? false);
3642
+ useEffect(() => {
3643
+ if (props.value != undefined) {
3644
+ setChecked(props.value); // Update local state when props.checked changes
3645
+ }
3646
+ }, [props.value]);
3647
+ const onChange = (event, _) => {
3648
+ props.onChange && props.onChange(event.target.checked);
3649
+ setChecked(event.target.checked);
3650
+ };
3651
+ return (jsx(Switch$1, { className: mergeClasses(classes.switch, size === "small" && classes.switchSmall), indicator: { className: classes.indicator }, checked: checked, disabled: props.disabled, onChange: onChange }));
3652
+ };
3653
+
3654
+ /**
3655
+ * Wraps a switch in a property line
3656
+ * @param props - The properties for the switch and property line
3657
+ * @returns A React element representing the property line with a switch
3658
+ */
3659
+ const SwitchPropertyLine = (props) => {
3660
+ SwitchPropertyLine.displayName = "SwitchPropertyLine";
3661
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
3662
+ const { label, ...switchProps } = props;
3663
+ // Ensure the label gets passed to the PropertyLine component and not to the underlying switch
3664
+ return (jsx(PropertyLine, { ...props, children: jsx(Switch, { ...switchProps }) }));
3665
+ };
3666
+
3696
3667
  /**
3697
3668
  * The unique identity symbol for the settings service.
3698
3669
  */
@@ -3802,6 +3773,36 @@ const SelectionServiceDefinition = {
3802
3773
  },
3803
3774
  };
3804
3775
 
3776
+ /**
3777
+ * A hook that provides a transient state value and a "pulse" function to set it.
3778
+ * The transient value is meant to be consumed immediately after being set, and will be cleared on the next render.
3779
+ * @typeParam T The type of the transient value.
3780
+ * @returns A tuple containing the transient value and a function to "pulse" the state.
3781
+ */
3782
+ function useImpulse() {
3783
+ const impulseRef = useRef(undefined);
3784
+ const [, setVersion] = useState(0);
3785
+ const pulse = useCallback((value) => {
3786
+ impulseRef.current = value;
3787
+ setVersion((v) => v + 1);
3788
+ }, []);
3789
+ // Consume the impulse value and clear it
3790
+ const value = impulseRef.current;
3791
+ impulseRef.current = undefined;
3792
+ return [value, pulse];
3793
+ }
3794
+
3795
+ const useStyles$R = makeStyles({
3796
+ placeholderDiv: {
3797
+ padding: `${tokens.spacingVerticalM} ${tokens.spacingHorizontalM}`,
3798
+ },
3799
+ });
3800
+ const PropertiesPane = (props) => {
3801
+ const classes = useStyles$R();
3802
+ const entity = props.context;
3803
+ return entity != null ? (jsx(ExtensibleAccordion, { ...props })) : (jsx("div", { className: classes.placeholderDiv, children: jsx(Body1Strong, { italic: true, children: "No entity selected." }) }));
3804
+ };
3805
+
3805
3806
  /**
3806
3807
  * The unique identity symbol for the properties service.
3807
3808
  */
@@ -3881,106 +3882,16 @@ const PropertiesServiceDefinition = {
3881
3882
  },
3882
3883
  };
3883
3884
 
3884
- /**
3885
- * Performs a topological sort on a graph.
3886
- * @param graph The set of nodes that make up the graph.
3887
- * @param getAdjacentNodes A function that returns the adjacent nodes for a given node.
3888
- * @param onSortedNode A function that is called for each node in the sorted order.
3889
- * @remarks
3890
- * This function allocates. Do not use it in the hot path. Instead use an instance of GraphUtils.
3891
- */
3892
- function SortGraph(graph, getAdjacentNodes, onSortedNode) {
3893
- const sorter = new GraphUtils();
3894
- sorter.sort(graph, getAdjacentNodes, onSortedNode);
3895
- }
3896
- /**
3897
- * Traverses a graph.
3898
- * @param graph The set of nodes that make up the graph.
3899
- * @param getAdjacentNodes A function that returns the adjacent nodes for a given node.
3900
- * @param onBeforeTraverse A function that is called before traversing each node.
3901
- * @param onAfterTraverse A function that is called after traversing each node.
3902
- * @remarks
3903
- * This function allocates. Do not use it in the hot path. Instead use an instance of GraphUtils.
3904
- */
3905
- function TraverseGraph(graph, getAdjacentNodes, onBeforeTraverse, onAfterTraverse) {
3906
- const traverser = new GraphUtils();
3907
- traverser.traverse(graph, getAdjacentNodes, onBeforeTraverse, onAfterTraverse);
3908
- }
3909
- /**
3910
- * A utility class for performing graph operations.
3911
- * @remarks
3912
- * The class allocates new objects, but each operation (e.g. sort, traverse) is allocation free. This is useful when used in the hot path.
3913
- */
3914
- class GraphUtils {
3915
- constructor() {
3916
- // Tracks three states:
3917
- // 1. No entry for the node - this means the node has not been encountered yet during any traversal
3918
- // 2. Entry with value false - this means the node is currently being traversed (needed to detect cycles)
3919
- // 3. Entry with value true - this means the node has already been fully traversed (and cycles were not detected)
3920
- this._traversalState = new Map();
3921
- this._isTraversing = false;
3922
- }
3923
- /**
3924
- * Performs a topological sort on a graph.
3925
- * @param graph The set of nodes that make up the graph.
3926
- * @param getAdjacentNodes A function that returns the adjacent nodes for a given node.
3927
- * @param onSortedNode A function that is called for each node in the sorted order.
3928
- */
3929
- sort(graph, getAdjacentNodes, onSortedNode) {
3930
- this.traverse(graph, getAdjacentNodes, undefined, onSortedNode);
3931
- }
3932
- /**
3933
- * Traverses a graph.
3934
- * @param graph The set of nodes that make up the graph.
3935
- * @param getAdjacentNodes A function that returns the adjacent nodes for a given node.
3936
- * @param onBeforeTraverse A function that is called before traversing each node.
3937
- * @param onAfterTraverse A function that is called after traversing each node.
3938
- */
3939
- traverse(graph, getAdjacentNodes, onBeforeTraverse, onAfterTraverse) {
3940
- // Since the traversal state is re-used, disallow re-entrancy through the getAdjacentNodes or onBeforeTraverse or onAfterTraverse callbacks.
3941
- if (this._isTraversing) {
3942
- throw new Error("This TopologicalSorter instance is already traversing.");
3943
- }
3944
- this._isTraversing = true;
3945
- try {
3946
- for (const node of graph) {
3947
- this._traverseCore(node, getAdjacentNodes, onBeforeTraverse, onAfterTraverse);
3948
- }
3949
- }
3950
- finally {
3951
- this._isTraversing = false;
3952
- this._traversalState.clear();
3953
- }
3954
- }
3955
- _traverseCore(node, getAdjacentNodes, onBeforeTraverse, onAfterTraverse) {
3956
- if (this._traversalState.get(node) !== true) {
3957
- if (this._traversalState.get(node) === false) {
3958
- throw new Error("Graph has cycle.");
3959
- }
3960
- this._traversalState.set(node, false);
3961
- onBeforeTraverse?.(node);
3962
- const adjacentNodes = getAdjacentNodes(node);
3963
- if (adjacentNodes) {
3964
- for (const adjacentNode of adjacentNodes) {
3965
- this._traverseCore(adjacentNode, getAdjacentNodes, onBeforeTraverse, onAfterTraverse);
3966
- }
3967
- }
3968
- this._traversalState.set(node, true);
3969
- onAfterTraverse?.(node);
3970
- }
3971
- }
3972
- }
3973
-
3974
- const NoOpSectionDropProps = {
3975
- onDragOver: () => { },
3976
- onDragLeave: () => { },
3977
- onDrop: () => { },
3978
- };
3979
- const NoOpDragProps = Object.assign({
3980
- draggable: false,
3981
- onDragStart: () => { },
3982
- onDragEnd: () => { },
3983
- }, NoOpSectionDropProps);
3885
+ const NoOpSectionDropProps = {
3886
+ onDragOver: () => { },
3887
+ onDragLeave: () => { },
3888
+ onDrop: () => { },
3889
+ };
3890
+ const NoOpDragProps = Object.assign({
3891
+ draggable: false,
3892
+ onDragStart: () => { },
3893
+ onDragEnd: () => { },
3894
+ }, NoOpSectionDropProps);
3984
3895
  /**
3985
3896
  * Hook that provides drag-drop functionality for the scene explorer.
3986
3897
  * Uses vanilla HTML5 drag and drop APIs.
@@ -4151,14 +4062,104 @@ function useSceneExplorerDragDrop(options) {
4151
4062
  };
4152
4063
  }
4153
4064
 
4154
- const SyntheticUniqueIds = new WeakMap();
4155
- function GetEntityId(entity) {
4065
+ /**
4066
+ * Performs a topological sort on a graph.
4067
+ * @param graph The set of nodes that make up the graph.
4068
+ * @param getAdjacentNodes A function that returns the adjacent nodes for a given node.
4069
+ * @param onSortedNode A function that is called for each node in the sorted order.
4070
+ * @remarks
4071
+ * This function allocates. Do not use it in the hot path. Instead use an instance of GraphUtils.
4072
+ */
4073
+ function SortGraph(graph, getAdjacentNodes, onSortedNode) {
4074
+ const sorter = new GraphUtils();
4075
+ sorter.sort(graph, getAdjacentNodes, onSortedNode);
4076
+ }
4077
+ /**
4078
+ * Traverses a graph.
4079
+ * @param graph The set of nodes that make up the graph.
4080
+ * @param getAdjacentNodes A function that returns the adjacent nodes for a given node.
4081
+ * @param onBeforeTraverse A function that is called before traversing each node.
4082
+ * @param onAfterTraverse A function that is called after traversing each node.
4083
+ * @remarks
4084
+ * This function allocates. Do not use it in the hot path. Instead use an instance of GraphUtils.
4085
+ */
4086
+ function TraverseGraph(graph, getAdjacentNodes, onBeforeTraverse, onAfterTraverse) {
4087
+ const traverser = new GraphUtils();
4088
+ traverser.traverse(graph, getAdjacentNodes, onBeforeTraverse, onAfterTraverse);
4089
+ }
4090
+ /**
4091
+ * A utility class for performing graph operations.
4092
+ * @remarks
4093
+ * The class allocates new objects, but each operation (e.g. sort, traverse) is allocation free. This is useful when used in the hot path.
4094
+ */
4095
+ class GraphUtils {
4096
+ constructor() {
4097
+ // Tracks three states:
4098
+ // 1. No entry for the node - this means the node has not been encountered yet during any traversal
4099
+ // 2. Entry with value false - this means the node is currently being traversed (needed to detect cycles)
4100
+ // 3. Entry with value true - this means the node has already been fully traversed (and cycles were not detected)
4101
+ this._traversalState = new Map();
4102
+ this._isTraversing = false;
4103
+ }
4104
+ /**
4105
+ * Performs a topological sort on a graph.
4106
+ * @param graph The set of nodes that make up the graph.
4107
+ * @param getAdjacentNodes A function that returns the adjacent nodes for a given node.
4108
+ * @param onSortedNode A function that is called for each node in the sorted order.
4109
+ */
4110
+ sort(graph, getAdjacentNodes, onSortedNode) {
4111
+ this.traverse(graph, getAdjacentNodes, undefined, onSortedNode);
4112
+ }
4113
+ /**
4114
+ * Traverses a graph.
4115
+ * @param graph The set of nodes that make up the graph.
4116
+ * @param getAdjacentNodes A function that returns the adjacent nodes for a given node.
4117
+ * @param onBeforeTraverse A function that is called before traversing each node.
4118
+ * @param onAfterTraverse A function that is called after traversing each node.
4119
+ */
4120
+ traverse(graph, getAdjacentNodes, onBeforeTraverse, onAfterTraverse) {
4121
+ // Since the traversal state is re-used, disallow re-entrancy through the getAdjacentNodes or onBeforeTraverse or onAfterTraverse callbacks.
4122
+ if (this._isTraversing) {
4123
+ throw new Error("This TopologicalSorter instance is already traversing.");
4124
+ }
4125
+ this._isTraversing = true;
4126
+ try {
4127
+ for (const node of graph) {
4128
+ this._traverseCore(node, getAdjacentNodes, onBeforeTraverse, onAfterTraverse);
4129
+ }
4130
+ }
4131
+ finally {
4132
+ this._isTraversing = false;
4133
+ this._traversalState.clear();
4134
+ }
4135
+ }
4136
+ _traverseCore(node, getAdjacentNodes, onBeforeTraverse, onAfterTraverse) {
4137
+ if (this._traversalState.get(node) !== true) {
4138
+ if (this._traversalState.get(node) === false) {
4139
+ throw new Error("Graph has cycle.");
4140
+ }
4141
+ this._traversalState.set(node, false);
4142
+ onBeforeTraverse?.(node);
4143
+ const adjacentNodes = getAdjacentNodes(node);
4144
+ if (adjacentNodes) {
4145
+ for (const adjacentNode of adjacentNodes) {
4146
+ this._traverseCore(adjacentNode, getAdjacentNodes, onBeforeTraverse, onAfterTraverse);
4147
+ }
4148
+ }
4149
+ this._traversalState.set(node, true);
4150
+ onAfterTraverse?.(node);
4151
+ }
4152
+ }
4153
+ }
4154
+
4155
+ const SyntheticUniqueIds$1 = new WeakMap();
4156
+ function GetEntityId$1(entity) {
4156
4157
  if ("uniqueId" in entity && typeof entity.uniqueId === "number") {
4157
4158
  return entity.uniqueId;
4158
4159
  }
4159
- let id = SyntheticUniqueIds.get(entity);
4160
+ let id = SyntheticUniqueIds$1.get(entity);
4160
4161
  if (!id) {
4161
- SyntheticUniqueIds.set(entity, (id = UniqueIdGenerator.UniqueId));
4162
+ SyntheticUniqueIds$1.set(entity, (id = UniqueIdGenerator.UniqueId));
4162
4163
  }
4163
4164
  return id;
4164
4165
  }
@@ -4178,7 +4179,7 @@ function GetEntitySection(entityItem) {
4178
4179
  }
4179
4180
  function ExpandOrCollapseAll(treeItem, open, openItems) {
4180
4181
  const addOrRemove = open ? openItems.add.bind(openItems) : openItems.delete.bind(openItems);
4181
- TraverseGraph([treeItem], (treeItem) => treeItem.children, (treeItem) => addOrRemove(treeItem.type === "entity" ? GetEntityId(treeItem.entity) : treeItem.sectionName));
4182
+ TraverseGraph([treeItem], (treeItem) => treeItem.children, (treeItem) => addOrRemove(treeItem.type === "entity" ? GetEntityId$1(treeItem.entity) : treeItem.sectionName));
4182
4183
  }
4183
4184
  function GetCommandHotKeyDescription(command) {
4184
4185
  if (!command.hotKey) {
@@ -4474,16 +4475,16 @@ const EntityTreeItem = (props) => {
4474
4475
  }
4475
4476
  }
4476
4477
  }, [commands]);
4477
- return (jsxs(Menu, { openOnContext: true, checkedValues: checkedContextMenuItems, onCheckedValueChange: onContextMenuCheckedValueChange, children: [jsx(MenuTrigger, { disableButtonEnhancement: true, children: jsx(FlatTreeItem, { className: mergeClasses(classes.treeItem, isDragging && classes.treeItemDragging, isDropTarget && classes.treeItemDropTarget), value: GetEntityId(entityItem.entity),
4478
+ return (jsxs(Menu, { openOnContext: true, checkedValues: checkedContextMenuItems, onCheckedValueChange: onContextMenuCheckedValueChange, children: [jsx(MenuTrigger, { disableButtonEnhancement: true, children: jsx(FlatTreeItem, { className: mergeClasses(classes.treeItem, isDragging && classes.treeItemDragging, isDropTarget && classes.treeItemDropTarget), value: GetEntityId$1(entityItem.entity),
4478
4479
  // Disable manual expand/collapse when a filter is active.
4479
- itemType: !isFiltering && hasChildren ? "branch" : "leaf", parentValue: entityItem.parent.type === "section" ? entityItem.parent.sectionName : GetEntityId(entityItem.parent.entity), "aria-level": entityItem.depth, "aria-setsize": 1, "aria-posinset": 1, onClick: select, onKeyDown: onKeyDown, style: { [treeItemLevelToken]: entityItem.depth }, ...dragProps, children: jsx(TreeItemLayout, { iconBefore: entityItem.icon ? jsx(entityItem.icon, { entity: entityItem.entity }) : null, className: mergeClasses(hasChildren ? classes.treeItemLayoutBranch : classes.treeItemLayoutLeaf, compactMode ? classes.treeItemLayoutCompact : undefined, isDropTarget && classes.treeItemDropTarget), style: isSelected ? { backgroundColor: tokens.colorNeutralBackground1Selected } : undefined, actions: actions, aside: {
4480
+ itemType: !isFiltering && hasChildren ? "branch" : "leaf", parentValue: entityItem.parent.type === "section" ? entityItem.parent.sectionName : GetEntityId$1(entityItem.parent.entity), "aria-level": entityItem.depth, "aria-setsize": 1, "aria-posinset": 1, onClick: select, onKeyDown: onKeyDown, style: { [treeItemLevelToken]: entityItem.depth }, ...dragProps, children: jsx(TreeItemLayout, { iconBefore: entityItem.icon ? jsx(entityItem.icon, { entity: entityItem.entity }) : null, className: mergeClasses(hasChildren ? classes.treeItemLayoutBranch : classes.treeItemLayoutLeaf, compactMode ? classes.treeItemLayoutCompact : undefined, isDropTarget && classes.treeItemDropTarget), style: isSelected ? { backgroundColor: tokens.colorNeutralBackground1Selected } : undefined, actions: actions, aside: {
4480
4481
  // Match the gap and padding of the actions.
4481
4482
  className: classes.treeItemLayoutAside,
4482
4483
  children: aside,
4483
4484
  }, main: {
4484
4485
  // Prevent the "main" content (the Body1 below) from growing too large and pushing the actions/aside out of view.
4485
4486
  className: classes.treeItemLayoutMain,
4486
- }, children: jsx(Tooltip$1, { content: name, relationship: "description", children: jsx(Body1, { wrap: false, truncate: true, children: name }) }) }) }, GetEntityId(entityItem.entity)) }), jsx(MenuPopover, { hidden: !hasChildren && contextMenuCommands.length === 0, children: jsxs(MenuList, { children: [hasChildren && (jsxs(Fragment, { children: [jsx(MenuItem, { icon: jsx(ArrowExpandAllRegular, {}), onClick: expandAll, children: jsx(Body1, { children: "Expand All" }) }), jsx(MenuItem, { icon: jsx(ArrowCollapseAllRegular, {}), onClick: collapseAll, children: jsx(Body1, { children: "Collapse All" }) })] })), hasChildren && contextMenuCommands.length > 0 && jsx(MenuDivider, {}), contextMenuItems] }) })] }));
4487
+ }, children: jsx(Tooltip$1, { content: name, relationship: "description", children: jsx(Body1, { wrap: false, truncate: true, children: name }) }) }) }, GetEntityId$1(entityItem.entity)) }), jsx(MenuPopover, { hidden: !hasChildren && contextMenuCommands.length === 0, children: jsxs(MenuList, { children: [hasChildren && (jsxs(Fragment, { children: [jsx(MenuItem, { icon: jsx(ArrowExpandAllRegular, {}), onClick: expandAll, children: jsx(Body1, { children: "Expand All" }) }), jsx(MenuItem, { icon: jsx(ArrowCollapseAllRegular, {}), onClick: collapseAll, children: jsx(Body1, { children: "Collapse All" }) })] })), hasChildren && contextMenuCommands.length > 0 && jsx(MenuDivider, {}), contextMenuItems] }) })] }));
4487
4488
  };
4488
4489
  const SceneExplorer = (props) => {
4489
4490
  const classes = useStyles$Q();
@@ -4506,7 +4507,7 @@ const SceneExplorer = (props) => {
4506
4507
  if (targetEntity) {
4507
4508
  setOpenItems((prev) => {
4508
4509
  const next = new Set(prev);
4509
- next.add(GetEntityId(targetEntity));
4510
+ next.add(GetEntityId$1(targetEntity));
4510
4511
  return next;
4511
4512
  });
4512
4513
  }
@@ -4523,7 +4524,7 @@ const SceneExplorer = (props) => {
4523
4524
  };
4524
4525
  const onSceneItemRemoved = (item) => {
4525
4526
  setSceneVersion((version) => version + 1);
4526
- if (openItems.delete(GetEntityId(item))) {
4527
+ if (openItems.delete(GetEntityId$1(item))) {
4527
4528
  setOpenItems(new Set(openItems));
4528
4529
  }
4529
4530
  if (item === selectedEntity) {
@@ -4579,7 +4580,7 @@ const SceneExplorer = (props) => {
4579
4580
  parent.children = [];
4580
4581
  }
4581
4582
  parent.children.push(treeItemData);
4582
- allTreeItems.set(GetEntityId(entity), treeItemData);
4583
+ allTreeItems.set(GetEntityId$1(entity), treeItemData);
4583
4584
  return treeItemData;
4584
4585
  };
4585
4586
  const rootEntityTreeItems = rootEntities.map((entity) => createEntityTreeItemData(entity, sectionTreeItem));
@@ -4627,7 +4628,7 @@ const SceneExplorer = (props) => {
4627
4628
  TraverseGraph(children,
4628
4629
  // Get children
4629
4630
  (treeItem) => {
4630
- if (filter || openItems.has(GetEntityId(treeItem.entity))) {
4631
+ if (filter || openItems.has(GetEntityId$1(treeItem.entity))) {
4631
4632
  if (!treeItem.children) {
4632
4633
  return null;
4633
4634
  }
@@ -4675,8 +4676,8 @@ const SceneExplorer = (props) => {
4675
4676
  }, [sceneTreeItem, sectionTreeItems, allTreeItems, openItems, itemsFilter, isSorted]);
4676
4677
  const getParentStack = useCallback((entity) => {
4677
4678
  const parentStack = [];
4678
- for (let treeItem = allTreeItems.get(GetEntityId(entity)); treeItem; treeItem = treeItem?.type === "entity" ? treeItem.parent : undefined) {
4679
- parentStack.push(treeItem.type === "entity" ? GetEntityId(treeItem.entity) : treeItem.sectionName);
4679
+ for (let treeItem = allTreeItems.get(GetEntityId$1(entity)); treeItem; treeItem = treeItem?.type === "entity" ? treeItem.parent : undefined) {
4680
+ parentStack.push(treeItem.type === "entity" ? GetEntityId$1(treeItem.entity) : treeItem.sectionName);
4680
4681
  }
4681
4682
  // The first item will be the entity itself, so just remove it.
4682
4683
  parentStack.shift();
@@ -4684,7 +4685,7 @@ const SceneExplorer = (props) => {
4684
4685
  }, [allTreeItems]);
4685
4686
  const selectEntity = useCallback((selectedEntity) => {
4686
4687
  const entity = selectedEntity;
4687
- if (entity && GetEntityId(entity) != undefined) {
4688
+ if (entity && GetEntityId$1(entity) != undefined) {
4688
4689
  const parentStack = getParentStack(entity);
4689
4690
  if (parentStack.length > 0) {
4690
4691
  const newOpenItems = new Set(openItems);
@@ -4756,7 +4757,7 @@ const SceneExplorer = (props) => {
4756
4757
  return name;
4757
4758
  };
4758
4759
  const dragProps = createDragProps(item.entity, getName, section.dragDropConfig);
4759
- return (jsx(EntityTreeItem, { scene: scene, entityItem: item, isSelected: selectedEntity === item.entity, select: () => setSelectedEntity?.(item.entity), isFiltering: !!itemsFilter, commandProviders: entityCommandProviders, expandAll: () => expandAll(item), collapseAll: () => collapseAll(item), isDragging: draggedEntity === item.entity, isDropTarget: dropTarget === item.entity, ...dragProps }, GetEntityId(item.entity)));
4760
+ return (jsx(EntityTreeItem, { scene: scene, entityItem: item, isSelected: selectedEntity === item.entity, select: () => setSelectedEntity?.(item.entity), isFiltering: !!itemsFilter, commandProviders: entityCommandProviders, expandAll: () => expandAll(item), collapseAll: () => collapseAll(item), isDragging: draggedEntity === item.entity, isDropTarget: dropTarget === item.entity, ...dragProps }, GetEntityId$1(item.entity)));
4760
4761
  }
4761
4762
  } }) })] }));
4762
4763
  };
@@ -6347,7 +6348,10 @@ const SpinButton = forwardRef((props, ref) => {
6347
6348
  // Update edit text to reflect the new value so the user sees the change
6348
6349
  setEditText(formatValue(newValue));
6349
6350
  }
6350
- HandleKeyDown(event);
6351
+ // Ignore modifier keys so the useKeyState calls above can still observe them.
6352
+ if (event.key !== "Alt" && event.key !== "Shift") {
6353
+ HandleKeyDown(event);
6354
+ }
6351
6355
  }, [value, step, constrainValue, tryCommitValue, commitEditText, formatValue]);
6352
6356
  const id = useId("spin-button2");
6353
6357
  // Real-time validation: when editing, validate the expression; otherwise validate the committed value.
@@ -6908,35 +6912,18 @@ const PerformanceViewer = (props) => {
6908
6912
  return (jsxs("div", { className: classes.container, children: [jsx(Button, { className: classes.returnButton, onClick: onReturnToPlayheadClick, label: "Return", title: "Return to Playhead" }), jsx("div", { className: classes.sidebar, children: jsx(PerformanceSidebar, { collector: performanceCollector, onVisibleRangeChangedObservable: onVisibleRangeChangedObservable }) }), jsx("div", { className: classes.graph, children: jsx(CanvasGraph, { returnToPlayheadObservable: returnToLiveObservable, layoutObservable: layoutObservable, scene: scene, collector: performanceCollector, onVisibleRangeChangedObservable: onVisibleRangeChangedObservable, initialGraphSize: initialGraphSize }) })] }));
6909
6913
  };
6910
6914
 
6911
- function AddStrategies(perfCollector) {
6912
- perfCollector.addCollectionStrategies(...DefaultStrategiesList);
6913
- if (PressureObserverWrapper.IsAvailable) {
6914
- // Do not enable for now as the Pressure API does not
6915
- // report factors at the moment.
6916
- // perfCollector.addCollectionStrategies({
6917
- // strategyCallback: PerfCollectionStrategy.ThermalStrategy(),
6918
- // category: IPerfMetadataCategory.FrameSteps,
6919
- // hidden: true,
6920
- // });
6921
- // perfCollector.addCollectionStrategies({
6922
- // strategyCallback: PerfCollectionStrategy.PowerSupplyStrategy(),
6923
- // category: IPerfMetadataCategory.FrameSteps,
6924
- // hidden: true,
6925
- // });
6926
- perfCollector.addCollectionStrategies({
6927
- strategyCallback: PerfCollectionStrategy.PressureStrategy(),
6928
- category: "Frame Steps Duration" /* PerfMetadataCategory.FrameSteps */,
6929
- hidden: true,
6930
- });
6931
- }
6932
- }
6915
+ /**
6916
+ * Performance metadata categories for grouping strategies.
6917
+ */
6933
6918
  var PerfMetadataCategory;
6934
6919
  (function (PerfMetadataCategory) {
6935
6920
  PerfMetadataCategory["Count"] = "Count";
6936
6921
  PerfMetadataCategory["FrameSteps"] = "Frame Steps Duration";
6937
6922
  })(PerfMetadataCategory || (PerfMetadataCategory = {}));
6938
- // list of strategies to add to perf graph automatically.
6939
- const DefaultStrategiesList = [
6923
+ /**
6924
+ * Default list of performance collection strategies used by the performance viewer and CLI perf trace.
6925
+ */
6926
+ const DefaultPerfStrategies = [
6940
6927
  { strategyCallback: PerfCollectionStrategy.FpsStrategy() },
6941
6928
  { strategyCallback: PerfCollectionStrategy.TotalMeshesStrategy(), category: "Count" /* PerfMetadataCategory.Count */, hidden: true },
6942
6929
  { strategyCallback: PerfCollectionStrategy.ActiveMeshesStrategy(), category: "Count" /* PerfMetadataCategory.Count */, hidden: true },
@@ -6960,6 +6947,21 @@ const DefaultStrategiesList = [
6960
6947
  { strategyCallback: PerfCollectionStrategy.InterFrameStrategy(), category: "Frame Steps Duration" /* PerfMetadataCategory.FrameSteps */, hidden: true },
6961
6948
  { strategyCallback: PerfCollectionStrategy.GpuFrameTimeStrategy(), category: "Frame Steps Duration" /* PerfMetadataCategory.FrameSteps */, hidden: true },
6962
6949
  ];
6950
+
6951
+ /**
6952
+ * Adds default and platform-specific performance collection strategies to the collector.
6953
+ * @param perfCollector - The performance viewer collector to add strategies to.
6954
+ */
6955
+ function AddStrategies(perfCollector) {
6956
+ perfCollector.addCollectionStrategies(...DefaultPerfStrategies);
6957
+ if (PressureObserverWrapper.IsAvailable) {
6958
+ perfCollector.addCollectionStrategies({
6959
+ strategyCallback: PerfCollectionStrategy.PressureStrategy(),
6960
+ category: "Frame Steps Duration" /* PerfMetadataCategory.FrameSteps */,
6961
+ hidden: true,
6962
+ });
6963
+ }
6964
+ }
6963
6965
  // arbitrary window size
6964
6966
  const InitialWindowSize = { width: 1024, height: 512 };
6965
6967
  const InitialGraphSize = new Vector2(724, 512);
@@ -7234,6 +7236,11 @@ const ToolsServiceDefinition = {
7234
7236
  },
7235
7237
  };
7236
7238
 
7239
+ /**
7240
+ * The unique identity symbol for the react context service.
7241
+ */
7242
+ const ReactContextServiceIdentity = Symbol("ReactContextService");
7243
+
7237
7244
  const useStyles$J = makeStyles({
7238
7245
  dropdown: {
7239
7246
  ...UniformWidthStyling,
@@ -7434,8 +7441,8 @@ const WatcherServiceIdentity = Symbol("WatcherService");
7434
7441
  const WatcherServiceDefinition = {
7435
7442
  friendlyName: "Watcher Service",
7436
7443
  produces: [WatcherServiceIdentity],
7437
- consumes: [SettingsStoreIdentity],
7438
- factory: (settingsStore) => {
7444
+ consumes: [SettingsStoreIdentity, ReactContextServiceIdentity],
7445
+ factory: (settingsStore, reactContextService) => {
7439
7446
  let refreshObservable = null;
7440
7447
  let pollingHandle = null;
7441
7448
  const applySettings = () => {
@@ -7465,7 +7472,7 @@ const WatcherServiceDefinition = {
7465
7472
  }
7466
7473
  });
7467
7474
  applySettings();
7468
- return {
7475
+ const watcherService = {
7469
7476
  watchProperty(target, propertyKey, onChanged) {
7470
7477
  if (refreshObservable) {
7471
7478
  let previousValue = target[propertyKey];
@@ -7490,6 +7497,7 @@ const WatcherServiceDefinition = {
7490
7497
  refreshObservable?.notifyObservers();
7491
7498
  },
7492
7499
  dispose: () => {
7500
+ contextHandle.dispose();
7493
7501
  if (pollingHandle !== null) {
7494
7502
  clearInterval(pollingHandle);
7495
7503
  pollingHandle = null;
@@ -7499,6 +7507,9 @@ const WatcherServiceDefinition = {
7499
7507
  settingsStoreObserver.remove();
7500
7508
  },
7501
7509
  };
7510
+ // Register the WatcherContext provider so React components can access the watcher service.
7511
+ const contextHandle = reactContextService.addContext(WatcherContext.Provider, watcherService);
7512
+ return watcherService;
7502
7513
  },
7503
7514
  };
7504
7515
  const WatchModes = [
@@ -7844,241 +7855,16 @@ const GizmoServiceDefinition = {
7844
7855
  },
7845
7856
  };
7846
7857
 
7847
- // Created by running the following command from the packages/dev/inspector-v2 directory:
7848
- // npm run makeAvatar https://raw.githubusercontent.com/BabylonJS/Brand-Toolkit/master/babylon_logo/fullColor/babylon_logo_color.png 0.8
7849
- const BabylonLogoBase64 = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsSAAALEgHS3X78AAAFXElEQVR4nO2WbWwTdRzHu+EbYmLgpeILwcQXvvCFLwwGxthG1/Z6d33Ag22FDXAE98wixsCbSiS+4IUhxoQYoyiSGKcogsjaa9c9dw9sbdfrbevW611nYTDYliCRsbv7mrt2sPiGUTZf8Uua9P7/u9/383v4//MzGJ5bjgaDIQ8Ms66FYdYZ/m+D250PgyF/GUy+trb2wshEnfmPvKnG3fukJoZ+vI912vqapRsGg+481cy8I9RSbTONNkw32pXJatMvCcvmN7Q9t5ad1QSBJuzOpHvmo7JXkrX01zfq6Yd3mhxIHrHIQuUOee5EJR4MtN0D8Bnm5jbo37nd+c/UH1jmINXcvF6scxxL1VJ3Zo86kKol5UTVTlmsIXH3p68g35uXASgAoCqyCKBKy4Lup0UL4Bn6Q2piaKmWjM0etSNdTyvCoV2LwsES9daXbixMCZomoKpQFFkFsKg9aUtzv//QM7ln6zYdIlu6lUVuyLw8e7zsLbGGunqrXq8zhMMmOVFZqKQ/+QD3w8GMsB6xAmi/rN0PB+X0qQZ5psGGG/W0LNXT5/9x731tRSDubKquWJlN3H7H7N1GO1K1hDy5v1BJNe/BvPcilIUHj4TVxUWosqJnYMlmfz0H4X0jhANF8l915OJ8oxORCnv0vHHfi0+EcGcBBpwVW9hSaqGdIBSu0qrOXvgCyvzsUsgZ4WVR6zDKYxD59g3cPvspwuVmpZ2g4DPR0khNzcYVA3QwzGY/QT/wFpnBMuVqShKwsCSQDVZVVaQ9LG519z7qgyXAhwCmxnh4TJTSZiThM1NioKpqw4oBWKdzi89CLbDFZrSVudRIfzti0UFMpyXIioJ5fhSDH36Ma9uL0LrTiPDJU/hbSkFWZMxMpyGK4xjv64KPsCmBUit8ZlrqqqjYmBvAXpcaG+pFbGQA0egAOF8rfCZSF/dZaC06/Ll1B9qZciT5CIQED0mawMRg7+oBcEM9OgA/FsbQby1gjQS8JgreUiu8pSQ8Riv8tt2YHO6HKMbXEiCE0KWf4Skh0ElT4A+Q4CpJBAgrWGo3JkMDawzADyPcehlhFwnx0C5IdRRSdSSE/TsRqi5HIhqCmBxfA4DBbh0gFulHjLuO8bZLSJyqh3jIiGS1CeKZE0gNd0FMxiFOaj0Qx8RAT+4AHQyzmTUvAVSo2gngR0PgQkEdguND4LghxC9+A9F7EWI6iaQ0qYsnJ2JITUtIhAe1JtUB2Kc9hl3Oii1+M7XgNRIKW2pVe44fx0iXD6MTI/pxjA0HwUX6wcejSE4JEBM8knFOr3/qpoR4dzt6m4/BW2JW/CYSfgstjVQ8xUV0hWE2BQjb3SBh0zpe9hQUKz7Kif4zn4MbDoKfiIAL92E00p8RFsYwdVOCwIVw/fRp/WheKyjR4OVB0ok2whb17FvBVbz8hU6GeZMl6MudFhu6CJsWjezZXqwEXJUYuvA9eH4IY+MRSGlBb7zId+cQeK8MrduKFO8ui9xttaPDQi+yhO3bPlf1q8t9P5X5bU7Cb6EjQasdAQuteIpMi55Co9pZ14ARz1XwrX+g8/ARtBYUq57iUrmDsKk9hF2re0e7nXk3Z2FoA4khM5D0NjPrfVZ7k99E3Q6SDu0mVDwFJTJrpuE1EmgtKJHbzJTSp+2ZSYGlHS4Aejm1oWaptDlZiz6SZU+Hy/UyS9jOBsz0Qo/VDi3VPiMh95IO+M3UPdbqONnnanhpKYBVm5RhMOS5CwtfcGdH8Q7Hnre9FortJvQ6K6yF+tG/t/J1bU+LVofOJe1PMs3p8kHTRzvKtB7JPuatmfB/TU8vHgutarpz7Y/nZsjR/gUhEcwHmuaM9wAAAABJRU5ErkJggg==";
7850
- const BabylonWebResources = {
7851
- homepage: "https://www.babylonjs.com",
7852
- repository: "https://github.com/BabylonJS/Babylon.js",
7853
- bugs: "https://github.com/BabylonJS/Babylon.js/issues",
7854
- author: {
7855
- name: "Babylon.js",
7856
- avatar: BabylonLogoBase64,
7857
- },
7858
- };
7858
+ const InstalledExtensionsKey = "Extensions/InstalledExtensions";
7859
+ const ExtensionInstalledKeyPrefix = "Extensions/IsExtensionInstalled";
7860
+ function GetExtensionInstalledKey(name) {
7861
+ return `${ExtensionInstalledKeyPrefix}/${name}`;
7862
+ }
7863
+ function GetExtensionIdentity(feed, name) {
7864
+ return `${feed}/${name}`;
7865
+ }
7859
7866
  /**
7860
- * Well-known default built in extensions for the Inspector.
7861
- */
7862
- const DefaultInspectorExtensionFeed = new BuiltInsExtensionFeed("Inspector", [
7863
- {
7864
- name: "Quick Creation Tools (Preview)",
7865
- description: "Adds a new panel for easy creation of various Babylon assets. This is a WIP extension...expect changes!",
7866
- keywords: ["creation", "tools"],
7867
- ...BabylonWebResources,
7868
- getExtensionModuleAsync: async () => await import('./quickCreateToolsService-BtsSLeMY.js'),
7869
- },
7870
- {
7871
- name: "Reflector",
7872
- description: "Connects to the Reflector Bridge for real-time scene synchronization with the Babylon.js Sandbox.",
7873
- keywords: ["reflector", "bridge", "sync", "sandbox", "tools"],
7874
- ...BabylonWebResources,
7875
- getExtensionModuleAsync: async () => await import('./reflectorService-Bzs-Ykos.js'),
7876
- },
7877
- ]);
7878
-
7879
- /**
7880
- * Reusable component which renders a color property line containing a label, colorPicker popout, and expandable RGBA values
7881
- * The expandable RGBA values are synced sliders that allow the user to modify the color's RGBA values directly
7882
- * @param props - PropertyLine props, replacing children with a color object so that we can properly display the color
7883
- * @returns Component wrapping a colorPicker component with a property line
7884
- */
7885
- const ColorPropertyLine = forwardRef((props, ref) => {
7886
- ColorPropertyLine.displayName = "ColorPropertyLine";
7887
- const [color, setColor] = useState(props.value);
7888
- useEffect(() => {
7889
- setColor(props.value);
7890
- }, [props.value]);
7891
- const onSliderChange = (value, key) => {
7892
- let newColor;
7893
- if (key === "a") {
7894
- newColor = Color4.FromColor3(color, value);
7895
- }
7896
- else {
7897
- newColor = color.clone();
7898
- newColor[key] = value / 255;
7899
- }
7900
- setColor(newColor); // Create a new object to trigger re-render
7901
- props.onChange(newColor);
7902
- };
7903
- const onColorPickerChange = (newColor) => {
7904
- setColor(newColor);
7905
- props.onChange(newColor);
7906
- };
7907
- return (jsx(PropertyLine, { ref: ref, ...props, expandedContent: color ? jsx(ColorSliders, { color: color, onSliderChange: onSliderChange }) : undefined, children: jsx(ColorPickerPopup, { ...props, onChange: onColorPickerChange, value: color }) }));
7908
- });
7909
- const ColorSliders = ({ color, onSliderChange }) => (jsxs(Fragment, { children: [jsx(SyncedSliderPropertyLine, { label: "R", value: color.r * 255, min: 0, max: 255, onChange: (value) => onSliderChange(value, "r") }), jsx(SyncedSliderPropertyLine, { label: "G", value: color.g * 255, min: 0, max: 255, onChange: (value) => onSliderChange(value, "g") }), jsx(SyncedSliderPropertyLine, { label: "B", value: color.b * 255, min: 0, max: 255, onChange: (value) => onSliderChange(value, "b") }), color instanceof Color4 && jsx(SyncedSliderPropertyLine, { label: "A", value: color.a, min: 0, max: 1, step: 0.01, onChange: (value) => onSliderChange(value, "a") })] }));
7910
- const Color3PropertyLine = ColorPropertyLine;
7911
- const Color4PropertyLine = ColorPropertyLine;
7912
-
7913
- const useStyles$H = makeStyles({
7914
- uniformWidth: {
7915
- ...UniformWidthStyling,
7916
- },
7917
- });
7918
- /**
7919
- * Wraps a text input in a property line
7920
- * @param props - PropertyLineProps and InputProps
7921
- * @returns property-line wrapped input component
7922
- */
7923
- const TextInputPropertyLine = (props) => {
7924
- TextInputPropertyLine.displayName = "TextInputPropertyLine";
7925
- const classes = useStyles$H();
7926
- return (jsx(PropertyLine, { ...props, children: jsx(TextInput, { ...props, className: mergeClasses(classes.uniformWidth, props.className) }) }));
7927
- };
7928
- /**
7929
- * Wraps a number input in a property line
7930
- * To force integer values, use forceInt param (this is distinct from the 'step' param, which will still allow submitting an integer value. forceInt will not)
7931
- * @param props - PropertyLineProps and InputProps
7932
- * @returns property-line wrapped input component
7933
- */
7934
- const NumberInputPropertyLine = (props) => {
7935
- NumberInputPropertyLine.displayName = "NumberInputPropertyLine";
7936
- const classes = useStyles$H();
7937
- return (jsx(PropertyLine, { ...props, children: jsx(SpinButton, { ...props, className: mergeClasses(classes.uniformWidth, props.className) }) }));
7938
- };
7939
-
7940
- const HasZ = (vector) => !(vector instanceof Vector2);
7941
- const HasW = (vector) => vector instanceof Vector4 || vector instanceof Quaternion;
7942
- /**
7943
- * Reusable component which renders a vector property line containing a label, vector value, and expandable XYZW values
7944
- * The expanded section contains a slider/input box for each component of the vector (x, y, z, w)
7945
- * @param props
7946
- * @returns
7947
- */
7948
- const TensorPropertyLine = (props) => {
7949
- TensorPropertyLine.displayName = "TensorPropertyLine";
7950
- const converted = (val) => (props.valueConverter ? props.valueConverter.from(val) : val);
7951
- const formatted = (val) => converted(val).toFixed(props.step !== undefined ? Math.max(0, CalculatePrecision(props.step)) : 2);
7952
- const [vector, setVector] = useState(props.value);
7953
- const { min, max } = props;
7954
- const onChange = (val, key) => {
7955
- const value = props.valueConverter ? props.valueConverter.to(val) : val;
7956
- const newVector = vector.clone();
7957
- newVector[key] = value; // The syncedSlider for 'w' is only rendered when vector is a Vector4, so this is safe
7958
- setVector(newVector);
7959
- props.onChange(newVector);
7960
- };
7961
- useEffect(() => {
7962
- setVector(props.value);
7963
- }, [props.value, props.expandedContent]);
7964
- return (jsx(PropertyLine, { ...props, expandedContent: jsxs(Fragment, { children: [props.expandedContent, vector ? (jsx(VectorSliders, { vector: vector, min: min, max: max, unit: props.unit, step: props.step, precision: props.precision, converted: converted, onChange: onChange })) : undefined] }), children: jsx(Body1, { children: `[${formatted(props.value.x)}, ${formatted(props.value.y)}${HasZ(props.value) ? `, ${formatted(props.value.z)}` : ""}${HasW(props.value) ? `, ${formatted(props.value.w)}` : ""}]` }) }));
7965
- };
7966
- const VectorSliders = ({ vector, min, max, unit, step, precision, converted, onChange }) => (jsxs(Fragment, { children: [jsx(NumberInputPropertyLine, { label: "X", value: converted(vector.x), min: min, max: max, onChange: (val) => onChange(val, "x"), unit: unit, step: step, precision: precision }), jsx(NumberInputPropertyLine, { label: "Y", value: converted(vector.y), min: min, max: max, onChange: (val) => onChange(val, "y"), unit: unit, step: step, precision: precision }), HasZ(vector) && (jsx(NumberInputPropertyLine, { label: "Z", value: converted(vector.z), min: min, max: max, onChange: (val) => onChange(val, "z"), unit: unit, step: step, precision: precision })), HasW(vector) && (jsx(NumberInputPropertyLine, { label: "W", value: converted(vector.w), min: min, max: max, onChange: (val) => onChange(val, "w"), unit: unit, step: step, precision: precision }))] }));
7967
- const ToDegreesConverter = { from: Tools.ToDegrees, to: Tools.ToRadians };
7968
- const RotationVectorPropertyLine = (props) => {
7969
- RotationVectorPropertyLine.displayName = "RotationVectorPropertyLine";
7970
- const step = props.useDegrees ? 1 : 0.01;
7971
- const precision = props.useDegrees ? 1 : 2;
7972
- return (jsx(Vector3PropertyLine, { ...props, unit: props.useDegrees ? "°" : "rad", valueConverter: props.useDegrees ? ToDegreesConverter : undefined, step: step, precision: precision }));
7973
- };
7974
- const QuaternionPropertyLineInternal = TensorPropertyLine;
7975
- const QuaternionPropertyLine = (props) => {
7976
- QuaternionPropertyLine.displayName = "QuaternionPropertyLine";
7977
- const step = props.useDegrees ? 1 : 0.01;
7978
- const precision = props.useDegrees ? 1 : 2;
7979
- const [quat, setQuat] = useState(props.value);
7980
- useEffect(() => {
7981
- setQuat(props.value);
7982
- }, [props.value]);
7983
- // Extract only the properties that exist on QuaternionPropertyLineProps
7984
- const { useEuler, ...restProps } = props;
7985
- const onQuatChange = (val) => {
7986
- setQuat(val);
7987
- props.onChange(val);
7988
- };
7989
- const onEulerChange = (val) => {
7990
- const quat = Quaternion.FromEulerAngles(val.x, val.y, val.z);
7991
- onQuatChange(quat);
7992
- };
7993
- return useEuler ? (jsx(Vector3PropertyLine, { ...restProps, nullable: false, ignoreNullable: false, value: quat.toEulerAngles(), valueConverter: ToDegreesConverter, onChange: onEulerChange, unit: props.useDegrees ? "°" : "rad", step: step, precision: precision, expandedContent: jsx(TextPropertyLine, { label: "Quaternion", value: `[${quat.x.toFixed(4)}, ${quat.y.toFixed(4)}, ${quat.z.toFixed(4)}, ${quat.w.toFixed(4)}]` }) })) : (jsx(QuaternionPropertyLineInternal, { ...props, nullable: false, value: quat, onChange: onQuatChange, unit: props.useDegrees ? "°" : "rad", step: step, precision: precision }));
7994
- };
7995
- const Vector2PropertyLine = TensorPropertyLine;
7996
- const Vector3PropertyLine = TensorPropertyLine;
7997
- const Vector4PropertyLine = TensorPropertyLine;
7998
-
7999
- function IsInspectableObject(entity) {
8000
- return !!entity.inspectableCustomProperties;
8001
- }
8002
- const LegacyInspectableObjectPropertiesServiceDefinition = {
8003
- friendlyName: "Additional Nodes",
8004
- consumes: [PropertiesServiceIdentity],
8005
- factory: (propertiesService) => {
8006
- const propertiesSectionRegistration = propertiesService.addSection({
8007
- identity: "Custom",
8008
- order: Number.MAX_SAFE_INTEGER,
8009
- });
8010
- const propertiesContentRegistration = propertiesService.addSectionContent({
8011
- key: "Additional Nodes Properties",
8012
- predicate: (entity) => IsInspectableObject(entity),
8013
- content: [
8014
- {
8015
- section: "Custom",
8016
- component: ({ context }) => {
8017
- return (jsx(Fragment, { children: (context.inspectableCustomProperties ?? []).map((prop) => {
8018
- const commonProps = {
8019
- target: context,
8020
- propertyKey: prop.propertyName,
8021
- label: prop.label,
8022
- ignoreNullable: true,
8023
- defaultValue: undefined,
8024
- };
8025
- switch (prop.type) {
8026
- case 0 /* InspectableType.Checkbox */:
8027
- return jsx(BoundProperty, { ...commonProps, component: SwitchPropertyLine }, prop.propertyName);
8028
- case 1 /* InspectableType.Slider */:
8029
- return (jsx(BoundProperty, { ...commonProps, min: prop.min, max: prop.max, step: prop.step, component: SyncedSliderPropertyLine }, prop.propertyName));
8030
- case 2 /* InspectableType.Vector3 */:
8031
- return jsx(BoundProperty, { ...commonProps, component: Vector3PropertyLine }, prop.propertyName);
8032
- case 3 /* InspectableType.Quaternion */:
8033
- return jsx(BoundProperty, { ...commonProps, component: QuaternionPropertyLine }, prop.propertyName);
8034
- case 4 /* InspectableType.Color3 */:
8035
- return jsx(BoundProperty, { ...commonProps, component: Color3PropertyLine }, prop.propertyName);
8036
- case 5 /* InspectableType.String */:
8037
- return jsx(BoundProperty, { ...commonProps, component: TextInputPropertyLine }, prop.propertyName);
8038
- case 6 /* InspectableType.Button */:
8039
- return jsx(ButtonLine, { label: prop.label, onClick: () => prop.callback?.() }, prop.propertyName);
8040
- case 7 /* InspectableType.Options */:
8041
- return jsx(BoundProperty, { ...commonProps, component: DropdownPropertyLine, options: prop.options ?? [] }, prop.propertyName);
8042
- case 8 /* InspectableType.Tab */:
8043
- return jsx(BoundProperty, { ...commonProps, component: TextPropertyLine }, prop.propertyName);
8044
- case 9 /* InspectableType.FileButton */:
8045
- return (jsx(FileUploadLine, { label: prop.label, accept: prop.accept ?? "", onClick: (files) => {
8046
- if (files.length > 0 && prop.fileCallback) {
8047
- prop.fileCallback(files[0]);
8048
- }
8049
- } }, prop.propertyName));
8050
- case 10 /* InspectableType.Vector2 */:
8051
- return jsx(BoundProperty, { ...commonProps, component: Vector2PropertyLine }, prop.propertyName);
8052
- }
8053
- }) }));
8054
- },
8055
- },
8056
- ],
8057
- });
8058
- return {
8059
- dispose: () => {
8060
- propertiesSectionRegistration.dispose();
8061
- propertiesContentRegistration.dispose();
8062
- },
8063
- };
8064
- },
8065
- };
8066
-
8067
- const ExtensionManagerContext = createContext(undefined);
8068
- function useExtensionManager() {
8069
- return useContext(ExtensionManagerContext)?.extensionManager;
8070
- }
8071
-
8072
- const InstalledExtensionsKey = "Extensions/InstalledExtensions";
8073
- const ExtensionInstalledKeyPrefix = "Extensions/IsExtensionInstalled";
8074
- function GetExtensionInstalledKey(name) {
8075
- return `${ExtensionInstalledKeyPrefix}/${name}`;
8076
- }
8077
- function GetExtensionIdentity(feed, name) {
8078
- return `${feed}/${name}`;
8079
- }
8080
- /**
8081
- * Manages the installation, uninstallation, enabling, and disabling of extensions.
7867
+ * Manages the installation, uninstallation, enabling, and disabling of extensions.
8082
7868
  */
8083
7869
  class ExtensionManager {
8084
7870
  constructor(_namespace, _serviceContainer, _feeds, _onInstallFailed) {
@@ -8363,12 +8149,20 @@ function SortServiceDefinitions(serviceDefinitions) {
8363
8149
  * passing dependencies through to services, and disposing of services when the container is disposed.
8364
8150
  */
8365
8151
  class ServiceContainer {
8366
- constructor(_friendlyName) {
8152
+ /**
8153
+ * Creates a new ServiceContainer.
8154
+ * @param _friendlyName A human-readable name for debugging.
8155
+ * @param _parent An optional parent container. Dependencies not found locally will be resolved from the parent.
8156
+ */
8157
+ constructor(_friendlyName, _parent) {
8367
8158
  this._friendlyName = _friendlyName;
8159
+ this._parent = _parent;
8368
8160
  this._isDisposed = false;
8369
8161
  this._serviceDefinitions = new Map();
8370
8162
  this._serviceDependents = new Map();
8371
8163
  this._serviceInstances = new Map();
8164
+ this._children = new Set();
8165
+ _parent?._children.add(this);
8372
8166
  }
8373
8167
  /**
8374
8168
  * Adds a set of service definitions in the service container.
@@ -8429,30 +8223,61 @@ class ServiceContainer {
8429
8223
  service.produces?.forEach((contract) => {
8430
8224
  this._serviceDefinitions.set(contract, service);
8431
8225
  });
8432
- const dependencies = service.consumes?.map((dependency) => {
8433
- const dependencyDefinition = this._serviceDefinitions.get(dependency);
8434
- if (!dependencyDefinition) {
8435
- throw new Error(`Service '${dependency.toString()}' has not been registered in the '${this._friendlyName}' container.`);
8436
- }
8437
- let dependentDefinitions = this._serviceDependents.get(dependencyDefinition);
8226
+ const dependencies = service.consumes?.map((contract) => this._resolveDependency(contract, service)) ?? [];
8227
+ this._serviceInstances.set(service, await service.factory(...dependencies, abortSignal));
8228
+ }
8229
+ /**
8230
+ * Resolves a dependency by contract identity for a consuming service.
8231
+ * Checks local services first, then walks up the parent chain.
8232
+ * Registers the consumer as a dependent in whichever container owns the dependency.
8233
+ * @param contract The contract identity to resolve.
8234
+ * @param consumer The service definition that consumes this dependency.
8235
+ * @returns The resolved service instance.
8236
+ */
8237
+ _resolveDependency(contract, consumer) {
8238
+ const definition = this._serviceDefinitions.get(contract);
8239
+ if (definition) {
8240
+ let dependentDefinitions = this._serviceDependents.get(definition);
8438
8241
  if (!dependentDefinitions) {
8439
- this._serviceDependents.set(dependencyDefinition, (dependentDefinitions = new Set()));
8242
+ this._serviceDependents.set(definition, (dependentDefinitions = new Set()));
8440
8243
  }
8441
- dependentDefinitions.add(service);
8442
- const dependencyInstance = this._serviceInstances.get(dependencyDefinition);
8443
- if (!dependencyInstance) {
8444
- throw new Error(`Service '${dependency.toString()}' has not been instantiated in the '${this._friendlyName}' container.`);
8244
+ dependentDefinitions.add(consumer);
8245
+ const instance = this._serviceInstances.get(definition);
8246
+ if (!instance) {
8247
+ throw new Error(`Service '${contract.toString()}' has not been instantiated in the '${this._friendlyName}' container.`);
8445
8248
  }
8446
- return dependencyInstance;
8447
- }) ?? [];
8448
- this._serviceInstances.set(service, await service.factory(...dependencies, abortSignal));
8249
+ return instance;
8250
+ }
8251
+ if (this._parent) {
8252
+ return this._parent._resolveDependency(contract, consumer);
8253
+ }
8254
+ throw new Error(`Service '${contract.toString()}' has not been registered in the '${this._friendlyName}' container.`);
8255
+ }
8256
+ /**
8257
+ * Removes a consumer from the dependent set for a given contract, checking locally first then the parent chain.
8258
+ * @param contract The contract identity.
8259
+ * @param consumer The service definition to remove as a dependent.
8260
+ */
8261
+ _removeDependentFromChain(contract, consumer) {
8262
+ const definition = this._serviceDefinitions.get(contract);
8263
+ if (definition) {
8264
+ const dependentDefinitions = this._serviceDependents.get(definition);
8265
+ if (dependentDefinitions) {
8266
+ dependentDefinitions.delete(consumer);
8267
+ if (dependentDefinitions.size === 0) {
8268
+ this._serviceDependents.delete(definition);
8269
+ }
8270
+ }
8271
+ return;
8272
+ }
8273
+ this._parent?._removeDependentFromChain(contract, consumer);
8449
8274
  }
8450
8275
  _removeService(service) {
8451
8276
  if (this._isDisposed) {
8452
8277
  throw new Error(`'${this._friendlyName}' container is disposed.`);
8453
8278
  }
8454
8279
  const serviceDependents = this._serviceDependents.get(service);
8455
- if (serviceDependents) {
8280
+ if (serviceDependents && serviceDependents.size > 0) {
8456
8281
  throw new Error(`Service '${service.friendlyName}' has dependents: ${Array.from(serviceDependents)
8457
8282
  .map((dependent) => dependent.friendlyName)
8458
8283
  .join(", ")}`);
@@ -8464,32 +8289,34 @@ class ServiceContainer {
8464
8289
  service.produces?.forEach((contract) => {
8465
8290
  this._serviceDefinitions.delete(contract);
8466
8291
  });
8467
- service.consumes?.forEach((dependency) => {
8468
- const dependencyDefinition = this._serviceDefinitions.get(dependency);
8469
- if (dependencyDefinition) {
8470
- const dependentDefinitions = this._serviceDependents.get(dependencyDefinition);
8471
- if (dependentDefinitions) {
8472
- dependentDefinitions.delete(service);
8473
- if (dependentDefinitions.size === 0) {
8474
- this._serviceDependents.delete(dependencyDefinition);
8475
- }
8476
- }
8477
- }
8292
+ // Remove this service as a dependent from each of its consumed dependencies (local or in parent chain).
8293
+ service.consumes?.forEach((contract) => {
8294
+ this._removeDependentFromChain(contract, service);
8478
8295
  });
8479
8296
  }
8480
8297
  /**
8481
8298
  * Disposes the service container and all contained services.
8299
+ * Throws if this container is still a parent of any live child containers.
8482
8300
  */
8483
8301
  dispose() {
8302
+ if (this._children.size > 0) {
8303
+ throw new Error(`'${this._friendlyName}' container cannot be disposed because it has ${this._children.size} active child container(s).`);
8304
+ }
8484
8305
  Array.from(this._serviceInstances.keys()).reverse().forEach(this._removeService.bind(this));
8485
8306
  this._serviceInstances.clear();
8486
8307
  this._serviceDependents.clear();
8487
8308
  this._serviceDefinitions.clear();
8309
+ this._parent?._children.delete(this);
8488
8310
  this._isDisposed = true;
8489
8311
  }
8490
8312
  }
8491
8313
 
8492
- const useStyles$G = makeStyles({
8314
+ const ExtensionManagerContext = createContext(undefined);
8315
+ function useExtensionManager() {
8316
+ return useContext(ExtensionManagerContext)?.extensionManager;
8317
+ }
8318
+
8319
+ const useStyles$H = makeStyles({
8493
8320
  themeButton: {
8494
8321
  margin: 0,
8495
8322
  },
@@ -8508,7 +8335,7 @@ const ThemeSelectorServiceDefinition = {
8508
8335
  teachingMoment: false,
8509
8336
  order: -300,
8510
8337
  component: () => {
8511
- const classes = useStyles$G();
8338
+ const classes = useStyles$H();
8512
8339
  const { isDarkMode, themeMode, setThemeMode } = useThemeMode();
8513
8340
  const onSelectedThemeChange = useCallback((e, data) => {
8514
8341
  setThemeMode(data.checkedItems.includes("System") ? "system" : data.checkedItems[0].toLocaleLowerCase());
@@ -8525,7 +8352,7 @@ const ThemeSelectorServiceDefinition = {
8525
8352
  },
8526
8353
  };
8527
8354
 
8528
- const useStyles$F = makeStyles({
8355
+ const useStyles$G = makeStyles({
8529
8356
  app: {
8530
8357
  colorScheme: "light dark",
8531
8358
  flexGrow: 1,
@@ -8551,13 +8378,16 @@ const useStyles$F = makeStyles({
8551
8378
  color: tokens.colorPaletteRedForeground1,
8552
8379
  },
8553
8380
  });
8381
+ const ReactContextsWrapper = ({ contexts, children }) => {
8382
+ return jsx(Fragment, { children: contexts.reduceRight((acc, entry) => createElement(entry.provider, { value: entry.value }, acc), children) });
8383
+ };
8554
8384
  /**
8555
8385
  * Creates a modular tool with a base set of common tool services, including the toolbar/side pane basic UI layout.
8556
8386
  * @param options The options for the tool.
8557
8387
  * @returns A token that can be used to dispose of the tool.
8558
8388
  */
8559
8389
  function MakeModularTool(options) {
8560
- const { namespace, containerElement, serviceDefinitions, themeMode, showThemeSelector = true, extensionFeeds = [] } = options;
8390
+ const { namespace, containerElement, serviceDefinitions, themeMode, showThemeSelector = true, extensionFeeds = [], parentContainer } = options;
8561
8391
  // Create the settings store immediately as it will be exposed to services and through React context.
8562
8392
  const settingsStore = new SettingsStore(namespace);
8563
8393
  // If a theme mode is provided, just write the setting so it is the active theme.
@@ -8565,38 +8395,62 @@ function MakeModularTool(options) {
8565
8395
  settingsStore.writeSetting(ThemeModeSettingDescriptor, themeMode);
8566
8396
  }
8567
8397
  const modularToolRootComponent = () => {
8568
- const classes = useStyles$F();
8398
+ const classes = useStyles$G();
8569
8399
  const [extensionManagerContext, setExtensionManagerContext] = useState();
8570
8400
  const [requiredExtensions, setRequiredExtensions] = useState();
8571
8401
  const [requiredExtensionsDeferred, setRequiredExtensionsDeferred] = useState();
8572
8402
  const [extensionInstallError, setExtensionInstallError] = useState();
8573
- const [bootstrapServices, setBootstrapServices] = useState();
8403
+ const [rootComponentService, setRootComponentService] = useState();
8404
+ const [contexts, updateContexts] = useReducer((state, action) => {
8405
+ switch (action.type) {
8406
+ case "add":
8407
+ return [...state, action.entry].sort((a, b) => a.order - b.order);
8408
+ case "remove":
8409
+ return state.filter((e) => e.provider !== action.provider);
8410
+ case "update":
8411
+ return state.map((e) => (e.provider === action.provider ? { ...e, value: action.value } : e));
8412
+ }
8413
+ }, []);
8574
8414
  // This is the main async initialization.
8575
8415
  useEffect(() => {
8576
8416
  const initializeExtensionManagerAsync = async () => {
8577
- const serviceContainer = new ServiceContainer("ModularToolContainer");
8417
+ const serviceContainer = new ServiceContainer("ModularToolContainer", parentContainer);
8578
8418
  // Expose the settings store as a service so other services can read/write settings.
8579
8419
  await serviceContainer.addServiceAsync({
8580
8420
  friendlyName: "Settings Store",
8581
8421
  produces: [SettingsStoreIdentity],
8582
8422
  factory: () => settingsStore,
8583
8423
  });
8584
- // Register watcher service early since many other services will rely on it.
8585
- // TODO: Really this should be in the Inspector layer, but we would need a way
8586
- // to setup the WatcherContext.Provider before the root component is rendered
8587
- // for that to work, since components will use the WatcherContext.
8588
- await serviceContainer.addServiceAsync(WatcherServiceDefinition);
8424
+ // Expose the react context service so other services can add React context providers.
8425
+ await serviceContainer.addServiceAsync({
8426
+ friendlyName: "React Context Service",
8427
+ produces: [ReactContextServiceIdentity],
8428
+ factory: () => ({
8429
+ addContext(provider, initialValue, options) {
8430
+ const typedProvider = provider;
8431
+ updateContexts({ type: "add", entry: { provider: typedProvider, value: initialValue, order: options?.order ?? 0 } });
8432
+ return {
8433
+ updateValue: (newValue) => {
8434
+ updateContexts({ type: "update", provider: typedProvider, value: newValue });
8435
+ },
8436
+ dispose: () => {
8437
+ updateContexts({ type: "remove", provider: typedProvider });
8438
+ },
8439
+ };
8440
+ },
8441
+ }),
8442
+ });
8589
8443
  // Register the shell service (top level toolbar/side pane UI layout).
8590
8444
  await serviceContainer.addServiceAsync(MakeShellServiceDefinition(options));
8591
8445
  // Register a service that simply consumes the services we need before first render.
8592
8446
  await serviceContainer.addServiceAsync({
8593
8447
  friendlyName: "Service Bootstrapper",
8594
- consumes: [RootComponentServiceIdentity, WatcherServiceIdentity],
8595
- factory: (rootComponentService, watcherService) => {
8448
+ consumes: [RootComponentServiceIdentity],
8449
+ factory: (rootComponent) => {
8596
8450
  // Use function syntax for the state setter since the root component may be a function component.
8597
- setBootstrapServices({ rootComponentService, watcherService });
8451
+ setRootComponentService(() => rootComponent);
8598
8452
  return {
8599
- dispose: () => setBootstrapServices(undefined),
8453
+ dispose: () => setRootComponentService(undefined),
8600
8454
  };
8601
8455
  },
8602
8456
  });
@@ -8608,7 +8462,7 @@ function MakeModularTool(options) {
8608
8462
  }
8609
8463
  // Register the extension list service (for browsing/installing extensions) if extension feeds are provided.
8610
8464
  if (extensionFeeds.length > 0) {
8611
- const { ExtensionListServiceDefinition } = await import('./extensionsListService-zYdKn8uM.js');
8465
+ const { ExtensionListServiceDefinition } = await import('./extensionsListService-eRZtqcfj.js');
8612
8466
  await serviceContainer.addServiceAsync(ExtensionListServiceDefinition);
8613
8467
  }
8614
8468
  // Register all external services (that make up a unique tool).
@@ -8675,36 +8529,1203 @@ function MakeModularTool(options) {
8675
8529
  setExtensionInstallError(undefined);
8676
8530
  }, [setExtensionInstallError]);
8677
8531
  // Show a spinner until a main view has been set.
8678
- if (!bootstrapServices) {
8679
- return (jsx(SettingsStoreContext.Provider, { value: settingsStore, children: jsx(Theme, { className: classes.app, children: jsx(Spinner, { className: classes.spinner }) }) }));
8532
+ if (!rootComponentService) {
8533
+ return (jsx(ReactContextsWrapper, { contexts: contexts, children: jsx(SettingsStoreContext.Provider, { value: settingsStore, children: jsx(Theme, { className: classes.app, children: jsx(Spinner, { className: classes.spinner }) }) }) }));
8680
8534
  }
8681
8535
  else {
8682
8536
  // eslint-disable-next-line @typescript-eslint/naming-convention
8683
- const Content = bootstrapServices.rootComponentService.rootComponent;
8684
- return (
8685
- // Expose the settings store as a React context so that UI components can read/write
8686
- // settings without the ISettingsService needing to be explicitly passed around.
8687
- jsx(SettingsStoreContext.Provider, { value: settingsStore, children: jsx(WatcherContext.Provider, { value: bootstrapServices.watcherService, children: jsx(ExtensionManagerContext.Provider, { value: extensionManagerContext, children: jsx(Theme, { className: classes.app, children: jsxs(ToastProvider, { children: [jsx(Dialog, { open: !!requiredExtensions, modalType: "alert", children: jsx(DialogSurface, { children: jsxs(DialogBody, { children: [jsx(DialogTitle, { children: "Required Extensions" }), jsxs(DialogContent, { children: ["Opening this URL requires the following extensions to be installed and enabled:", jsx("ul", { children: requiredExtensions?.map((name) => jsx("li", { children: name }, name)) })] }), jsxs(DialogActions, { children: [jsx(Button$1, { appearance: "primary", onClick: onAcceptRequiredExtensions, children: "Install & Enable" }), jsx(Button$1, { appearance: "secondary", onClick: onRejectRequiredExtensions, children: "No Thanks" })] })] }) }) }), jsx(Dialog, { open: !!extensionInstallError, modalType: "alert", children: jsx(DialogSurface, { children: jsxs(DialogBody, { children: [jsx(DialogTitle, { children: jsxs("div", { className: classes.extensionErrorTitleDiv, children: ["Extension Install Error", jsx(ErrorCircleRegular, { className: classes.extensionErrorIcon })] }) }), jsx(DialogContent, { children: jsxs(List$1, { children: [jsx(ListItem, { children: jsx(Body1, { children: `Extension "${extensionInstallError?.extension.name}" failed to install and was removed.` }) }), jsx(ListItem, { children: jsx(Body1, { children: `${extensionInstallError?.error}` }) })] }) }), jsx(DialogActions, { children: jsx(Button$1, { appearance: "primary", onClick: onAcknowledgedExtensionInstallError, children: "Close" }) })] }) }) }), jsx(Suspense, { fallback: jsx(Spinner, { className: classes.spinner }), children: jsx(Content, {}) })] }) }) }) }) }));
8537
+ const Content = rootComponentService.rootComponent;
8538
+ return (jsx(ReactContextsWrapper, { contexts: contexts, children: jsx(SettingsStoreContext.Provider, { value: settingsStore, children: jsx(ExtensionManagerContext.Provider, { value: extensionManagerContext, children: jsx(Theme, { className: classes.app, children: jsxs(ToastProvider, { children: [jsx(Dialog, { open: !!requiredExtensions, modalType: "alert", children: jsx(DialogSurface, { children: jsxs(DialogBody, { children: [jsx(DialogTitle, { children: "Required Extensions" }), jsxs(DialogContent, { children: ["Opening this URL requires the following extensions to be installed and enabled:", jsx("ul", { children: requiredExtensions?.map((name) => (jsx("li", { children: name }, name))) })] }), jsxs(DialogActions, { children: [jsx(Button$1, { appearance: "primary", onClick: onAcceptRequiredExtensions, children: "Install & Enable" }), jsx(Button$1, { appearance: "secondary", onClick: onRejectRequiredExtensions, children: "No Thanks" })] })] }) }) }), jsx(Dialog, { open: !!extensionInstallError, modalType: "alert", children: jsx(DialogSurface, { children: jsxs(DialogBody, { children: [jsx(DialogTitle, { children: jsxs("div", { className: classes.extensionErrorTitleDiv, children: ["Extension Install Error", jsx(ErrorCircleRegular, { className: classes.extensionErrorIcon })] }) }), jsx(DialogContent, { children: jsxs(List$1, { children: [jsx(ListItem, { children: jsx(Body1, { children: `Extension "${extensionInstallError?.extension.name}" failed to install and was removed.` }) }), jsx(ListItem, { children: jsx(Body1, { children: `${extensionInstallError?.error}` }) })] }) }), jsx(DialogActions, { children: jsx(Button$1, { appearance: "primary", onClick: onAcknowledgedExtensionInstallError, children: "Close" }) })] }) }) }), jsx(Suspense, { fallback: jsx(Spinner, { className: classes.spinner }), children: jsx(Content, {}) })] }) }) }) }) }));
8688
8539
  }
8689
8540
  };
8690
- // Set the container element to be a flex container so that the tool can be displayed properly.
8691
- const originalContainerElementDisplay = containerElement.style.display;
8692
- containerElement.style.display = "flex";
8693
- // Create and render the react root component.
8694
- const reactRoot = createRoot(containerElement);
8695
- reactRoot.render(createElement(modularToolRootComponent));
8696
- let disposed = false;
8697
- return {
8698
- dispose: () => {
8699
- // Unmount and restore the original container element display.
8700
- if (!disposed) {
8701
- disposed = true;
8702
- reactRoot.unmount();
8703
- containerElement.style.display = originalContainerElementDisplay;
8704
- }
8705
- },
8541
+ // Set the container element to be a flex container so that the tool can be displayed properly.
8542
+ const originalContainerElementDisplay = containerElement.style.display;
8543
+ containerElement.style.display = "flex";
8544
+ // Create and render the react root component.
8545
+ const reactRoot = createRoot(containerElement);
8546
+ reactRoot.render(createElement(modularToolRootComponent));
8547
+ let disposed = false;
8548
+ return {
8549
+ dispose: () => {
8550
+ // Unmount and restore the original container element display.
8551
+ if (!disposed) {
8552
+ disposed = true;
8553
+ reactRoot.unmount();
8554
+ containerElement.style.display = originalContainerElementDisplay;
8555
+ }
8556
+ },
8557
+ };
8558
+ }
8559
+
8560
+ // Created by running the following command from the packages/dev/inspector-v2 directory:
8561
+ // npm run makeAvatar https://raw.githubusercontent.com/BabylonJS/Brand-Toolkit/master/babylon_logo/fullColor/babylon_logo_color.png 0.8
8562
+ const BabylonLogoBase64 = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsSAAALEgHS3X78AAAFXElEQVR4nO2WbWwTdRzHu+EbYmLgpeILwcQXvvCFLwwGxthG1/Z6d33Ag22FDXAE98wixsCbSiS+4IUhxoQYoyiSGKcogsjaa9c9dw9sbdfrbevW611nYTDYliCRsbv7mrt2sPiGUTZf8Uua9P7/u9/383v4//MzGJ5bjgaDIQ8Ms66FYdYZ/m+D250PgyF/GUy+trb2wshEnfmPvKnG3fukJoZ+vI912vqapRsGg+481cy8I9RSbTONNkw32pXJatMvCcvmN7Q9t5ad1QSBJuzOpHvmo7JXkrX01zfq6Yd3mhxIHrHIQuUOee5EJR4MtN0D8Bnm5jbo37nd+c/UH1jmINXcvF6scxxL1VJ3Zo86kKol5UTVTlmsIXH3p68g35uXASgAoCqyCKBKy4Lup0UL4Bn6Q2piaKmWjM0etSNdTyvCoV2LwsES9daXbixMCZomoKpQFFkFsKg9aUtzv//QM7ln6zYdIlu6lUVuyLw8e7zsLbGGunqrXq8zhMMmOVFZqKQ/+QD3w8GMsB6xAmi/rN0PB+X0qQZ5psGGG/W0LNXT5/9x731tRSDubKquWJlN3H7H7N1GO1K1hDy5v1BJNe/BvPcilIUHj4TVxUWosqJnYMlmfz0H4X0jhANF8l915OJ8oxORCnv0vHHfi0+EcGcBBpwVW9hSaqGdIBSu0qrOXvgCyvzsUsgZ4WVR6zDKYxD59g3cPvspwuVmpZ2g4DPR0khNzcYVA3QwzGY/QT/wFpnBMuVqShKwsCSQDVZVVaQ9LG519z7qgyXAhwCmxnh4TJTSZiThM1NioKpqw4oBWKdzi89CLbDFZrSVudRIfzti0UFMpyXIioJ5fhSDH36Ma9uL0LrTiPDJU/hbSkFWZMxMpyGK4xjv64KPsCmBUit8ZlrqqqjYmBvAXpcaG+pFbGQA0egAOF8rfCZSF/dZaC06/Ll1B9qZciT5CIQED0mawMRg7+oBcEM9OgA/FsbQby1gjQS8JgreUiu8pSQ8Riv8tt2YHO6HKMbXEiCE0KWf4Skh0ElT4A+Q4CpJBAgrWGo3JkMDawzADyPcehlhFwnx0C5IdRRSdSSE/TsRqi5HIhqCmBxfA4DBbh0gFulHjLuO8bZLSJyqh3jIiGS1CeKZE0gNd0FMxiFOaj0Qx8RAT+4AHQyzmTUvAVSo2gngR0PgQkEdguND4LghxC9+A9F7EWI6iaQ0qYsnJ2JITUtIhAe1JtUB2Kc9hl3Oii1+M7XgNRIKW2pVe44fx0iXD6MTI/pxjA0HwUX6wcejSE4JEBM8knFOr3/qpoR4dzt6m4/BW2JW/CYSfgstjVQ8xUV0hWE2BQjb3SBh0zpe9hQUKz7Kif4zn4MbDoKfiIAL92E00p8RFsYwdVOCwIVw/fRp/WheKyjR4OVB0ok2whb17FvBVbz8hU6GeZMl6MudFhu6CJsWjezZXqwEXJUYuvA9eH4IY+MRSGlBb7zId+cQeK8MrduKFO8ui9xttaPDQi+yhO3bPlf1q8t9P5X5bU7Cb6EjQasdAQuteIpMi55Co9pZ14ARz1XwrX+g8/ARtBYUq57iUrmDsKk9hF2re0e7nXk3Z2FoA4khM5D0NjPrfVZ7k99E3Q6SDu0mVDwFJTJrpuE1EmgtKJHbzJTSp+2ZSYGlHS4Aejm1oWaptDlZiz6SZU+Hy/UyS9jOBsz0Qo/VDi3VPiMh95IO+M3UPdbqONnnanhpKYBVm5RhMOS5CwtfcGdH8Q7Hnre9FortJvQ6K6yF+tG/t/J1bU+LVofOJe1PMs3p8kHTRzvKtB7JPuatmfB/TU8vHgutarpz7Y/nZsjR/gUhEcwHmuaM9wAAAABJRU5ErkJggg==";
8563
+ const BabylonWebResources = {
8564
+ homepage: "https://www.babylonjs.com",
8565
+ repository: "https://github.com/BabylonJS/Babylon.js",
8566
+ bugs: "https://github.com/BabylonJS/Babylon.js/issues",
8567
+ author: {
8568
+ name: "Babylon.js",
8569
+ avatar: BabylonLogoBase64,
8570
+ },
8571
+ };
8572
+ /**
8573
+ * Well-known default built in extensions for the Inspector.
8574
+ */
8575
+ const DefaultInspectorExtensionFeed = new BuiltInsExtensionFeed("Inspector", [
8576
+ {
8577
+ name: "Quick Creation Tools (Preview)",
8578
+ description: "Adds a new panel for easy creation of various Babylon assets. This is a WIP extension...expect changes!",
8579
+ keywords: ["creation", "tools"],
8580
+ ...BabylonWebResources,
8581
+ getExtensionModuleAsync: async () => await import('./quickCreateToolsService-MzZbVrvr.js'),
8582
+ },
8583
+ {
8584
+ name: "Reflector",
8585
+ description: "Connects to the Reflector Bridge for real-time scene synchronization with the Babylon.js Sandbox.",
8586
+ keywords: ["reflector", "bridge", "sync", "sandbox", "tools"],
8587
+ ...BabylonWebResources,
8588
+ getExtensionModuleAsync: async () => await import('./reflectorService-DdPEZLjO.js'),
8589
+ },
8590
+ ]);
8591
+
8592
+ /**
8593
+ * The service identity for the inspectable command registry.
8594
+ */
8595
+ const InspectableCommandRegistryIdentity = Symbol("InspectableCommandRegistry");
8596
+
8597
+ const UniqueIdArg = {
8598
+ name: "uniqueId",
8599
+ description: "The uniqueId of the entity to query. Omit to list all entities of this type.",
8600
+ required: false,
8601
+ };
8602
+ const SyntheticUniqueIds = new WeakMap();
8603
+ function GetEntityId(entity) {
8604
+ if ("uniqueId" in entity && typeof entity.uniqueId === "number") {
8605
+ return entity.uniqueId;
8606
+ }
8607
+ let id = SyntheticUniqueIds.get(entity);
8608
+ if (!id) {
8609
+ SyntheticUniqueIds.set(entity, (id = UniqueIdGenerator.UniqueId));
8610
+ }
8611
+ return id;
8612
+ }
8613
+ function NodeSummary(entity) {
8614
+ return {
8615
+ uniqueId: entity.uniqueId,
8616
+ name: entity.name,
8617
+ className: entity.getClassName(),
8618
+ parentId: entity.parent?.uniqueId,
8619
+ };
8620
+ }
8621
+ function NamedSummary(entity) {
8622
+ return {
8623
+ uniqueId: entity.uniqueId,
8624
+ name: entity.name,
8625
+ className: entity.getClassName(),
8626
+ };
8627
+ }
8628
+ function MinimalSummary(entity) {
8629
+ return {
8630
+ uniqueId: entity.uniqueId,
8631
+ name: entity.name,
8632
+ };
8633
+ }
8634
+ function MakeQueryCommand(collection, sceneContext) {
8635
+ return {
8636
+ id: collection.id,
8637
+ description: collection.description,
8638
+ args: [UniqueIdArg],
8639
+ executeAsync: async (args) => {
8640
+ const scene = sceneContext.currentScene;
8641
+ if (!scene) {
8642
+ throw new Error("No active scene.");
8643
+ }
8644
+ const entities = collection.getEntities(scene);
8645
+ if (!entities) {
8646
+ return JSON.stringify([], null, 2);
8647
+ }
8648
+ if (!args.uniqueId) {
8649
+ return JSON.stringify(entities.map((e) => collection.getSummary(e)), null, 2);
8650
+ }
8651
+ const id = parseInt(args.uniqueId, 10);
8652
+ if (isNaN(id)) {
8653
+ throw new Error("uniqueId must be a number.");
8654
+ }
8655
+ const entity = entities.find((e) => collection.getUniqueId(e) === id);
8656
+ if (!entity) {
8657
+ throw new Error(`No ${collection.id.replace("query-", "")} found with uniqueId ${id}.`);
8658
+ }
8659
+ return JSON.stringify(collection.serialize ? collection.serialize(entity) : collection.getSummary(entity), null, 2);
8660
+ },
8661
+ };
8662
+ }
8663
+ /**
8664
+ * Service that registers CLI commands for querying scene entities by uniqueId.
8665
+ * When uniqueId is omitted, returns a summary list of all entities of that type.
8666
+ */
8667
+ const EntityQueryServiceDefinition = {
8668
+ friendlyName: "Entity Query Service",
8669
+ consumes: [InspectableCommandRegistryIdentity, SceneContextIdentity],
8670
+ factory: (commandRegistry, sceneContext) => {
8671
+ const collections = [
8672
+ {
8673
+ id: "query-mesh",
8674
+ description: "List meshes, or query a specific mesh by uniqueId.",
8675
+ getEntities: (scene) => scene.meshes,
8676
+ getUniqueId: (e) => e.uniqueId,
8677
+ getSummary: NodeSummary,
8678
+ serialize: (e) => e.serialize(),
8679
+ },
8680
+ {
8681
+ id: "query-light",
8682
+ description: "List lights, or query a specific light by uniqueId.",
8683
+ getEntities: (scene) => scene.lights,
8684
+ getUniqueId: (e) => e.uniqueId,
8685
+ getSummary: NodeSummary,
8686
+ serialize: (e) => e.serialize(),
8687
+ },
8688
+ {
8689
+ id: "query-camera",
8690
+ description: "List cameras, or query a specific camera by uniqueId.",
8691
+ getEntities: (scene) => scene.cameras,
8692
+ getUniqueId: (e) => e.uniqueId,
8693
+ getSummary: NodeSummary,
8694
+ serialize: (e) => e.serialize(),
8695
+ },
8696
+ {
8697
+ id: "query-transformNode",
8698
+ description: "List transform nodes, or query a specific transform node by uniqueId.",
8699
+ getEntities: (scene) => scene.transformNodes,
8700
+ getUniqueId: (e) => e.uniqueId,
8701
+ getSummary: NodeSummary,
8702
+ serialize: (e) => e.serialize(),
8703
+ },
8704
+ {
8705
+ id: "query-material",
8706
+ description: "List materials, or query a specific material by uniqueId.",
8707
+ getEntities: (scene) => scene.materials,
8708
+ getUniqueId: (e) => e.uniqueId,
8709
+ getSummary: NamedSummary,
8710
+ serialize: (e) => e.serialize(),
8711
+ },
8712
+ {
8713
+ id: "query-texture",
8714
+ description: "List textures, or query a specific texture by uniqueId.",
8715
+ getEntities: (scene) => scene.textures,
8716
+ getUniqueId: (e) => e.uniqueId,
8717
+ getSummary: NamedSummary,
8718
+ serialize: (e) => e.serialize(),
8719
+ },
8720
+ {
8721
+ id: "query-skeleton",
8722
+ description: "List skeletons, or query a specific skeleton by uniqueId.",
8723
+ getEntities: (scene) => scene.skeletons,
8724
+ getUniqueId: (e) => e.uniqueId,
8725
+ getSummary: NamedSummary,
8726
+ serialize: (e) => e.serialize(),
8727
+ },
8728
+ {
8729
+ id: "query-geometry",
8730
+ description: "List geometries, or query a specific geometry by uniqueId.",
8731
+ getEntities: (scene) => scene.geometries,
8732
+ getUniqueId: (e) => e.uniqueId,
8733
+ getSummary: MinimalSummary,
8734
+ serialize: (e) => e.serialize(),
8735
+ },
8736
+ {
8737
+ id: "query-animation",
8738
+ description: "List animations, or query a specific animation by uniqueId.",
8739
+ getEntities: (scene) => scene.animations,
8740
+ getUniqueId: (e) => e.uniqueId,
8741
+ getSummary: MinimalSummary,
8742
+ serialize: (e) => e.serialize(),
8743
+ },
8744
+ {
8745
+ id: "query-animationGroup",
8746
+ description: "List animation groups, or query a specific animation group by uniqueId.",
8747
+ getEntities: (scene) => scene.animationGroups,
8748
+ getUniqueId: (e) => e.uniqueId,
8749
+ getSummary: NamedSummary,
8750
+ serialize: (e) => e.serialize(),
8751
+ },
8752
+ {
8753
+ id: "query-particleSystem",
8754
+ description: "List particle systems, or query a specific particle system by uniqueId.",
8755
+ getEntities: (scene) => scene.particleSystems,
8756
+ getUniqueId: (e) => e.uniqueId,
8757
+ getSummary: NamedSummary,
8758
+ serialize: (e) => e.serialize(false),
8759
+ },
8760
+ {
8761
+ id: "query-morphTargetManager",
8762
+ description: "List morph target managers, or query a specific morph target manager by uniqueId.",
8763
+ getEntities: (scene) => scene.morphTargetManagers,
8764
+ getUniqueId: (e) => e.uniqueId,
8765
+ getSummary: MinimalSummary,
8766
+ serialize: (e) => e.serialize(),
8767
+ },
8768
+ {
8769
+ id: "query-multiMaterial",
8770
+ description: "List multi-materials, or query a specific multi-material by uniqueId.",
8771
+ getEntities: (scene) => scene.multiMaterials,
8772
+ getUniqueId: (e) => e.uniqueId,
8773
+ getSummary: NamedSummary,
8774
+ serialize: (e) => e.serialize(),
8775
+ },
8776
+ {
8777
+ id: "query-postProcess",
8778
+ description: "List post-processes, or query a specific post-process by uniqueId.",
8779
+ getEntities: (scene) => scene.postProcesses,
8780
+ getUniqueId: (e) => e.uniqueId,
8781
+ getSummary: NamedSummary,
8782
+ serialize: (e) => e.serialize(),
8783
+ },
8784
+ {
8785
+ id: "query-frameGraph",
8786
+ description: "List frame graphs, or query a specific frame graph by uniqueId.",
8787
+ getEntities: (scene) => scene.frameGraphs,
8788
+ getUniqueId: (e) => e.uniqueId,
8789
+ getSummary: NamedSummary,
8790
+ },
8791
+ {
8792
+ id: "query-effectLayer",
8793
+ description: "List effect layers, or query a specific effect layer by uniqueId.",
8794
+ getEntities: (scene) => scene.effectLayers,
8795
+ getUniqueId: (e) => e.uniqueId,
8796
+ getSummary: NamedSummary,
8797
+ serialize: (e) => e.serialize?.(),
8798
+ },
8799
+ {
8800
+ id: "query-spriteManager",
8801
+ description: "List sprite managers, or query a specific sprite manager by uniqueId.",
8802
+ getEntities: (scene) => scene.spriteManagers,
8803
+ getUniqueId: (e) => e.uniqueId,
8804
+ getSummary: MinimalSummary,
8805
+ serialize: (e) => e.serialize(false),
8806
+ },
8807
+ {
8808
+ id: "query-sound",
8809
+ description: "List sounds in the main sound track, or query a specific sound by uniqueId.",
8810
+ getEntities: (scene) => scene.mainSoundTrack?.soundCollection ?? [],
8811
+ getUniqueId: (e) => GetEntityId(e),
8812
+ getSummary: (e) => ({ uniqueId: GetEntityId(e), name: e.name, className: e.getClassName() }),
8813
+ serialize: (e) => e.serialize(),
8814
+ },
8815
+ {
8816
+ id: "query-renderingPipeline",
8817
+ description: "List rendering pipelines, or query a specific rendering pipeline by uniqueId.",
8818
+ getEntities: (scene) => scene.postProcessRenderPipelineManager?.supportedPipelines ?? [],
8819
+ getUniqueId: (e) => e.uniqueId,
8820
+ getSummary: NamedSummary,
8821
+ },
8822
+ ];
8823
+ const registrations = collections.map((col) => commandRegistry.addCommand(MakeQueryCommand(col, sceneContext)));
8824
+ return {
8825
+ dispose: () => {
8826
+ for (const reg of registrations) {
8827
+ reg.dispose();
8828
+ }
8829
+ },
8830
+ };
8831
+ },
8832
+ };
8833
+
8834
+ /**
8835
+ * The service identity for the CLI connection status.
8836
+ */
8837
+ const CliConnectionStatusIdentity = Symbol("CliConnectionStatus");
8838
+
8839
+ /**
8840
+ * Creates the service definition for the InspectableBridgeService.
8841
+ * @param options The options for connecting to the bridge.
8842
+ * @returns A service definition that produces an IInspectableCommandRegistry.
8843
+ */
8844
+ function MakeInspectableBridgeServiceDefinition(options) {
8845
+ return {
8846
+ friendlyName: "Inspectable Bridge Service",
8847
+ produces: [InspectableCommandRegistryIdentity, CliConnectionStatusIdentity],
8848
+ factory: () => {
8849
+ const commands = new Map();
8850
+ let ws = null;
8851
+ let reconnectTimer = null;
8852
+ let disposed = false;
8853
+ let connected = false;
8854
+ const onConnectionStatusChanged = new Observable();
8855
+ function setConnected(value) {
8856
+ if (connected !== value) {
8857
+ connected = value;
8858
+ onConnectionStatusChanged.notifyObservers(value);
8859
+ }
8860
+ }
8861
+ function sendToBridge(message) {
8862
+ ws?.send(JSON.stringify(message));
8863
+ }
8864
+ function connect() {
8865
+ if (disposed) {
8866
+ return;
8867
+ }
8868
+ try {
8869
+ ws = new WebSocket(`ws://127.0.0.1:${options.port}`);
8870
+ }
8871
+ catch {
8872
+ scheduleReconnect();
8873
+ return;
8874
+ }
8875
+ ws.onopen = () => {
8876
+ setConnected(true);
8877
+ sendToBridge({ type: "register", name: options.name });
8878
+ };
8879
+ ws.onmessage = (event) => {
8880
+ try {
8881
+ const message = JSON.parse(event.data);
8882
+ void handleMessage(message);
8883
+ }
8884
+ catch {
8885
+ Logger.Warn("InspectableBridgeService: Failed to parse message from bridge.");
8886
+ }
8887
+ };
8888
+ ws.onclose = () => {
8889
+ ws = null;
8890
+ setConnected(false);
8891
+ scheduleReconnect();
8892
+ };
8893
+ ws.onerror = () => {
8894
+ // onclose will fire after onerror, which handles reconnection.
8895
+ };
8896
+ }
8897
+ function scheduleReconnect() {
8898
+ if (disposed || reconnectTimer !== null) {
8899
+ return;
8900
+ }
8901
+ reconnectTimer = setTimeout(() => {
8902
+ reconnectTimer = null;
8903
+ connect();
8904
+ }, 3000);
8905
+ }
8906
+ async function handleMessage(message) {
8907
+ switch (message.type) {
8908
+ case "listCommands": {
8909
+ const commandList = Array.from(commands.values()).map((cmd) => ({
8910
+ id: cmd.id,
8911
+ description: cmd.description,
8912
+ args: cmd.args,
8913
+ }));
8914
+ sendToBridge({
8915
+ type: "commandListResponse",
8916
+ requestId: message.requestId,
8917
+ commands: commandList,
8918
+ });
8919
+ break;
8920
+ }
8921
+ case "execCommand": {
8922
+ const command = commands.get(message.commandId);
8923
+ if (!command) {
8924
+ sendToBridge({
8925
+ type: "commandResponse",
8926
+ requestId: message.requestId,
8927
+ error: `Unknown command: ${message.commandId}`,
8928
+ });
8929
+ break;
8930
+ }
8931
+ try {
8932
+ const result = await command.executeAsync(message.args);
8933
+ sendToBridge({
8934
+ type: "commandResponse",
8935
+ requestId: message.requestId,
8936
+ result,
8937
+ });
8938
+ }
8939
+ catch (error) {
8940
+ sendToBridge({
8941
+ type: "commandResponse",
8942
+ requestId: message.requestId,
8943
+ error: String(error),
8944
+ });
8945
+ }
8946
+ break;
8947
+ }
8948
+ }
8949
+ }
8950
+ // Initiate connection.
8951
+ connect();
8952
+ const registry = {
8953
+ addCommand(descriptor) {
8954
+ if (commands.has(descriptor.id)) {
8955
+ throw new Error(`Command '${descriptor.id}' is already registered.`);
8956
+ }
8957
+ commands.set(descriptor.id, descriptor);
8958
+ return {
8959
+ dispose: () => {
8960
+ commands.delete(descriptor.id);
8961
+ },
8962
+ };
8963
+ },
8964
+ get isConnected() {
8965
+ return connected;
8966
+ },
8967
+ onConnectionStatusChanged,
8968
+ dispose: () => {
8969
+ disposed = true;
8970
+ if (reconnectTimer !== null) {
8971
+ clearTimeout(reconnectTimer);
8972
+ reconnectTimer = null;
8973
+ }
8974
+ commands.clear();
8975
+ setConnected(false);
8976
+ onConnectionStatusChanged.clear();
8977
+ if (ws) {
8978
+ ws.onclose = null;
8979
+ ws.close();
8980
+ ws = null;
8981
+ }
8982
+ },
8983
+ };
8984
+ return registry;
8985
+ },
8986
+ };
8987
+ }
8988
+
8989
+ /**
8990
+ * Service that registers CLI commands for performance tracing using the PerformanceViewerCollector.
8991
+ * start-perf-trace begins collecting data, stop-perf-trace stops and returns the collected data as JSON.
8992
+ */
8993
+ const PerfTraceCommandServiceDefinition = {
8994
+ friendlyName: "Perf Trace Command Service",
8995
+ consumes: [InspectableCommandRegistryIdentity, SceneContextIdentity],
8996
+ factory: (commandRegistry, sceneContext) => {
8997
+ let perfCollector;
8998
+ const startReg = commandRegistry.addCommand({
8999
+ id: "start-perf-trace",
9000
+ description: "Start collecting performance trace data.",
9001
+ executeAsync: async () => {
9002
+ const scene = sceneContext.currentScene;
9003
+ if (!scene) {
9004
+ throw new Error("No active scene.");
9005
+ }
9006
+ if (perfCollector?.isStarted) {
9007
+ return "Performance trace is already running.";
9008
+ }
9009
+ perfCollector = scene.getPerfCollector();
9010
+ perfCollector.stop();
9011
+ perfCollector.clear(false);
9012
+ perfCollector.addCollectionStrategies(...DefaultPerfStrategies);
9013
+ perfCollector.start(true);
9014
+ return "Performance trace started.";
9015
+ },
9016
+ });
9017
+ const stopReg = commandRegistry.addCommand({
9018
+ id: "stop-perf-trace",
9019
+ description: "Stop collecting performance trace data and return the results as JSON.",
9020
+ executeAsync: async () => {
9021
+ if (!perfCollector || !perfCollector.isStarted) {
9022
+ throw new Error("Performance trace is not running. Run start-perf-trace first.");
9023
+ }
9024
+ perfCollector.stop();
9025
+ const datasets = perfCollector.datasets;
9026
+ const ids = datasets.ids;
9027
+ const rawData = datasets.data.subarray(0, datasets.data.itemLength);
9028
+ const sliceSize = ids.length + PerformanceViewerCollector.SliceDataOffset;
9029
+ const samples = [];
9030
+ for (let i = 0; i < rawData.length; i += sliceSize) {
9031
+ const timestamp = rawData[i];
9032
+ const sample = { timestamp };
9033
+ for (let j = 0; j < ids.length; j++) {
9034
+ sample[ids[j]] = rawData[i + PerformanceViewerCollector.SliceDataOffset + j];
9035
+ }
9036
+ samples.push(sample);
9037
+ }
9038
+ perfCollector.clear(false);
9039
+ perfCollector = undefined;
9040
+ return JSON.stringify({ strategies: ids, sampleCount: samples.length, samples }, null, 2);
9041
+ },
9042
+ });
9043
+ return {
9044
+ dispose: () => {
9045
+ startReg.dispose();
9046
+ stopReg.dispose();
9047
+ if (perfCollector?.isStarted) {
9048
+ perfCollector.stop();
9049
+ }
9050
+ perfCollector = undefined;
9051
+ },
9052
+ };
9053
+ },
9054
+ };
9055
+
9056
+ /**
9057
+ * Service that registers a CLI command for capturing a screenshot of the scene.
9058
+ * Returns the image as a base64 data string, suitable for consumption by AI agents.
9059
+ */
9060
+ const ScreenshotCommandServiceDefinition = {
9061
+ friendlyName: "Screenshot Command Service",
9062
+ consumes: [InspectableCommandRegistryIdentity, SceneContextIdentity],
9063
+ factory: (commandRegistry, sceneContext) => {
9064
+ const registration = commandRegistry.addCommand({
9065
+ id: "take-screenshot",
9066
+ description: "Capture a screenshot of the scene. Returns base64-encoded PNG data.",
9067
+ args: [
9068
+ {
9069
+ name: "cameraUniqueId",
9070
+ description: "The uniqueId of the camera to use. Defaults to the active camera.",
9071
+ required: false,
9072
+ },
9073
+ {
9074
+ name: "width",
9075
+ description: "Screenshot width in pixels. When set, uses custom size mode.",
9076
+ required: false,
9077
+ },
9078
+ {
9079
+ name: "height",
9080
+ description: "Screenshot height in pixels. When set, uses custom size mode.",
9081
+ required: false,
9082
+ },
9083
+ {
9084
+ name: "precision",
9085
+ description: "Resolution multiplier (e.g. 2 for double resolution). Defaults to 1.",
9086
+ required: false,
9087
+ },
9088
+ ],
9089
+ executeAsync: async (args) => {
9090
+ const scene = sceneContext.currentScene;
9091
+ if (!scene) {
9092
+ throw new Error("No active scene.");
9093
+ }
9094
+ const engine = scene.getEngine();
9095
+ // Resolve camera: explicit uniqueId, or active/frame-graph camera.
9096
+ let camera;
9097
+ if (args.cameraUniqueId) {
9098
+ const cameraId = parseInt(args.cameraUniqueId, 10);
9099
+ if (isNaN(cameraId)) {
9100
+ throw new Error("cameraUniqueId must be a number.");
9101
+ }
9102
+ camera = scene.cameras.find((c) => c.uniqueId === cameraId);
9103
+ if (!camera) {
9104
+ throw new Error(`No camera found with uniqueId ${cameraId}.`);
9105
+ }
9106
+ }
9107
+ else {
9108
+ camera = scene.frameGraph ? FrameGraphUtils.FindMainCamera(scene.frameGraph) : scene.activeCamera;
9109
+ }
9110
+ if (!camera) {
9111
+ throw new Error("No camera available for screenshot.");
9112
+ }
9113
+ const precision = args.precision !== undefined ? Number(args.precision) : 1;
9114
+ if (!Number.isFinite(precision) || precision <= 0) {
9115
+ throw new Error("precision must be a finite number greater than 0.");
9116
+ }
9117
+ let width;
9118
+ if (args.width !== undefined) {
9119
+ width = Number(args.width);
9120
+ if (!Number.isFinite(width) || width <= 0 || !Number.isInteger(width)) {
9121
+ throw new Error("width must be a finite positive integer.");
9122
+ }
9123
+ }
9124
+ let height;
9125
+ if (args.height !== undefined) {
9126
+ height = Number(args.height);
9127
+ if (!Number.isFinite(height) || height <= 0 || !Number.isInteger(height)) {
9128
+ throw new Error("height must be a finite positive integer.");
9129
+ }
9130
+ }
9131
+ const screenshotSize = width !== undefined && height !== undefined ? { width, height, precision } : { precision };
9132
+ // Omit fileName to get data URL back without triggering a download.
9133
+ const dataUrl = await CreateScreenshotUsingRenderTargetAsync(engine, camera, screenshotSize, "image/png");
9134
+ // Strip the data URI prefix to return raw base64, which is what AI agent APIs expect.
9135
+ const commaIndex = dataUrl.indexOf(",");
9136
+ return commaIndex !== -1 ? dataUrl.substring(commaIndex + 1) : dataUrl;
9137
+ },
9138
+ });
9139
+ return {
9140
+ dispose: () => {
9141
+ registration.dispose();
9142
+ },
9143
+ };
9144
+ },
9145
+ };
9146
+
9147
+ /**
9148
+ * Service that registers a CLI command for retrieving compiled shader code from a material.
9149
+ */
9150
+ const ShaderCommandServiceDefinition = {
9151
+ friendlyName: "Shader Command Service",
9152
+ consumes: [InspectableCommandRegistryIdentity, SceneContextIdentity],
9153
+ factory: (commandRegistry, sceneContext) => {
9154
+ const registration = commandRegistry.addCommand({
9155
+ id: "get-shader-code",
9156
+ description: "Get the shader code for a material by uniqueId.",
9157
+ args: [
9158
+ {
9159
+ name: "uniqueId",
9160
+ description: "The uniqueId of the material.",
9161
+ required: true,
9162
+ },
9163
+ {
9164
+ name: "variant",
9165
+ description: "Which shader variant to return: compiled (default), raw, or beforeMigration.",
9166
+ required: false,
9167
+ },
9168
+ ],
9169
+ executeAsync: async (args) => {
9170
+ const scene = sceneContext.currentScene;
9171
+ if (!scene) {
9172
+ throw new Error("No active scene.");
9173
+ }
9174
+ const id = parseInt(args.uniqueId, 10);
9175
+ if (isNaN(id)) {
9176
+ throw new Error("uniqueId must be a number.");
9177
+ }
9178
+ const material = scene.materials.find((m) => m.uniqueId === id);
9179
+ if (!material) {
9180
+ throw new Error(`No material found with uniqueId ${id}.`);
9181
+ }
9182
+ const effect = material.getEffect();
9183
+ if (!effect) {
9184
+ throw new Error(`Material "${material.name}" has no effect. It may not have been rendered yet.`);
9185
+ }
9186
+ if (!effect.isReady()) {
9187
+ throw new Error(`Material "${material.name}" effect is not ready. Wait for it to be rendered.`);
9188
+ }
9189
+ const variant = args.variant ?? "compiled";
9190
+ let vertexShader;
9191
+ let fragmentShader;
9192
+ switch (variant) {
9193
+ case "compiled":
9194
+ vertexShader = effect.vertexSourceCode;
9195
+ fragmentShader = effect.fragmentSourceCode;
9196
+ break;
9197
+ case "raw":
9198
+ vertexShader = effect.rawVertexSourceCode;
9199
+ fragmentShader = effect.rawFragmentSourceCode;
9200
+ break;
9201
+ case "beforeMigration":
9202
+ vertexShader = effect.vertexSourceCodeBeforeMigration;
9203
+ fragmentShader = effect.fragmentSourceCodeBeforeMigration;
9204
+ break;
9205
+ default:
9206
+ throw new Error(`Unknown variant "${variant}". Use: compiled, raw, or beforeMigration.`);
9207
+ }
9208
+ return JSON.stringify({ vertexShader, fragmentShader }, null, 2);
9209
+ },
9210
+ });
9211
+ return {
9212
+ dispose: () => {
9213
+ registration.dispose();
9214
+ },
9215
+ };
9216
+ },
9217
+ };
9218
+
9219
+ /**
9220
+ * Service that registers CLI commands for querying scene and engine statistics.
9221
+ */
9222
+ const StatsCommandServiceDefinition = {
9223
+ friendlyName: "Stats Command Service",
9224
+ consumes: [InspectableCommandRegistryIdentity, SceneContextIdentity],
9225
+ factory: (commandRegistry, sceneContext) => {
9226
+ let sceneInstrumentation;
9227
+ let engineInstrumentation;
9228
+ function disposeInstrumentation() {
9229
+ sceneInstrumentation?.dispose();
9230
+ sceneInstrumentation = undefined;
9231
+ engineInstrumentation?.dispose();
9232
+ engineInstrumentation = undefined;
9233
+ }
9234
+ const startPerfReg = commandRegistry.addCommand({
9235
+ id: "start-perf-instrumentation",
9236
+ description: "Start scene and engine performance instrumentation for frame stats collection.",
9237
+ executeAsync: async () => {
9238
+ const scene = sceneContext.currentScene;
9239
+ if (!scene) {
9240
+ throw new Error("No active scene.");
9241
+ }
9242
+ // Dispose any stale instrumentation (e.g. scene changed).
9243
+ if (sceneInstrumentation && sceneInstrumentation.scene !== scene) {
9244
+ disposeInstrumentation();
9245
+ }
9246
+ if (sceneInstrumentation) {
9247
+ return "Performance instrumentation is already running.";
9248
+ }
9249
+ sceneInstrumentation = new SceneInstrumentation(scene);
9250
+ sceneInstrumentation.captureActiveMeshesEvaluationTime = true;
9251
+ sceneInstrumentation.captureRenderTargetsRenderTime = true;
9252
+ sceneInstrumentation.captureFrameTime = true;
9253
+ sceneInstrumentation.captureRenderTime = true;
9254
+ sceneInstrumentation.captureInterFrameTime = true;
9255
+ sceneInstrumentation.captureParticlesRenderTime = true;
9256
+ sceneInstrumentation.captureSpritesRenderTime = true;
9257
+ sceneInstrumentation.capturePhysicsTime = true;
9258
+ sceneInstrumentation.captureAnimationsTime = true;
9259
+ engineInstrumentation = new EngineInstrumentation(scene.getEngine());
9260
+ engineInstrumentation.captureGPUFrameTime = true;
9261
+ return "Performance instrumentation started.";
9262
+ },
9263
+ });
9264
+ const stopPerfReg = commandRegistry.addCommand({
9265
+ id: "stop-perf-instrumentation",
9266
+ description: "Stop scene and engine performance instrumentation.",
9267
+ executeAsync: async () => {
9268
+ if (!sceneInstrumentation && !engineInstrumentation) {
9269
+ return "Performance instrumentation is not running.";
9270
+ }
9271
+ disposeInstrumentation();
9272
+ return "Performance instrumentation stopped.";
9273
+ },
9274
+ });
9275
+ const countStatsReg = commandRegistry.addCommand({
9276
+ id: "get-count-stats",
9277
+ description: "Get scene entity counts (meshes, lights, vertices, draw calls, etc.).",
9278
+ executeAsync: async () => {
9279
+ const scene = sceneContext.currentScene;
9280
+ if (!scene) {
9281
+ throw new Error("No active scene.");
9282
+ }
9283
+ let activeMeshesCount = scene.getActiveMeshes().length;
9284
+ for (const objectRenderer of scene.objectRenderers) {
9285
+ activeMeshesCount += objectRenderer.getActiveMeshes().length;
9286
+ }
9287
+ const activeIndices = scene.getActiveIndices();
9288
+ return JSON.stringify({
9289
+ totalMeshes: scene.meshes.length,
9290
+ activeMeshes: activeMeshesCount,
9291
+ activeIndices,
9292
+ activeFaces: Math.floor(activeIndices / 3),
9293
+ activeBones: scene.getActiveBones(),
9294
+ activeParticles: scene.getActiveParticles(),
9295
+ drawCalls: scene.getEngine()._drawCalls.current,
9296
+ totalLights: scene.lights.length,
9297
+ totalVertices: scene.getTotalVertices(),
9298
+ totalMaterials: scene.materials.length,
9299
+ totalTextures: scene.textures.length,
9300
+ }, null, 2);
9301
+ },
9302
+ });
9303
+ const frameStatsReg = commandRegistry.addCommand({
9304
+ id: "get-frame-stats",
9305
+ description: "Get frame timing statistics. Requires start-perf-instrumentation to be run first.",
9306
+ executeAsync: async () => {
9307
+ if (!sceneInstrumentation || !engineInstrumentation) {
9308
+ throw new Error("Performance instrumentation is not running. Run start-perf-instrumentation first.");
9309
+ }
9310
+ const si = sceneInstrumentation;
9311
+ const ei = engineInstrumentation;
9312
+ const round = (v) => Math.round(v * 100) / 100;
9313
+ return JSON.stringify({
9314
+ absoluteFPS: Math.floor(1000.0 / si.frameTimeCounter.lastSecAverage),
9315
+ meshesSelectionMs: round(si.activeMeshesEvaluationTimeCounter.lastSecAverage),
9316
+ renderTargetsMs: round(si.renderTargetsRenderTimeCounter.lastSecAverage),
9317
+ particlesMs: round(si.particlesRenderTimeCounter.lastSecAverage),
9318
+ spritesMs: round(si.spritesRenderTimeCounter.lastSecAverage),
9319
+ animationsMs: round(si.animationsTimeCounter.lastSecAverage),
9320
+ physicsMs: round(si.physicsTimeCounter.lastSecAverage),
9321
+ renderMs: round(si.renderTimeCounter.lastSecAverage),
9322
+ frameMs: round(si.frameTimeCounter.lastSecAverage),
9323
+ interFrameMs: round(si.interFrameTimeCounter.lastSecAverage),
9324
+ gpuFrameMs: round(ei.gpuFrameTimeCounter.lastSecAverage * 0.000001),
9325
+ gpuFrameAverageMs: round(ei.gpuFrameTimeCounter.average * 0.000001),
9326
+ }, null, 2);
9327
+ },
9328
+ });
9329
+ const systemStatsReg = commandRegistry.addCommand({
9330
+ id: "get-system-stats",
9331
+ description: "Get engine capabilities and system information.",
9332
+ executeAsync: async () => {
9333
+ const scene = sceneContext.currentScene;
9334
+ if (!scene) {
9335
+ throw new Error("No active scene.");
9336
+ }
9337
+ const engine = scene.getEngine();
9338
+ const caps = engine.getCaps();
9339
+ return JSON.stringify({
9340
+ resolution: `${engine.getRenderWidth()} x ${engine.getRenderHeight()}`,
9341
+ hardwareScalingLevel: engine.getHardwareScalingLevel(),
9342
+ engine: engine.description,
9343
+ driver: engine.extractDriverInfo(),
9344
+ capabilities: {
9345
+ stdDerivatives: caps.standardDerivatives,
9346
+ compressedTextures: caps.s3tc !== undefined,
9347
+ hardwareInstances: caps.instancedArrays,
9348
+ textureFloat: caps.textureFloat,
9349
+ textureHalfFloat: caps.textureHalfFloat,
9350
+ renderToTextureFloat: caps.textureFloatRender,
9351
+ renderToTextureHalfFloat: caps.textureHalfFloatRender,
9352
+ indices32Bit: caps.uintIndices,
9353
+ fragmentDepth: caps.fragmentDepthSupported,
9354
+ highPrecisionShaders: caps.highPrecisionShaderSupported,
9355
+ drawBuffers: caps.drawBuffersExtension,
9356
+ vertexArrayObject: caps.vertexArrayObject,
9357
+ timerQuery: caps.timerQuery !== undefined,
9358
+ stencil: engine.isStencilEnable,
9359
+ parallelShaderCompilation: caps.parallelShaderCompile != null,
9360
+ maxTexturesUnits: caps.maxTexturesImageUnits,
9361
+ maxTexturesSize: caps.maxTextureSize,
9362
+ maxAnisotropy: caps.maxAnisotropy,
9363
+ },
9364
+ }, null, 2);
9365
+ },
9366
+ });
9367
+ return {
9368
+ dispose: () => {
9369
+ startPerfReg.dispose();
9370
+ stopPerfReg.dispose();
9371
+ countStatsReg.dispose();
9372
+ frameStatsReg.dispose();
9373
+ systemStatsReg.dispose();
9374
+ disposeInstrumentation();
9375
+ },
9376
+ };
9377
+ },
9378
+ };
9379
+
9380
+ const DefaultPort = 4400;
9381
+ const InspectableStates = new Map();
9382
+ /**
9383
+ * @internal
9384
+ * Internal implementation that returns an {@link InternalInspectableToken} with access
9385
+ * to the underlying ServiceContainer. Used by ShowInspector to set up a parent container relationship.
9386
+ */
9387
+ function _StartInspectable(scene, options) {
9388
+ let state = InspectableStates.get(scene);
9389
+ if (!state) {
9390
+ const port = options?.port ?? DefaultPort;
9391
+ const name = options?.name ?? (typeof document !== "undefined" ? document.title : "Babylon.js Scene");
9392
+ const serviceContainer = new ServiceContainer("InspectableContainer");
9393
+ const fullyDispose = () => {
9394
+ InspectableStates.delete(scene);
9395
+ serviceContainer.dispose();
9396
+ sceneDisposeObserver.remove();
9397
+ };
9398
+ // Initialize the service container asynchronously.
9399
+ const sceneContextServiceDefinition = {
9400
+ friendlyName: "Inspectable Scene Context",
9401
+ produces: [SceneContextIdentity],
9402
+ factory: () => ({
9403
+ currentScene: scene,
9404
+ currentSceneObservable: new Observable(),
9405
+ }),
9406
+ };
9407
+ const readyPromise = (async () => {
9408
+ await serviceContainer.addServicesAsync(sceneContextServiceDefinition, MakeInspectableBridgeServiceDefinition({
9409
+ port,
9410
+ name,
9411
+ }), EntityQueryServiceDefinition, ScreenshotCommandServiceDefinition, ShaderCommandServiceDefinition, StatsCommandServiceDefinition, PerfTraceCommandServiceDefinition);
9412
+ })();
9413
+ state = {
9414
+ refCount: 0,
9415
+ serviceContainer,
9416
+ sceneDisposeObserver: { remove: () => { } },
9417
+ fullyDispose,
9418
+ readyPromise,
9419
+ };
9420
+ const capturedState = state;
9421
+ InspectableStates.set(scene, state);
9422
+ // Auto-dispose when the scene is disposed.
9423
+ const sceneDisposeObserver = scene.onDisposeObservable.addOnce(() => {
9424
+ capturedState.refCount = 0;
9425
+ capturedState.fullyDispose();
9426
+ });
9427
+ state.sceneDisposeObserver = sceneDisposeObserver;
9428
+ // Handle initialization failure (guard against already-disposed state).
9429
+ void (async () => {
9430
+ try {
9431
+ await readyPromise;
9432
+ }
9433
+ catch (error) {
9434
+ if (InspectableStates.has(scene)) {
9435
+ Logger.Error(`Failed to initialize Inspectable: ${error}`);
9436
+ capturedState.refCount = 0;
9437
+ capturedState.fullyDispose();
9438
+ }
9439
+ }
9440
+ })();
9441
+ }
9442
+ state.refCount++;
9443
+ const { serviceContainer } = state;
9444
+ const owningState = state;
9445
+ // If additional service definitions were provided, add them in a separate call
9446
+ // so they can be independently removed when this token is disposed.
9447
+ let extraServicesDisposable;
9448
+ const extraAbortController = new AbortController();
9449
+ const extraServiceDefinitions = options?.serviceDefinitions;
9450
+ if (extraServiceDefinitions && extraServiceDefinitions.length > 0) {
9451
+ // Wait for the built-in services to be ready, then add the extra ones.
9452
+ void (async () => {
9453
+ try {
9454
+ await owningState.readyPromise;
9455
+ extraServicesDisposable = await serviceContainer.addServicesAsync(...extraServiceDefinitions, extraAbortController.signal);
9456
+ }
9457
+ catch (error) {
9458
+ if (!extraAbortController.signal.aborted) {
9459
+ Logger.Error(`Failed to add extra inspectable services: ${error}`);
9460
+ }
9461
+ }
9462
+ })();
9463
+ }
9464
+ let disposed = false;
9465
+ const token = {
9466
+ get isDisposed() {
9467
+ return disposed;
9468
+ },
9469
+ get serviceContainer() {
9470
+ return serviceContainer;
9471
+ },
9472
+ dispose() {
9473
+ if (disposed) {
9474
+ return;
9475
+ }
9476
+ disposed = true;
9477
+ // Abort any in-flight extra service initialization and remove already-added extra services.
9478
+ extraAbortController.abort();
9479
+ extraServicesDisposable?.dispose();
9480
+ owningState.refCount--;
9481
+ if (owningState.refCount <= 0) {
9482
+ owningState.fullyDispose();
9483
+ }
9484
+ },
9485
+ };
9486
+ return token;
9487
+ }
9488
+ /**
9489
+ * Makes a scene inspectable by connecting it to the Inspector CLI bridge.
9490
+ * This creates a headless {@link ServiceContainer} (no UI) and registers the
9491
+ * {@link InspectableBridgeService} which opens a WebSocket to the bridge and
9492
+ * exposes a command registry for CLI-invocable commands.
9493
+ *
9494
+ * Multiple callers may call this for the same scene. Each returned token is
9495
+ * ref-counted — the underlying connection is only torn down when all tokens
9496
+ * have been disposed. Additional {@link InspectableOptions.serviceDefinitions}
9497
+ * passed by each caller are added to the shared container and removed when
9498
+ * that caller's token is disposed.
9499
+ *
9500
+ * @param scene The scene to make inspectable.
9501
+ * @param options Optional configuration.
9502
+ * @returns An {@link InspectableToken} that can be disposed to disconnect.
9503
+ * @experimental
9504
+ */
9505
+ function StartInspectable(scene, options) {
9506
+ return _StartInspectable(scene, options);
9507
+ }
9508
+
9509
+ /**
9510
+ * Reusable component which renders a color property line containing a label, colorPicker popout, and expandable RGBA values
9511
+ * The expandable RGBA values are synced sliders that allow the user to modify the color's RGBA values directly
9512
+ * @param props - PropertyLine props, replacing children with a color object so that we can properly display the color
9513
+ * @returns Component wrapping a colorPicker component with a property line
9514
+ */
9515
+ const ColorPropertyLine = forwardRef((props, ref) => {
9516
+ ColorPropertyLine.displayName = "ColorPropertyLine";
9517
+ const [color, setColor] = useState(props.value);
9518
+ useEffect(() => {
9519
+ setColor(props.value);
9520
+ }, [props.value]);
9521
+ const onSliderChange = (value, key) => {
9522
+ let newColor;
9523
+ if (key === "a") {
9524
+ newColor = Color4.FromColor3(color, value);
9525
+ }
9526
+ else {
9527
+ newColor = color.clone();
9528
+ newColor[key] = value / 255;
9529
+ }
9530
+ setColor(newColor); // Create a new object to trigger re-render
9531
+ props.onChange(newColor);
9532
+ };
9533
+ const onColorPickerChange = (newColor) => {
9534
+ setColor(newColor);
9535
+ props.onChange(newColor);
9536
+ };
9537
+ return (jsx(PropertyLine, { ref: ref, ...props, expandedContent: color ? jsx(ColorSliders, { color: color, onSliderChange: onSliderChange }) : undefined, children: jsx(ColorPickerPopup, { ...props, onChange: onColorPickerChange, value: color }) }));
9538
+ });
9539
+ const ColorSliders = ({ color, onSliderChange }) => (jsxs(Fragment, { children: [jsx(SyncedSliderPropertyLine, { label: "R", value: color.r * 255, min: 0, max: 255, onChange: (value) => onSliderChange(value, "r") }), jsx(SyncedSliderPropertyLine, { label: "G", value: color.g * 255, min: 0, max: 255, onChange: (value) => onSliderChange(value, "g") }), jsx(SyncedSliderPropertyLine, { label: "B", value: color.b * 255, min: 0, max: 255, onChange: (value) => onSliderChange(value, "b") }), color instanceof Color4 && jsx(SyncedSliderPropertyLine, { label: "A", value: color.a, min: 0, max: 1, step: 0.01, onChange: (value) => onSliderChange(value, "a") })] }));
9540
+ const Color3PropertyLine = ColorPropertyLine;
9541
+ const Color4PropertyLine = ColorPropertyLine;
9542
+
9543
+ const useStyles$F = makeStyles({
9544
+ uniformWidth: {
9545
+ ...UniformWidthStyling,
9546
+ },
9547
+ });
9548
+ /**
9549
+ * Wraps a text input in a property line
9550
+ * @param props - PropertyLineProps and InputProps
9551
+ * @returns property-line wrapped input component
9552
+ */
9553
+ const TextInputPropertyLine = (props) => {
9554
+ TextInputPropertyLine.displayName = "TextInputPropertyLine";
9555
+ const classes = useStyles$F();
9556
+ return (jsx(PropertyLine, { ...props, children: jsx(TextInput, { ...props, className: mergeClasses(classes.uniformWidth, props.className) }) }));
9557
+ };
9558
+ /**
9559
+ * Wraps a number input in a property line
9560
+ * To force integer values, use forceInt param (this is distinct from the 'step' param, which will still allow submitting an integer value. forceInt will not)
9561
+ * @param props - PropertyLineProps and InputProps
9562
+ * @returns property-line wrapped input component
9563
+ */
9564
+ const NumberInputPropertyLine = (props) => {
9565
+ NumberInputPropertyLine.displayName = "NumberInputPropertyLine";
9566
+ const classes = useStyles$F();
9567
+ return (jsx(PropertyLine, { ...props, children: jsx(SpinButton, { ...props, className: mergeClasses(classes.uniformWidth, props.className) }) }));
9568
+ };
9569
+
9570
+ const HasZ = (vector) => !(vector instanceof Vector2);
9571
+ const HasW = (vector) => vector instanceof Vector4 || vector instanceof Quaternion;
9572
+ /**
9573
+ * Reusable component which renders a vector property line containing a label, vector value, and expandable XYZW values
9574
+ * The expanded section contains a slider/input box for each component of the vector (x, y, z, w)
9575
+ * @param props
9576
+ * @returns
9577
+ */
9578
+ const TensorPropertyLine = (props) => {
9579
+ TensorPropertyLine.displayName = "TensorPropertyLine";
9580
+ const converted = (val) => (props.valueConverter ? props.valueConverter.from(val) : val);
9581
+ const formatted = (val) => converted(val).toFixed(props.step !== undefined ? Math.max(0, CalculatePrecision(props.step)) : 2);
9582
+ const [vector, setVector] = useState(props.value);
9583
+ const { min, max } = props;
9584
+ const onChange = (val, key) => {
9585
+ const value = props.valueConverter ? props.valueConverter.to(val) : val;
9586
+ const newVector = vector.clone();
9587
+ newVector[key] = value; // The syncedSlider for 'w' is only rendered when vector is a Vector4, so this is safe
9588
+ setVector(newVector);
9589
+ props.onChange(newVector);
9590
+ };
9591
+ useEffect(() => {
9592
+ setVector(props.value);
9593
+ }, [props.value, props.expandedContent]);
9594
+ return (jsx(PropertyLine, { ...props, expandedContent: jsxs(Fragment, { children: [props.expandedContent, vector ? (jsx(VectorSliders, { vector: vector, min: min, max: max, unit: props.unit, step: props.step, precision: props.precision, converted: converted, onChange: onChange })) : undefined] }), children: jsx(Body1, { children: `[${formatted(props.value.x)}, ${formatted(props.value.y)}${HasZ(props.value) ? `, ${formatted(props.value.z)}` : ""}${HasW(props.value) ? `, ${formatted(props.value.w)}` : ""}]` }) }));
9595
+ };
9596
+ const VectorSliders = ({ vector, min, max, unit, step, precision, converted, onChange }) => (jsxs(Fragment, { children: [jsx(NumberInputPropertyLine, { label: "X", value: converted(vector.x), min: min, max: max, onChange: (val) => onChange(val, "x"), unit: unit, step: step, precision: precision }), jsx(NumberInputPropertyLine, { label: "Y", value: converted(vector.y), min: min, max: max, onChange: (val) => onChange(val, "y"), unit: unit, step: step, precision: precision }), HasZ(vector) && (jsx(NumberInputPropertyLine, { label: "Z", value: converted(vector.z), min: min, max: max, onChange: (val) => onChange(val, "z"), unit: unit, step: step, precision: precision })), HasW(vector) && (jsx(NumberInputPropertyLine, { label: "W", value: converted(vector.w), min: min, max: max, onChange: (val) => onChange(val, "w"), unit: unit, step: step, precision: precision }))] }));
9597
+ const ToDegreesConverter = { from: Tools.ToDegrees, to: Tools.ToRadians };
9598
+ const RotationVectorPropertyLine = (props) => {
9599
+ RotationVectorPropertyLine.displayName = "RotationVectorPropertyLine";
9600
+ const step = props.useDegrees ? 1 : 0.01;
9601
+ const precision = props.useDegrees ? 1 : 2;
9602
+ return (jsx(Vector3PropertyLine, { ...props, unit: props.useDegrees ? "°" : "rad", valueConverter: props.useDegrees ? ToDegreesConverter : undefined, step: step, precision: precision }));
9603
+ };
9604
+ const QuaternionPropertyLineInternal = TensorPropertyLine;
9605
+ const QuaternionPropertyLine = (props) => {
9606
+ QuaternionPropertyLine.displayName = "QuaternionPropertyLine";
9607
+ const step = props.useDegrees ? 1 : 0.01;
9608
+ const precision = props.useDegrees ? 1 : 2;
9609
+ const [quat, setQuat] = useState(props.value);
9610
+ useEffect(() => {
9611
+ setQuat(props.value);
9612
+ }, [props.value]);
9613
+ // Extract only the properties that exist on QuaternionPropertyLineProps
9614
+ const { useEuler, ...restProps } = props;
9615
+ const onQuatChange = (val) => {
9616
+ setQuat(val);
9617
+ props.onChange(val);
9618
+ };
9619
+ const onEulerChange = (val) => {
9620
+ const quat = Quaternion.FromEulerAngles(val.x, val.y, val.z);
9621
+ onQuatChange(quat);
8706
9622
  };
9623
+ return useEuler ? (jsx(Vector3PropertyLine, { ...restProps, nullable: false, ignoreNullable: false, value: quat.toEulerAngles(), valueConverter: ToDegreesConverter, onChange: onEulerChange, unit: props.useDegrees ? "°" : "rad", step: step, precision: precision, expandedContent: jsx(TextPropertyLine, { label: "Quaternion", value: `[${quat.x.toFixed(4)}, ${quat.y.toFixed(4)}, ${quat.z.toFixed(4)}, ${quat.w.toFixed(4)}]` }) })) : (jsx(QuaternionPropertyLineInternal, { ...props, nullable: false, value: quat, onChange: onQuatChange, unit: props.useDegrees ? "°" : "rad", step: step, precision: precision }));
9624
+ };
9625
+ const Vector2PropertyLine = TensorPropertyLine;
9626
+ const Vector3PropertyLine = TensorPropertyLine;
9627
+ const Vector4PropertyLine = TensorPropertyLine;
9628
+
9629
+ function IsInspectableObject(entity) {
9630
+ return !!entity.inspectableCustomProperties;
8707
9631
  }
9632
+ const LegacyInspectableObjectPropertiesServiceDefinition = {
9633
+ friendlyName: "Additional Nodes",
9634
+ consumes: [PropertiesServiceIdentity],
9635
+ factory: (propertiesService) => {
9636
+ const propertiesSectionRegistration = propertiesService.addSection({
9637
+ identity: "Custom",
9638
+ order: Number.MAX_SAFE_INTEGER,
9639
+ });
9640
+ const propertiesContentRegistration = propertiesService.addSectionContent({
9641
+ key: "Additional Nodes Properties",
9642
+ predicate: (entity) => IsInspectableObject(entity),
9643
+ content: [
9644
+ {
9645
+ section: "Custom",
9646
+ component: ({ context }) => {
9647
+ return (jsx(Fragment, { children: (context.inspectableCustomProperties ?? []).map((prop) => {
9648
+ const commonProps = {
9649
+ target: context,
9650
+ propertyKey: prop.propertyName,
9651
+ label: prop.label,
9652
+ ignoreNullable: true,
9653
+ defaultValue: undefined,
9654
+ };
9655
+ switch (prop.type) {
9656
+ case 0 /* InspectableType.Checkbox */:
9657
+ return jsx(BoundProperty, { ...commonProps, component: SwitchPropertyLine }, prop.propertyName);
9658
+ case 1 /* InspectableType.Slider */:
9659
+ return (jsx(BoundProperty, { ...commonProps, min: prop.min, max: prop.max, step: prop.step, component: SyncedSliderPropertyLine }, prop.propertyName));
9660
+ case 2 /* InspectableType.Vector3 */:
9661
+ return jsx(BoundProperty, { ...commonProps, component: Vector3PropertyLine }, prop.propertyName);
9662
+ case 3 /* InspectableType.Quaternion */:
9663
+ return jsx(BoundProperty, { ...commonProps, component: QuaternionPropertyLine }, prop.propertyName);
9664
+ case 4 /* InspectableType.Color3 */:
9665
+ return jsx(BoundProperty, { ...commonProps, component: Color3PropertyLine }, prop.propertyName);
9666
+ case 5 /* InspectableType.String */:
9667
+ return jsx(BoundProperty, { ...commonProps, component: TextInputPropertyLine }, prop.propertyName);
9668
+ case 6 /* InspectableType.Button */:
9669
+ return jsx(ButtonLine, { label: prop.label, onClick: () => prop.callback?.() }, prop.propertyName);
9670
+ case 7 /* InspectableType.Options */:
9671
+ return jsx(BoundProperty, { ...commonProps, component: DropdownPropertyLine, options: prop.options ?? [] }, prop.propertyName);
9672
+ case 8 /* InspectableType.Tab */:
9673
+ return jsx(BoundProperty, { ...commonProps, component: TextPropertyLine }, prop.propertyName);
9674
+ case 9 /* InspectableType.FileButton */:
9675
+ return (jsx(FileUploadLine, { label: prop.label, accept: prop.accept ?? "", onClick: (files) => {
9676
+ if (files.length > 0 && prop.fileCallback) {
9677
+ prop.fileCallback(files[0]);
9678
+ }
9679
+ } }, prop.propertyName));
9680
+ case 10 /* InspectableType.Vector2 */:
9681
+ return jsx(BoundProperty, { ...commonProps, component: Vector2PropertyLine }, prop.propertyName);
9682
+ }
9683
+ }) }));
9684
+ },
9685
+ },
9686
+ ],
9687
+ });
9688
+ return {
9689
+ dispose: () => {
9690
+ propertiesSectionRegistration.dispose();
9691
+ propertiesContentRegistration.dispose();
9692
+ },
9693
+ };
9694
+ },
9695
+ };
9696
+
9697
+ const CliConnectionStatusServiceDefinition = {
9698
+ friendlyName: "CLI Connection Status",
9699
+ consumes: [ShellServiceIdentity, CliConnectionStatusIdentity],
9700
+ factory: (shellService, cliConnectionStatus) => {
9701
+ shellService.addToolbarItem({
9702
+ key: "CLI Connection Status",
9703
+ verticalLocation: "bottom",
9704
+ horizontalLocation: "right",
9705
+ teachingMoment: false,
9706
+ order: 0 /* DefaultToolbarItemOrder.CliStatus */,
9707
+ component: () => {
9708
+ const isConnected = useObservableState(() => cliConnectionStatus.isConnected, cliConnectionStatus.onConnectionStatusChanged);
9709
+ const { showToast } = useToast();
9710
+ const isFirstRender = useRef(true);
9711
+ useEffect(() => {
9712
+ if (isFirstRender.current) {
9713
+ isFirstRender.current = false;
9714
+ return;
9715
+ }
9716
+ if (isConnected) {
9717
+ showToast("Inspector bridge connected.", { intent: "success" });
9718
+ }
9719
+ else {
9720
+ showToast("Inspector bridge disconnected.", { intent: "warning" });
9721
+ }
9722
+ }, [isConnected, showToast]);
9723
+ // Using raw Fluent Button to pass color directly to the icon.
9724
+ return (jsx(Tooltip, { content: isConnected ? "Connected to Inspector CLI Bridge" : "Disconnected from Inspector CLI Bridge", children: jsx(Button$1, { appearance: "subtle", icon: isConnected ? (jsx(PlugConnectedRegular, { color: tokens.colorPaletteGreenForeground2 })) : (jsx(PlugDisconnectedRegular, { color: tokens.colorPaletteRedForeground2 })), onClick: () => window.open("https://www.npmjs.com/package/@babylonjs/inspector", "_blank") }) }));
9725
+ },
9726
+ });
9727
+ },
9728
+ };
8708
9729
 
8709
9730
  const MeshIcon = createFluentIcon("Mesh", "1em", '<g transform="scale(1.25)"><path d="M14.03,3.54l-5.11-2.07c-.61-.25-1.27-.25-1.88,0L1.93,3.54c-.57.23-.94.78-.94,1.39v6.15c0,.61.37,1.16.94,1.39l5.11,2.07c.3.12.62.18.94.18s.64-.06.94-.18l5.12-2.07c.57-.23.94-.78.94-1.39v-6.15c0-.61-.37-1.16-.94-1.39ZM13.97,7.71l-2.11.86v-2.71l2.11-.86v2.71ZM1.99,5l2.11.86v2.71l-2.11-.86v-2.71ZM11.35,4.98l-2.04-.83,1.78-.72,2.04.83-1.78.72ZM10.02,5.52l-2.04.83-2.04-.83,2.04-.83,2.04.83ZM4.6,4.98l-1.78-.72,2.04-.83,1.78.72-2.04.83ZM5.1,6.26l2.38.96v2.71l-2.38-.96v-2.71ZM8.48,7.22l2.38-.96v2.71l-2.38.96v-2.71ZM7.41,2.39c.18-.07.37-.11.56-.11s.38.04.56.11l1.22.49-1.78.72-1.79-.72,1.22-.49ZM1.99,11.07v-2.29l2.11.86v2.62l-1.8-.73c-.19-.08-.31-.26-.31-.46ZM5.1,12.67v-2.62l2.38.96v2.61s-.04,0-.06-.01l-2.31-.94ZM8.54,13.61s-.04,0-.06.01v-2.61l2.38-.96v2.62l-2.31.94ZM13.66,11.54l-1.8.73v-2.62l2.11-.86v2.29c0,.2-.12.39-.31.46Z"/></g>');
8710
9731
  const TranslateIcon = createFluentIcon("Translate", "1em", '<g transform="scale(0.833)"><path d="M20.16,12.98l-2.75-2.75c-.29-.29-.77-.29-1.06,0-.29.29-.29.77,0,1.06l1.47,1.47h-6.69v-6.69l1.47,1.47c.29.29.77.29,1.06,0,.29-.29.29-.77,0-1.06l-2.75-2.75c-.14-.14-.33-.22-.53-.22s-.39.08-.53.22l-2.75,2.75c-.29.29-.29.77,0,1.06.29.29.77.29,1.06,0l1.47-1.47v7.13l-3.52,3.52v-2.08c0-.41-.34-.75-.75-.75s-.75.34-.75.75v3.89c0,.2.08.39.22.53.14.14.33.22.53.22h3.89c.41,0,.75-.34.75-.75s-.34-.75-.75-.75h-2.08s3.52-3.52,3.52-3.52h7.13l-1.47,1.47c-.29.29-.29.77,0,1.06s.77.29,1.06,0l2.75-2.75c.14-.14.22-.33.22-.53s-.08-.39-.22-.53Z" /></g>');
@@ -13453,7 +14474,7 @@ const EffectLayerPropertiesServiceDefinition = {
13453
14474
  async function EditNodeRenderGraph(nodeRenderGraph) {
13454
14475
  // TODO: Figure out how to get all the various build steps to work with this.
13455
14476
  // See the initial attempt here: https://github.com/BabylonJS/Babylon.js/pull/17646
13456
- // const { NodeRenderGraphEditor } = await import("node-render-graph-editor/nodeRenderGraphEditor");
14477
+ // const { NodeRenderGraphEditor } = await import("@babylonjs/node-render-graph-editor");
13457
14478
  // NodeRenderGraphEditor.Show({ nodeRenderGraph: renderGraph, hostScene: frameGraph.scene });
13458
14479
  await nodeRenderGraph.edit({ nodeRenderGraphEditorConfig: { backgroundColor: nodeRenderGraph.getScene().clearColor, hostScene: nodeRenderGraph.getScene() } });
13459
14480
  }
@@ -13687,14 +14708,10 @@ function EntitySelector(props) {
13687
14708
  if (value && !isEditing) {
13688
14709
  // If there is a value and we are not editing, show the link view
13689
14710
  return (jsxs("div", { className: classes.linkDiv, children: [jsx(Tooltip$1, { content: getName(value), relationship: "label", children: jsx(Link, { className: classes.link, value: getName(value), onLink: () => onLink(value) }) }), onChange &&
13690
- (defaultValue !== undefined ? (
13691
- // If the defaultValue is specified, then allow resetting to the default
13692
- jsx(Tooltip$1, { content: "Unlink", relationship: "label", children: jsx(Button, { icon: LinkDismissRegular, onClick: () => {
14711
+ (defaultValue !== undefined ? (jsx(Tooltip$1, { content: "Unlink", relationship: "label", children: jsx(Button, { icon: LinkDismissRegular, onClick: () => {
13693
14712
  pulseEnteringEditMode(true);
13694
14713
  onChange(defaultValue);
13695
- } }) })) : (
13696
- // Otherwise, just allow editing to a new value
13697
- jsx(Tooltip$1, { content: "Edit Link", relationship: "label", children: jsx(Button, { icon: LinkEditRegular, onClick: () => {
14714
+ } }) })) : (jsx(Tooltip$1, { content: "Edit Link", relationship: "label", children: jsx(Button, { icon: LinkEditRegular, onClick: () => {
13698
14715
  pulseEnteringEditMode(true);
13699
14716
  setIsEditing(true);
13700
14717
  } }) })))] }));
@@ -14417,7 +15434,7 @@ function GroupBy(items, getKey) {
14417
15434
  async function EditNodeMaterial(material) {
14418
15435
  // TODO: Figure out how to get all the various build steps to work with this.
14419
15436
  // See the initial attempt here: https://github.com/BabylonJS/Babylon.js/pull/17646
14420
- // const { NodeEditor } = await import("node-editor/nodeEditor");
15437
+ // const { NodeEditor } = await import("@babylonjs/node-editor");
14421
15438
  // NodeEditor.Show({ nodeMaterial: material });
14422
15439
  await material.edit({ nodeEditorConfig: { backgroundColor: material.getScene().clearColor } });
14423
15440
  }
@@ -14522,14 +15539,42 @@ const NormalMapProperties = (props) => {
14522
15539
  return (jsx(Fragment, { children: IsMaterialWithPublicNormalMaps(material) ? (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SwitchPropertyLine, label: "Invert X Axis", target: material, propertyKey: "invertNormalMapX" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Invert Y Axis", target: material, propertyKey: "invertNormalMapY" })] })) : (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SwitchPropertyLine, label: "Invert X Axis", target: material, propertyKey: "_invertNormalMapX" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Invert Y Axis", target: material, propertyKey: "_invertNormalMapY" })] })) }));
14523
15540
  };
14524
15541
 
14525
- // TODO: ryamtrem / gehalper This function is temporal until there is a line control to handle texture links (similar to the old TextureLinkLineComponent)
14526
- const UpdateTexture = (file, material, textureSetter) => {
14527
- ReadFile(file, (data) => {
14528
- const blob = new Blob([data], { type: "octet/stream" });
14529
- const url = URL.createObjectURL(blob);
14530
- textureSetter(new Texture(url, material.getScene(), false, false));
14531
- }, undefined, true);
15542
+ const useCheckboxStyles = makeStyles({
15543
+ indicator: {
15544
+ margin: 0,
15545
+ },
15546
+ });
15547
+ /**
15548
+ * This is a primitive fluent checkbox that can both read and write checked state
15549
+ * @param props
15550
+ * @returns Checkbox component
15551
+ */
15552
+ const Checkbox = (props) => {
15553
+ Checkbox.displayName = "Checkbox";
15554
+ const [checked, setChecked] = useState(() => props.value ?? false);
15555
+ const classes = useCheckboxStyles();
15556
+ useEffect(() => {
15557
+ if (props.value != undefined) {
15558
+ setChecked(props.value); // Update local state when props.checked changes
15559
+ }
15560
+ }, [props.value]);
15561
+ const onChange = (ev, _) => {
15562
+ props.onChange(ev.target.checked);
15563
+ setChecked(ev.target.checked);
15564
+ };
15565
+ return jsx(Checkbox$1, { checked: checked, onChange: onChange, indicator: { className: classes.indicator } });
15566
+ };
15567
+
15568
+ /**
15569
+ * Wraps a checkbox in a property line
15570
+ * @param props - PropertyLineProps and CheckboxProps
15571
+ * @returns property-line wrapped checkbox
15572
+ */
15573
+ const CheckboxPropertyLine = (props) => {
15574
+ CheckboxPropertyLine.displayName = "CheckboxPropertyLine";
15575
+ return (jsx(PropertyLine, { ...props, children: jsx(Checkbox, { ...props }) }));
14532
15576
  };
15577
+
14533
15578
  /**
14534
15579
  * Displays the base layer properties of an OpenPBR material.
14535
15580
  * @param props - The required properties
@@ -14537,23 +15582,7 @@ const UpdateTexture = (file, material, textureSetter) => {
14537
15582
  */
14538
15583
  const OpenPBRMaterialBaseProperties = (props) => {
14539
15584
  const { material } = props;
14540
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Base Weight", target: material, propertyKey: "baseWeight", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Base Weight", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14541
- if (files.length > 0) {
14542
- UpdateTexture(files[0], material, (texture) => (material.baseWeightTexture = texture));
14543
- }
14544
- } }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Base Color", target: material, propertyKey: "baseColor", isLinearMode: true }), jsx(FileUploadLine, { label: "Base Color", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14545
- if (files.length > 0) {
14546
- UpdateTexture(files[0], material, (texture) => (material.baseColorTexture = texture));
14547
- }
14548
- } }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Base Metalness", target: material, propertyKey: "baseMetalness", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Base Metalness", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14549
- if (files.length > 0) {
14550
- UpdateTexture(files[0], material, (texture) => (material.baseMetalnessTexture = texture));
14551
- }
14552
- } }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Base Diffuse Roughness", target: material, propertyKey: "baseDiffuseRoughness", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Base Diffuse Roughness", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14553
- if (files.length > 0) {
14554
- UpdateTexture(files[0], material, (texture) => (material.baseDiffuseRoughnessTexture = texture));
14555
- }
14556
- } })] }));
15585
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Base Weight", target: material, propertyKey: "baseWeight", min: 0, max: 1, step: 0.01, description: "Controls how strong or visible the base aspect appears.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Base Weight", target: material, propertyKey: "baseWeightTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Base Color", target: material, propertyKey: "baseColor", isLinearMode: true, description: "Sets the primary surface color of the material.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Base Color", target: material, propertyKey: "baseColorTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Base Metalness", target: material, propertyKey: "baseMetalness", min: 0, max: 1, step: 0.01, description: "Controls whether the material behaves as metal or non-metal. The parameter supersedes transmission_weight and subsurface_weight.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Base Metalness", target: material, propertyKey: "baseMetalnessTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Base Diffuse Roughness", target: material, propertyKey: "baseDiffuseRoughness", min: 0, max: 1, step: 0.01, description: "Softens the surface's base appearance. Higher values create matte or porous looks. Lower values are smoother.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Base Diffuse Roughness", target: material, propertyKey: "baseDiffuseRoughnessTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Ambient Occlusion", target: material, propertyKey: "ambientOcclusionTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 })] }));
14557
15586
  };
14558
15587
  /**
14559
15588
  * Displays the specular layer properties of an OpenPBR material.
@@ -14562,47 +15591,15 @@ const OpenPBRMaterialBaseProperties = (props) => {
14562
15591
  */
14563
15592
  const OpenPBRMaterialSpecularProperties = (props) => {
14564
15593
  const { material } = props;
14565
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular Weight", target: material, propertyKey: "specularWeight", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Specular Weight", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14566
- if (files.length > 0) {
14567
- UpdateTexture(files[0], material, (texture) => (material.specularWeightTexture = texture));
14568
- }
14569
- } }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Specular Color", target: material, propertyKey: "specularColor", isLinearMode: true }), jsx(FileUploadLine, { label: "Specular Color", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14570
- if (files.length > 0) {
14571
- UpdateTexture(files[0], material, (texture) => (material.specularColorTexture = texture));
14572
- }
14573
- } }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular Roughness", target: material, propertyKey: "specularRoughness", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Specular Roughness", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14574
- if (files.length > 0) {
14575
- UpdateTexture(files[0], material, (texture) => (material.specularRoughnessTexture = texture));
14576
- }
14577
- } }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular Roughness Anisotropy", target: material, propertyKey: "specularRoughnessAnisotropy", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Specular Roughness Anisotropy", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14578
- if (files.length > 0) {
14579
- UpdateTexture(files[0], material, (texture) => (material.specularRoughnessAnisotropyTexture = texture));
14580
- }
14581
- } }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular IOR", target: material, propertyKey: "specularIor", min: 1, max: 3, step: 0.01 })] }));
15594
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular Weight", target: material, propertyKey: "specularWeight", min: 0, max: 1, step: 0.01, description: "Controls how strong the reflections appear.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Specular Weight", target: material, propertyKey: "specularWeightTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Specular Color", target: material, propertyKey: "specularColor", isLinearMode: true, description: "Tints the color of reflections.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Specular Color", target: material, propertyKey: "specularColorTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular Roughness", target: material, propertyKey: "specularRoughness", min: 0, max: 1, step: 0.01, description: "Controls how sharp or blurry reflections are.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Specular Roughness", target: material, propertyKey: "specularRoughnessTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular Roughness Anisotropy", target: material, propertyKey: "specularRoughnessAnisotropy", min: 0, max: 1, step: 0.01, description: "Stretches reflections in one direction for brushed or streaked looks. Requires specular_roughness > 0.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/microfacetmodel" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Specular Roughness Anisotropy", target: material, propertyKey: "specularRoughnessAnisotropyTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular IOR", target: material, propertyKey: "specularIor", min: 1, max: 3, step: 0.01, description: "Index of refraction is a physical value controlling the reflective intensity and refraction. The parameter has no effect on metals.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate" })] }));
14582
15595
  };
14583
15596
  const OpenPBRMaterialTransmissionProperties = (props) => {
14584
15597
  const { material } = props;
14585
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Transmission Weight", target: material, propertyKey: "transmissionWeight", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Transmission Weight", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14586
- if (files.length > 0) {
14587
- UpdateTexture(files[0], material, (texture) => (material.transmissionWeightTexture = texture));
14588
- }
14589
- } }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Transmission Color", target: material, propertyKey: "transmissionColor", isLinearMode: true }), jsx(FileUploadLine, { label: "Transmission Color", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14590
- if (files.length > 0) {
14591
- UpdateTexture(files[0], material, (texture) => (material.transmissionColorTexture = texture));
14592
- }
14593
- } }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Transmission Depth", target: material, propertyKey: "transmissionDepth", min: 0, step: 0.01 }), jsx(FileUploadLine, { label: "Transmission Depth", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14594
- if (files.length > 0) {
14595
- UpdateTexture(files[0], material, (texture) => (material.transmissionDepthTexture = texture));
14596
- }
14597
- } }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Transmission Scatter", target: material, propertyKey: "transmissionScatter", isLinearMode: true }), jsx(FileUploadLine, { label: "Transmission Scatter", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14598
- if (files.length > 0) {
14599
- UpdateTexture(files[0], material, (texture) => (material.transmissionScatterTexture = texture));
14600
- }
14601
- } }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Transmission Scatter Anisotropy", target: material, propertyKey: "transmissionScatterAnisotropy", min: -1, max: 1, step: 0.01 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Transmission Dispersion Abbe Number", target: material, propertyKey: "transmissionDispersionAbbeNumber", min: 1, max: 100, step: 1 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Transmission Dispersion Scale", target: material, propertyKey: "transmissionDispersionScale", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Transmission Dispersion Scale", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14602
- if (files.length > 0) {
14603
- UpdateTexture(files[0], material, (texture) => (material.transmissionDispersionScaleTexture = texture));
14604
- }
14605
- } })] }));
15598
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Transmission Weight", target: material, propertyKey: "transmissionWeight", min: 0, max: 1, step: 0.01, description: "Controls the presence of the transparency effect. The parameter is superseded by base_metalness.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate/translucentbase" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Transmission Weight", target: material, propertyKey: "transmissionWeightTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Transmission Color", target: material, propertyKey: "transmissionColor", isLinearMode: true, description: "Tints light passing through the material. Works with transmission_depth for realistic thickness-based coloring.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate/translucentbase" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Transmission Color", target: material, propertyKey: "transmissionColorTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Transmission Depth (cm)", target: material, propertyKey: "transmissionDepth", min: 0, max: 5, step: 0.001, convertTo: (value) => value * 100, convertFrom: (value) => value / 100, description: "Controls how quickly light is absorbed with thickness. Distance is in scene units.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate/translucentbase" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Transmission Depth", target: material, propertyKey: "transmissionDepthTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Transmission Scatter", target: material, propertyKey: "transmissionScatter", isLinearMode: true, description: "Adds internal cloudiness to create materials like juice, honey, etc. Requires transmission_depth > 0.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate/translucentbase" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Transmission Scatter", target: material, propertyKey: "transmissionScatterTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Transmission Scatter Anisotropy", target: material, propertyKey: "transmissionScatterAnisotropy", min: -1, max: 1, step: 0.01, description: "Shifts scattering forward/backward for clearer or hazier appearance depending on viewing angle.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate/translucentbase" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Transmission Dispersion Abbe Number", target: material, propertyKey: "transmissionDispersionAbbeNumber", min: 1, max: 100, step: 1, description: "Physical value for the rainbow color separation in refraction.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate/translucentbase" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Transmission Dispersion Scale", target: material, propertyKey: "transmissionDispersionScale", min: 0, max: 1, step: 0.01, description: "Strength of rainbow color separation in refraction.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate/translucentbase" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Transmission Dispersion Scale", target: material, propertyKey: "transmissionDispersionScaleTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 })] }));
15599
+ };
15600
+ const OpenPBRMaterialSubsurfaceProperties = (props) => {
15601
+ const { material } = props;
15602
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Subsurface Weight", target: material, propertyKey: "subsurfaceWeight", min: 0, max: 1, step: 0.01, description: "Controls the presence of the subsurface effect. The parameter is superseded by base_metalness and transmission_weight.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate/subsurface" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Subsurface Weight", target: material, propertyKey: "subsurfaceWeightTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Subsurface Color", target: material, propertyKey: "subsurfaceColor", isLinearMode: true, description: "Colors the light that scatters under the surface.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate/subsurface" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Subsurface Color", target: material, propertyKey: "subsurfaceColorTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Subsurface Radius (cm)", target: material, propertyKey: "subsurfaceRadius", min: 0, max: 4, step: 0.001, convertTo: (value) => value * 100, convertFrom: (value) => value / 100, description: "Controls how soft and spread-out the subsurface look appears.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate/subsurface" }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Subsurface Radius Scale", target: material, propertyKey: "subsurfaceRadiusScale", isLinearMode: true, description: "Tints thin areas with light shining through, like warm glow on ears or leaves.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate/subsurface" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Subsurface Radius Scale", target: material, propertyKey: "subsurfaceRadiusScaleTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Subsurface Scatter Anisotropy", target: material, propertyKey: "subsurfaceScatterAnisotropy", min: -1, max: 1, step: 0.01, description: "Shifts scattering forward/backward for a softer glow or a sharper one.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate/subsurface" })] }));
14606
15603
  };
14607
15604
  /**
14608
15605
  * Displays the coat layer properties of an OpenPBR material.
@@ -14611,27 +15608,7 @@ const OpenPBRMaterialTransmissionProperties = (props) => {
14611
15608
  */
14612
15609
  const OpenPBRMaterialCoatProperties = (props) => {
14613
15610
  const { material } = props;
14614
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Coat Weight", target: material, propertyKey: "coatWeight", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Coat Weight", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14615
- if (files.length > 0) {
14616
- UpdateTexture(files[0], material, (texture) => (material.coatWeightTexture = texture));
14617
- }
14618
- } }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Coat Color", target: material, propertyKey: "coatColor", isLinearMode: true }), jsx(FileUploadLine, { label: "Coat Color", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14619
- if (files.length > 0) {
14620
- UpdateTexture(files[0], material, (texture) => (material.coatColorTexture = texture));
14621
- }
14622
- } }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Coat Roughness", target: material, propertyKey: "coatRoughness", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Coat Roughness", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14623
- if (files.length > 0) {
14624
- UpdateTexture(files[0], material, (texture) => (material.coatRoughnessTexture = texture));
14625
- }
14626
- } }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Coat Roughness Anisotropy", target: material, propertyKey: "coatRoughnessAnisotropy", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Coat Roughness Anisotropy", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14627
- if (files.length > 0) {
14628
- UpdateTexture(files[0], material, (texture) => (material.coatRoughnessAnisotropyTexture = texture));
14629
- }
14630
- } }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Coat IOR", target: material, propertyKey: "coatIor", min: 1, max: 3, step: 0.01 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Coat Darkening", target: material, propertyKey: "coatDarkening", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Coat Darkening", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14631
- if (files.length > 0) {
14632
- UpdateTexture(files[0], material, (texture) => (material.coatDarkeningTexture = texture));
14633
- }
14634
- } })] }));
15611
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Coat Weight", target: material, propertyKey: "coatWeight", min: 0, max: 1, step: 0.01, description: "Controls the presence of the coat.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/coat" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Coat Weight", target: material, propertyKey: "coatWeightTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Coat Color", target: material, propertyKey: "coatColor", isLinearMode: true, description: "Tints the coat, for tinted varnish or paint.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/coat" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Coat Color", target: material, propertyKey: "coatColorTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Coat Roughness", target: material, propertyKey: "coatRoughness", min: 0, max: 1, step: 0.01, description: "Controls how sharp or blurry the coat reflections appear.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/coat/roughening" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Coat Roughness", target: material, propertyKey: "coatRoughnessTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Coat Roughness Anisotropy", target: material, propertyKey: "coatRoughnessAnisotropy", min: 0, max: 1, step: 0.01, description: "Stretches coat reflections in one direction for brushed or streaked looks. Requires coat_roughness > 0.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/coat" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Coat Roughness Anisotropy", target: material, propertyKey: "coatRoughnessAnisotropyTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Coat IOR", target: material, propertyKey: "coatIor", min: 1, max: 3, step: 0.01, description: "Index of refraction is a physical value controlling the reflective intensity of the coat.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/coat" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Coat Darkening", target: material, propertyKey: "coatDarkening", min: 0, max: 1, step: 0.01, description: "Darkens the base under the coat, similar to how real varnish deepens color.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/coat/darkening" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Coat Darkening", target: material, propertyKey: "coatDarkeningTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 })] }));
14635
15612
  };
14636
15613
  /**
14637
15614
  * Displays the fuzz layer properties of an OpenPBR material.
@@ -14640,19 +15617,7 @@ const OpenPBRMaterialCoatProperties = (props) => {
14640
15617
  */
14641
15618
  const OpenPBRMaterialFuzzProperties = (props) => {
14642
15619
  const { material } = props;
14643
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Fuzz Weight", target: material, propertyKey: "fuzzWeight", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Fuzz Weight", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14644
- if (files.length > 0) {
14645
- UpdateTexture(files[0], material, (texture) => (material.fuzzWeightTexture = texture));
14646
- }
14647
- } }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Fuzz Color", target: material, propertyKey: "fuzzColor", isLinearMode: true }), jsx(FileUploadLine, { label: "Fuzz Color", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14648
- if (files.length > 0) {
14649
- UpdateTexture(files[0], material, (texture) => (material.fuzzColorTexture = texture));
14650
- }
14651
- } }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Fuzz Roughness", target: material, propertyKey: "fuzzRoughness", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Fuzz Roughness", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14652
- if (files.length > 0) {
14653
- UpdateTexture(files[0], material, (texture) => (material.fuzzRoughnessTexture = texture));
14654
- }
14655
- } }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Number of Samples", target: material, propertyKey: "fuzzSampleNumber", min: 4, max: 64, step: 1 })] }));
15620
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Fuzz Weight", target: material, propertyKey: "fuzzWeight", min: 0, max: 1, step: 0.01, description: "Controls the presence of the fuzz.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/fuzz" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Fuzz Weight", target: material, propertyKey: "fuzzWeightTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Fuzz Color", target: material, propertyKey: "fuzzColor", isLinearMode: true, description: "Controls the color of the fuzz.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/fuzz" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Fuzz Color", target: material, propertyKey: "fuzzColorTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Fuzz Roughness", target: material, propertyKey: "fuzzRoughness", min: 0, max: 1, step: 0.01, description: "Controls the roughness of the fuzz.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/fuzz" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Fuzz Roughness", target: material, propertyKey: "fuzzRoughnessTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Number of Samples", target: material, propertyKey: "fuzzSampleNumber", min: 4, max: 64, step: 1 })] }));
14656
15621
  };
14657
15622
  /**
14658
15623
  * Displays the emission properties of an OpenPBR material.
@@ -14661,11 +15626,7 @@ const OpenPBRMaterialFuzzProperties = (props) => {
14661
15626
  */
14662
15627
  const OpenPBRMaterialEmissionProperties = (props) => {
14663
15628
  const { material } = props;
14664
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: Color3PropertyLine, label: "Emission Color", target: material, propertyKey: "emissionColor", isLinearMode: true }), jsx(FileUploadLine, { label: "Emission Color", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14665
- if (files.length > 0) {
14666
- UpdateTexture(files[0], material, (texture) => (material.emissionColorTexture = texture));
14667
- }
14668
- } }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Emission Luminance", target: material, propertyKey: "emissionLuminance", min: 0, max: 10, step: 0.01 })] }));
15629
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: Color3PropertyLine, label: "Emission Color", target: material, propertyKey: "emissionColor", isLinearMode: true, description: "Controls the color of the glow.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/emission" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Emission Color", target: material, propertyKey: "emissionColorTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Emission Luminance", target: material, propertyKey: "emissionLuminance", min: 0, max: 10, step: 0.01, description: "Controls how bright the glow is.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/emission" })] }));
14669
15630
  };
14670
15631
  /**
14671
15632
  * Displays the thin film properties of an OpenPBR material.
@@ -14674,15 +15635,7 @@ const OpenPBRMaterialEmissionProperties = (props) => {
14674
15635
  */
14675
15636
  const OpenPBRMaterialThinFilmProperties = (props) => {
14676
15637
  const { material } = props;
14677
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Thin Film Weight", target: material, propertyKey: "thinFilmWeight", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Thin Film Weight", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14678
- if (files.length > 0) {
14679
- UpdateTexture(files[0], material, (texture) => (material.thinFilmWeightTexture = texture));
14680
- }
14681
- } }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Thin Film Thickness", target: material, propertyKey: "thinFilmThickness", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Thin Film Thickness", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14682
- if (files.length > 0) {
14683
- UpdateTexture(files[0], material, (texture) => (material.thinFilmThicknessTexture = texture));
14684
- }
14685
- } }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Thin Film IOR", target: material, propertyKey: "thinFilmIor", min: 1, max: 3, step: 0.01 })] }));
15638
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Thin Film Weight", target: material, propertyKey: "thinFilmWeight", min: 0, max: 1, step: 0.01, description: "Controls the presence of the thin-film.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/thin-filmiridescence" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Thin Film Weight", target: material, propertyKey: "thinFilmWeightTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Thin Film Thickness", target: material, propertyKey: "thinFilmThickness", min: 0, max: 1, step: 0.01, description: "Changes the color pattern of the iridescence.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/thin-filmiridescence" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Thin Film Thickness", target: material, propertyKey: "thinFilmThicknessTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Thin Film IOR", target: material, propertyKey: "thinFilmIor", min: 1, max: 3, step: 0.01, description: "Alters the strength and contrast of the color shift based in the index of refraction.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/thin-filmiridescence" })] }));
14686
15639
  };
14687
15640
  /**
14688
15641
  * Displays the geometry properties of an OpenPBR material.
@@ -14691,31 +15644,7 @@ const OpenPBRMaterialThinFilmProperties = (props) => {
14691
15644
  */
14692
15645
  const OpenPBRMaterialGeometryProperties = (props) => {
14693
15646
  const { material } = props;
14694
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Opacity", target: material, propertyKey: "geometryOpacity", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Geometry Opacity", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14695
- if (files.length > 0) {
14696
- UpdateTexture(files[0], material, (texture) => (material.geometryOpacityTexture = texture));
14697
- }
14698
- } }), jsx(FileUploadLine, { label: "Geometry Normal", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14699
- if (files.length > 0) {
14700
- UpdateTexture(files[0], material, (texture) => (material.geometryNormalTexture = texture));
14701
- }
14702
- } }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Tangent Angle", target: material, propertyKey: "geometryTangentAngle", min: 0, max: Math.PI, step: 0.01 }), jsx(FileUploadLine, { label: "Geometry Tangent", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14703
- if (files.length > 0) {
14704
- UpdateTexture(files[0], material, (texture) => (material.geometryTangentTexture = texture));
14705
- }
14706
- } }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Coat Tangent Angle", target: material, propertyKey: "geometryCoatTangentAngle", min: 0, max: Math.PI, step: 0.01 }), jsx(FileUploadLine, { label: "Geometry Coat Normal", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14707
- if (files.length > 0) {
14708
- UpdateTexture(files[0], material, (texture) => (material.geometryCoatNormalTexture = texture));
14709
- }
14710
- } }), jsx(FileUploadLine, { label: "Geometry Coat Tangent", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14711
- if (files.length > 0) {
14712
- UpdateTexture(files[0], material, (texture) => (material.geometryCoatTangentTexture = texture));
14713
- }
14714
- } }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Geometry Thickness", target: material, propertyKey: "geometryThickness", min: 0, step: 0.1 }), jsx(FileUploadLine, { label: "Geometry Thickness", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
14715
- if (files.length > 0) {
14716
- UpdateTexture(files[0], material, (texture) => (material.geometryThicknessTexture = texture));
14717
- }
14718
- } })] }));
15647
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Geometry Opacity", target: material, propertyKey: "geometryOpacity", min: 0, max: 1, step: 0.01, description: "Controls material presence and transparency cutout.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/opacity/transparency" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Geometry Opacity", target: material, propertyKey: "geometryOpacityTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: CheckboxPropertyLine, label: "Thin-Walled", target: material, propertyKey: "geometryThinWalled", description: "When enabled, treats material as a thin shell (like leaves, paper sheets or windows). Disables ray bending in refraction.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/thin-walledcase" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Geometry Normal", target: material, propertyKey: "geometryNormalTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Tangent Angle", target: material, propertyKey: "geometryTangentAngle", min: 0, max: Math.PI, step: 0.01, description: "Tangent vector controlling anisotropic reflection direction for the base (metal and non-metal). Works with specular_roughness_anisotropy.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/geometry/tangent" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Geometry Tangent", target: material, propertyKey: "geometryTangentTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Coat Tangent Angle", target: material, propertyKey: "geometryCoatTangentAngle", min: 0, max: Math.PI, step: 0.01, description: "Tangent vector controlling anisotropic reflection direction for the coat. Works with coat_roughness_anisotropy.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/geometry/coat-tangent" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Geometry Coat Normal", target: material, propertyKey: "geometryCoatNormalTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Geometry Coat Tangent", target: material, propertyKey: "geometryCoatTangentTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Geometry Thickness", target: material, propertyKey: "geometryThickness", min: 0, step: 0.001, description: "Controls the thickness of the geometry for volume approximations.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/thickness" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Geometry Thickness", target: material, propertyKey: "geometryThicknessTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 })] }));
14719
15648
  };
14720
15649
 
14721
15650
  const LightFalloffOptions = [
@@ -15076,6 +16005,10 @@ const MaterialPropertiesServiceDefinition = {
15076
16005
  section: "OpenPBR",
15077
16006
  component: ({ context }) => jsx(OpenPBRMaterialTransmissionProperties, { material: context }),
15078
16007
  },
16008
+ {
16009
+ section: "OpenPBR",
16010
+ component: ({ context }) => jsx(OpenPBRMaterialSubsurfaceProperties, { material: context }),
16011
+ },
15079
16012
  {
15080
16013
  section: "OpenPBR",
15081
16014
  component: ({ context }) => jsx(OpenPBRMaterialCoatProperties, { material: context }),
@@ -15626,9 +16559,15 @@ const AbstractMeshDebugProperties = (props) => {
15626
16559
  } }), jsx(SyncedSliderPropertyLine, { label: "Target Bone", value: displayBoneIndex, min: 0, max: targetBoneOptions.length - 1, step: 1, onChange: (value) => onBoneDisplayIndexChangeHandler(value) })] }) }), skeleton && jsx(SwitchPropertyLine, { label: "Display Skeleton Map", value: displaySkeletonMap, onChange: () => displaySkeletonMapHandler() })] }));
15627
16560
  };
15628
16561
 
16562
+ const ShDegreeOptions = [
16563
+ { label: "None (0)", value: 0 },
16564
+ { label: "Degree 1 (3 params)", value: 1 },
16565
+ { label: "Degree 2 (8 params)", value: 2 },
16566
+ { label: "Degree 3 (15 params)", value: 3 },
16567
+ ];
15629
16568
  const GaussianSplattingDisplayProperties = (props) => {
15630
16569
  const { mesh } = props;
15631
- return (jsxs(Fragment, { children: [jsx(StringifiedPropertyLine, { label: "Splat Count", value: mesh.splatCount ?? 0 }), jsx(StringifiedPropertyLine, { label: "SH Parameter Count", value: (mesh.shDegree + 1) * (mesh.shDegree + 1) - 1 }), jsx(BooleanBadgePropertyLine, { label: "Has Compensation", value: mesh.compensation }), jsx(StringifiedPropertyLine, { label: "Kernel Size", value: mesh.kernelSize })] }));
16570
+ return (jsxs(Fragment, { children: [jsx(StringifiedPropertyLine, { label: "Splat Count", value: mesh.splatCount ?? 0 }), jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "SH Degree", options: ShDegreeOptions, target: mesh, propertyKey: "shDegree" }), jsx(StringifiedPropertyLine, { label: "Max SH Degree", value: mesh.maxShDegree }), jsx(BooleanBadgePropertyLine, { label: "Has Compensation", value: mesh.compensation }), jsx(StringifiedPropertyLine, { label: "Kernel Size", value: mesh.kernelSize })] }));
15632
16571
  };
15633
16572
 
15634
16573
  function GetNodeGeometry(mesh) {
@@ -15637,7 +16576,7 @@ function GetNodeGeometry(mesh) {
15637
16576
  async function EditNodeGeometry(nodeGeometry, hostScene) {
15638
16577
  // TODO: Figure out how to get all the various build steps to work with this.
15639
16578
  // See the initial attempt here: https://github.com/BabylonJS/Babylon.js/pull/17646
15640
- // const { NodeGeometryEditor } = await import("node-geometry-editor/nodeGeometryEditor");
16579
+ // const { NodeGeometryEditor } = await import("@babylonjs/node-geometry-editor");
15641
16580
  // NodeGeometryEditor.Show({ nodeGeometry: nodeGeometry, hostScene: mesh.getScene() });
15642
16581
  await nodeGeometry.edit({ nodeGeometryEditorConfig: { hostScene } });
15643
16582
  }
@@ -15772,7 +16711,7 @@ const NodePropertiesServiceDefinition = {
15772
16711
  async function EditParticleSystem(particleSystem) {
15773
16712
  // TODO: Figure out how to get all the various build steps to work with this.
15774
16713
  // See the initial attempt here: https://github.com/BabylonJS/Babylon.js/pull/17646
15775
- // const { NodeParticleEditor } = await import("node-particle-editor/nodeParticleEditor");
16714
+ // const { NodeParticleEditor } = await import("@babylonjs/node-particle-editor");
15776
16715
  // NodeParticleEditor.Show({ nodeParticleSet: NodeParticleSystemSet });
15777
16716
  const scene = particleSystem.getScene();
15778
16717
  if (scene) {
@@ -17133,48 +18072,12 @@ const PhysicsPropertiesServiceDefinition = {
17133
18072
  },
17134
18073
  ],
17135
18074
  });
17136
- return {
17137
- dispose: () => {
17138
- contentRegistration.dispose();
17139
- },
17140
- };
17141
- },
17142
- };
17143
-
17144
- const useCheckboxStyles = makeStyles({
17145
- indicator: {
17146
- margin: 0,
17147
- },
17148
- });
17149
- /**
17150
- * This is a primitive fluent checkbox that can both read and write checked state
17151
- * @param props
17152
- * @returns Checkbox component
17153
- */
17154
- const Checkbox = (props) => {
17155
- Checkbox.displayName = "Checkbox";
17156
- const [checked, setChecked] = useState(() => props.value ?? false);
17157
- const classes = useCheckboxStyles();
17158
- useEffect(() => {
17159
- if (props.value != undefined) {
17160
- setChecked(props.value); // Update local state when props.checked changes
17161
- }
17162
- }, [props.value]);
17163
- const onChange = (ev, _) => {
17164
- props.onChange(ev.target.checked);
17165
- setChecked(ev.target.checked);
17166
- };
17167
- return jsx(Checkbox$1, { checked: checked, onChange: onChange, indicator: { className: classes.indicator } });
17168
- };
17169
-
17170
- /**
17171
- * Wraps a checkbox in a property line
17172
- * @param props - PropertyLineProps and CheckboxProps
17173
- * @returns property-line wrapped checkbox
17174
- */
17175
- const CheckboxPropertyLine = (props) => {
17176
- CheckboxPropertyLine.displayName = "CheckboxPropertyLine";
17177
- return (jsx(PropertyLine, { ...props, children: jsx(Checkbox, { ...props }) }));
18075
+ return {
18076
+ dispose: () => {
18077
+ contentRegistration.dispose();
18078
+ },
18079
+ };
18080
+ },
17178
18081
  };
17179
18082
 
17180
18083
  /**
@@ -18139,256 +19042,40 @@ const SpritePropertiesServiceDefinition = {
18139
19042
  component: ({ context }) => jsx(SpriteManagerOtherProperties, { spriteManager: context }),
18140
19043
  },
18141
19044
  ],
18142
- });
18143
- const spriteSectionContentRegistration = propertiesService.addSectionContent({
18144
- key: "Sprite Properties",
18145
- predicate: (entity) => entity instanceof Sprite,
18146
- content: [
18147
- {
18148
- section: "General",
18149
- component: ({ context }) => jsx(SpriteGeneralProperties, { sprite: context, selectionService: selectionService }),
18150
- },
18151
- {
18152
- section: "Transform",
18153
- component: ({ context }) => jsx(SpriteTransformProperties, { sprite: context }),
18154
- },
18155
- {
18156
- section: "Cell",
18157
- component: ({ context }) => jsx(SpriteCellProperties, { sprite: context }),
18158
- },
18159
- {
18160
- section: "Animation",
18161
- component: ({ context }) => jsx(SpriteAnimationProperties, { sprite: context }),
18162
- },
18163
- {
18164
- section: "Other",
18165
- component: ({ context }) => jsx(SpriteOtherProperties, { sprite: context }),
18166
- },
18167
- ],
18168
- });
18169
- return {
18170
- dispose: () => {
18171
- spriteManagerSectionContentRegistration.dispose();
18172
- spriteSectionContentRegistration.dispose();
18173
- },
18174
- };
18175
- },
18176
- };
18177
-
18178
- /**
18179
- * Creates a lazy component wrapper that only calls the async function to get the underlying component when the lazy component is actually mounted.
18180
- * This allows deferring imports until they are needed. While the underlying component is being loaded, a spinner is displayed.
18181
- * @param getComponentAsync A function that returns a promise resolving to the component.
18182
- * @param defaultProps Options for the loading spinner.
18183
- * @returns A React component that displays a spinner while loading the async component.
18184
- */
18185
- function MakeLazyComponent(getComponentAsync, defaultProps) {
18186
- // eslint-disable-next-line @typescript-eslint/naming-convention
18187
- const LazyComponent = lazy(async () => {
18188
- const component = await getComponentAsync();
18189
- return { default: component };
18190
- });
18191
- return forwardRef((props, ref) => {
18192
- const { spinnerSize = defaultProps?.spinnerSize, spinnerLabel = defaultProps?.spinnerLabel, ...rest } = props;
18193
- const componentProps = { ...rest, ref };
18194
- return (jsx(Suspense, { fallback: jsx(Spinner, { ref: ref, size: spinnerSize, label: spinnerLabel }), children: jsx(LazyComponent, { ...componentProps }) }));
18195
- });
18196
- }
18197
-
18198
- const AdvancedDynamicTextureGeneralProperties = MakeLazyComponent(async () => {
18199
- // Defer importing anything from the gui package until this component is actually mounted.
18200
- const { AdvancedDynamicTextureInstrumentation } = await import('@babylonjs/gui/2D/adtInstrumentation.js');
18201
- return (props) => {
18202
- const { texture } = props;
18203
- const instrumentation = useResource(useCallback(() => {
18204
- const instrumentation = new AdvancedDynamicTextureInstrumentation(texture);
18205
- instrumentation.captureRenderTime = true;
18206
- instrumentation.captureLayoutTime = true;
18207
- return instrumentation;
18208
- }, [texture]));
18209
- const layoutTime = useObservableState(useCallback(() => instrumentation.layoutTimeCounter.current, [instrumentation.layoutTimeCounter]), usePollingObservable(1000));
18210
- const renderTime = useObservableState(useCallback(() => instrumentation.renderTimeCounter.current, [instrumentation.renderTimeCounter]), usePollingObservable(1000));
18211
- return (jsxs(Fragment, { children: [jsx(StringifiedPropertyLine, { label: "Last Layout Time", value: layoutTime, precision: 2, units: "ms" }), jsx(StringifiedPropertyLine, { label: "Last Render Time", value: renderTime, precision: 2, units: "ms" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Render Scale", target: texture, propertyKey: "renderScale", min: 0.1, max: 5, step: 0.1 }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Premultiply Alpha", target: texture, propertyKey: "premulAlpha" }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Ideal Width", target: texture, propertyKey: "idealWidth" }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Ideal Height", target: texture, propertyKey: "idealHeight" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Use Smallest Ideal", target: texture, propertyKey: "useSmallestIdeal" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Render at Ideal Size", target: texture, propertyKey: "renderAtIdealSize" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Invalidate Rect Optimization", target: texture, propertyKey: "useInvalidateRectOptimization" })] }));
18212
- };
18213
- }, { spinnerSize: "extra-tiny", spinnerLabel: "Loading..." });
18214
- const AdvancedDynamicTexturePreviewProperties = (props) => {
18215
- const { texture } = props;
18216
- return (jsx(Fragment, { children: jsx(ButtonLine, { label: "Edit GUI", icon: EditRegular, onClick: async () => {
18217
- const { GUIEditor } = await import('@babylonjs/gui-editor');
18218
- await GUIEditor.Show({ liveGuiTexture: texture });
18219
- } }) }));
18220
- };
18221
-
18222
- const TextureFormat = [
18223
- { label: "Alpha", normalizable: false, value: Constants.TEXTUREFORMAT_ALPHA },
18224
- { label: "Luminance", normalizable: false, value: Constants.TEXTUREFORMAT_LUMINANCE },
18225
- { label: "Luminance/Alpha", normalizable: false, value: Constants.TEXTUREFORMAT_LUMINANCE_ALPHA },
18226
- { label: "RGB", normalizable: true, value: Constants.TEXTUREFORMAT_RGB },
18227
- { label: "RGBA", normalizable: true, value: Constants.TEXTUREFORMAT_RGBA },
18228
- { label: "R (red)", normalizable: true, value: Constants.TEXTUREFORMAT_RED },
18229
- { label: "RG (red/green)", normalizable: true, value: Constants.TEXTUREFORMAT_RG },
18230
- { label: "R (red) integer", normalizable: false, value: Constants.TEXTUREFORMAT_RED_INTEGER },
18231
- { label: "RG (red/green) integer", normalizable: false, value: Constants.TEXTUREFORMAT_RG_INTEGER },
18232
- { label: "RGB integer", normalizable: false, value: Constants.TEXTUREFORMAT_RGB_INTEGER },
18233
- { label: "RGBA integer", normalizable: false, value: Constants.TEXTUREFORMAT_RGBA_INTEGER },
18234
- { label: "BGRA", normalizable: true, value: Constants.TEXTUREFORMAT_BGRA },
18235
- { label: "Depth24/Stencil8", normalizable: false, hideType: true, value: Constants.TEXTUREFORMAT_DEPTH24_STENCIL8 },
18236
- { label: "Depth32 float", normalizable: false, hideType: true, value: Constants.TEXTUREFORMAT_DEPTH32_FLOAT },
18237
- { label: "Depth16", normalizable: false, value: Constants.TEXTUREFORMAT_DEPTH16 },
18238
- { label: "Depth24", normalizable: false, value: Constants.TEXTUREFORMAT_DEPTH24 },
18239
- { label: "Depth24Unorm/Stencil8", normalizable: false, hideType: true, value: Constants.TEXTUREFORMAT_DEPTH24UNORM_STENCIL8 },
18240
- { label: "Depth32Float/Stencil8", normalizable: false, hideType: true, value: Constants.TEXTUREFORMAT_DEPTH32FLOAT_STENCIL8 },
18241
- { label: "RGBA BPTC UNorm", normalizable: false, compressed: true, value: Constants.TEXTUREFORMAT_COMPRESSED_RGBA_BPTC_UNORM },
18242
- { label: "RGB BPTC UFloat", normalizable: false, compressed: true, value: Constants.TEXTUREFORMAT_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT },
18243
- { label: "RGB BPTC SFloat", normalizable: false, compressed: true, value: Constants.TEXTUREFORMAT_COMPRESSED_RGB_BPTC_SIGNED_FLOAT },
18244
- { label: "RGBA S3TC DXT5", normalizable: false, compressed: true, value: Constants.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT5 },
18245
- { label: "RGBA S3TC DXT3", normalizable: false, compressed: true, value: Constants.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT3 },
18246
- { label: "RGBA S3TC DXT1", normalizable: false, compressed: true, value: Constants.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT1 },
18247
- { label: "RGB S3TC DXT1", normalizable: false, compressed: true, value: Constants.TEXTUREFORMAT_COMPRESSED_RGB_S3TC_DXT1 },
18248
- { label: "RGBA ASTC 4x4", normalizable: false, compressed: true, value: Constants.TEXTUREFORMAT_COMPRESSED_RGBA_ASTC_4x4 },
18249
- ];
18250
- function FindTextureFormat(format) {
18251
- for (let i = 0; i < TextureFormat.length; ++i) {
18252
- if (TextureFormat[i].value === format) {
18253
- return TextureFormat[i];
18254
- }
18255
- }
18256
- return null;
18257
- }
18258
- const TextureType = [
18259
- { label: "unsigned byte", normalizable: true, value: Constants.TEXTURETYPE_UNSIGNED_BYTE },
18260
- { label: "32-bit float", normalizable: false, value: Constants.TEXTURETYPE_FLOAT },
18261
- { label: "16-bit float", normalizable: false, value: Constants.TEXTURETYPE_HALF_FLOAT },
18262
- { label: "signed byte", normalizable: true, value: Constants.TEXTURETYPE_BYTE },
18263
- { label: "signed short", normalizable: false, value: Constants.TEXTURETYPE_SHORT },
18264
- { label: "unsigned short", normalizable: false, value: Constants.TEXTURETYPE_UNSIGNED_SHORT },
18265
- { label: "signed int", normalizable: false, value: Constants.TEXTURETYPE_INT },
18266
- { label: "unsigned int", normalizable: false, value: Constants.TEXTURETYPE_UNSIGNED_INTEGER },
18267
- { label: "unsigned 4/4/4/4 short", normalizable: false, value: Constants.TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4 },
18268
- { label: "unsigned 5/5/5/1 short", normalizable: false, value: Constants.TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1 },
18269
- { label: "unsigned 5/6/5 short", normalizable: false, value: Constants.TEXTURETYPE_UNSIGNED_SHORT_5_6_5 },
18270
- { label: "unsigned 2/10/10/10 int", normalizable: false, value: Constants.TEXTURETYPE_UNSIGNED_INT_2_10_10_10_REV },
18271
- { label: "unsigned 24/8 int", normalizable: false, value: Constants.TEXTURETYPE_UNSIGNED_INT_24_8 },
18272
- { label: "unsigned 10f/11f/11f int", normalizable: false, value: Constants.TEXTURETYPE_UNSIGNED_INT_10F_11F_11F_REV },
18273
- { label: "unsigned 5/9/9/9 int", normalizable: false, value: Constants.TEXTURETYPE_UNSIGNED_INT_5_9_9_9_REV },
18274
- { label: "32-bits with only 8-bit used (stencil)", normalizable: false, value: Constants.TEXTURETYPE_FLOAT_32_UNSIGNED_INT_24_8_REV },
18275
- ];
18276
- function FindTextureType(type) {
18277
- for (let i = 0; i < TextureType.length; ++i) {
18278
- if (TextureType[i].value === type) {
18279
- return TextureType[i];
18280
- }
18281
- }
18282
- return null;
18283
- }
18284
-
18285
- const BaseTexturePreviewProperties = (props) => {
18286
- // eslint-disable-next-line @typescript-eslint/naming-convention
18287
- const { texture, textureEditor: TextureEditor } = props;
18288
- const texturePreviewImperativeRef = useRef(null);
18289
- const childWindow = useRef(null);
18290
- return (jsxs(Fragment, { children: [jsx(TexturePreview, { imperativeRef: texturePreviewImperativeRef, texture: texture }), jsx(TextureUpload, { texture: texture }), jsx(ButtonLine, { label: "Edit Texture", onClick: () => childWindow.current?.open(), icon: EditRegular }), jsx(ChildWindow, { id: "Texture Editor", imperativeRef: childWindow, children: jsx(TextureEditor, { texture: texture, onUpdate: async () => await texturePreviewImperativeRef.current?.refresh() }) })] }));
18291
- };
18292
- const BaseTextureGeneralProperties = (props) => {
18293
- const { texture } = props;
18294
- const internalTexture = useProperty(texture, "_texture");
18295
- const internalUniqueId = useProperty(internalTexture, "uniqueId");
18296
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: TextInputPropertyLine, label: "Display Name", target: texture, propertyKey: "displayName" }), internalUniqueId != null ? (jsx(StringifiedPropertyLine, { label: "Internal Unique ID", description: "The unique ID of the internal texture.", value: internalUniqueId })) : (jsx(BooleanBadgePropertyLine, { label: "Internal Unique ID", description: "This texture has no internal texture.", value: false }))] }));
18297
- };
18298
- const CoordinatesMode = [
18299
- { label: "Explicit", value: Texture.EXPLICIT_MODE },
18300
- { label: "Cubic", value: Texture.CUBIC_MODE },
18301
- { label: "Inverse cubic", value: Texture.INVCUBIC_MODE },
18302
- { label: "Equirectangular", value: Texture.EQUIRECTANGULAR_MODE },
18303
- { label: "Fixed equirectangular", value: Texture.FIXED_EQUIRECTANGULAR_MODE },
18304
- { label: "Fixed equirectangular mirrored", value: Texture.FIXED_EQUIRECTANGULAR_MIRRORED_MODE },
18305
- { label: "Planar", value: Texture.PLANAR_MODE },
18306
- { label: "Projection", value: Texture.PROJECTION_MODE },
18307
- { label: "Skybox", value: Texture.SKYBOX_MODE },
18308
- { label: "Spherical", value: Texture.SPHERICAL_MODE },
18309
- ];
18310
- const BaseTextureCharacteristicProperties = (props) => {
18311
- const { texture } = props;
18312
- const internalTexture = useProperty(texture, "_texture");
18313
- const format = useProperty(internalTexture, "format") ?? NaN;
18314
- const type = useProperty(internalTexture, "type") ?? NaN;
18315
- const depth = useProperty(internalTexture, "depth");
18316
- const useSRGBBuffer = useProperty(internalTexture, "_useSRGBBuffer");
18317
- const samples = useProperty(internalTexture, "samples") ?? "?";
18318
- const displayFormat = FindTextureFormat(format === -1 ? Constants.TEXTUREFORMAT_RGBA : format);
18319
- const displayType = FindTextureType(type === -1 ? Constants.TEXTURETYPE_UNSIGNED_BYTE : type);
18320
- const maxAnisotropy = texture.getScene()?.getEngine().getCaps().maxAnisotropy ?? 1;
18321
- return (jsxs(Fragment, { children: [texture.is2DArray && jsx(TextPropertyLine, { label: "Layers", value: depth?.toString() ?? "?" }), texture.is3D && jsx(TextPropertyLine, { label: "Depth", value: depth?.toString() ?? "?" }), jsx(TextPropertyLine, { label: "Format", value: displayFormat?.label ?? "unknown" }), !displayFormat?.hideType && !displayFormat?.compressed && jsx(TextPropertyLine, { label: "Type", value: displayType?.label ?? "unknown" }), !!displayFormat?.normalizable && !displayFormat?.compressed && displayType?.normalizable != undefined && (jsx(BooleanBadgePropertyLine, { label: "Normalized", value: displayType.normalizable })), jsx(BooleanBadgePropertyLine, { label: "Compressed", value: displayFormat?.compressed ?? false }), jsx(BooleanBadgePropertyLine, { label: "sRGB Buffers", value: useSRGBBuffer ?? false }), jsx(BoundProperty, { component: BooleanBadgePropertyLine, label: "Gamma Space", target: texture, propertyKey: "gammaSpace" }), jsx(BoundProperty, { component: BooleanBadgePropertyLine, label: "Has Alpha", target: texture, propertyKey: "hasAlpha" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Alpha from RGB", target: texture, propertyKey: "getAlphaFromRGB" }), jsx(BooleanBadgePropertyLine, { label: "3D", value: texture.is3D }), jsx(BooleanBadgePropertyLine, { label: "2D Array", value: texture.is2DArray }), jsx(BooleanBadgePropertyLine, { label: "Cube", value: texture.isCube }), jsx(BooleanBadgePropertyLine, { label: "Render Target", value: texture.isRenderTarget }), jsx(BooleanBadgePropertyLine, { label: "Mipmaps", value: !texture.noMipmap }), jsx(TextPropertyLine, { label: "Samples", value: samples.toString() }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "UV Set", target: texture, propertyKey: "coordinatesIndex", min: 0, max: 3, step: 1 }), jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Mode", target: texture, propertyKey: "coordinatesMode", options: CoordinatesMode }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Level", target: texture, propertyKey: "level", min: 0, max: 2, step: 0.01 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Anisotropy", target: texture, propertyKey: "anisotropicFilteringLevel", min: 1, max: maxAnisotropy, step: 1 })] }));
18322
- };
18323
- const BaseTextureTransformProperties = (props) => {
18324
- const { texture } = props;
18325
- return (jsxs(Fragment, { children: [texture.canRescale && (jsx(ButtonLine, { label: "Scale Up", onClick: () => {
18326
- texture.scale(2);
18327
- } })), texture.canRescale && (jsx(ButtonLine, { label: "Scale Down", onClick: () => {
18328
- texture.scale(0.5);
18329
- } }))] }));
18330
- };
18331
-
18332
- const CubeTextureTransformProperties = (props) => {
18333
- const { texture } = props;
18334
- const [toDisplayAngle, fromDisplayAngle] = useAngleConverters();
18335
- return (jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Rotation Y", target: texture, propertyKey: "rotationY", min: 0, max: toDisplayAngle(Math.PI * 2), step: 0.0001, convertTo: (value) => toDisplayAngle(value, true), convertFrom: fromDisplayAngle }));
18336
- };
18337
-
18338
- const MultiRenderTargetGeneralProperties = (props) => {
18339
- const { texture } = props;
18340
- return (jsx(Fragment, { children: jsx(BoundProperty, { component: StringifiedPropertyLine, label: "Count", description: "The number of render target textures.", target: texture, propertyKey: "count" }) }));
18341
- };
18342
-
18343
- const RenderTargetTextureGeneralProperties = (props) => {
18344
- const { texture } = props;
18345
- const depthStencilTexture = useProperty(texture.renderTarget, "_depthStencilTexture");
18346
- const depthStencilTextureDisplayFormat = depthStencilTexture ? FindTextureFormat(depthStencilTexture.format) : null;
18347
- return (jsx(Fragment, { children: depthStencilTextureDisplayFormat ? (jsx(TextPropertyLine, { label: "Depth/Stencil Format", value: depthStencilTextureDisplayFormat.label })) : (jsx(BooleanBadgePropertyLine, { label: "Depth/Stencil Texture", value: false })) }));
18348
- };
18349
-
18350
- const TexturePreviewProperties = (props) => {
18351
- const { texture } = props;
18352
- const rawUrl = useProperty(texture, "url");
18353
- const displayUrl = !rawUrl || rawUrl.substring(0, 4) === "data" || rawUrl.substring(0, 4) === "blob" ? "" : rawUrl;
18354
- return (jsx(Fragment, { children: jsx(TextInputPropertyLine, { label: "URL", value: displayUrl, onChange: (value) => {
18355
- texture.updateURL(value);
18356
- } }) }));
18357
- };
18358
- const TextureGeneralProperties = (props) => {
18359
- const { texture } = props;
18360
- return (jsx(Fragment, { children: jsx(BooleanBadgePropertyLine, { label: "Invert Y", description: "If true, the texture is stored as inverted on Y", value: texture.invertY }) }));
18361
- };
18362
- const TextureTransformProperties = (props) => {
18363
- const { texture } = props;
18364
- const [toDisplayAngle, fromDisplayAngle] = useAngleConverters();
18365
- const wrapU = useProperty(texture, "wrapU");
18366
- const wrapV = useProperty(texture, "wrapV");
18367
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "U offset", target: texture, propertyKey: "uOffset", step: 0.01 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "V offset", target: texture, propertyKey: "vOffset", step: 0.01 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "U scale", target: texture, propertyKey: "uScale", step: 0.01 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "V scale", target: texture, propertyKey: "vScale", step: 0.01 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "U angle", target: texture, propertyKey: "uAng", min: 0, max: toDisplayAngle(Math.PI * 2), step: 0.01, convertTo: (value) => toDisplayAngle(value, true), convertFrom: fromDisplayAngle }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "V angle", target: texture, propertyKey: "vAng", min: 0, max: toDisplayAngle(Math.PI * 2), step: 0.01, convertTo: (value) => toDisplayAngle(value, true), convertFrom: fromDisplayAngle }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "W angle", target: texture, propertyKey: "wAng", min: 0, max: toDisplayAngle(Math.PI * 2), step: 0.01, convertTo: (value) => toDisplayAngle(value, true), convertFrom: fromDisplayAngle }), jsx(SwitchPropertyLine, { label: "Clamp U", value: wrapU === Constants.TEXTURE_CLAMP_ADDRESSMODE, onChange: (value) => (texture.wrapU = value ? Constants.TEXTURE_CLAMP_ADDRESSMODE : Constants.TEXTURE_WRAP_ADDRESSMODE) }), jsx(SwitchPropertyLine, { label: "Clamp V", value: wrapV === Constants.TEXTURE_CLAMP_ADDRESSMODE, onChange: (value) => (texture.wrapV = value ? Constants.TEXTURE_CLAMP_ADDRESSMODE : Constants.TEXTURE_WRAP_ADDRESSMODE) })] }));
18368
- };
18369
-
18370
- const SamplingMode = [
18371
- { label: "Nearest", value: Texture.NEAREST_NEAREST }, // 1
18372
- { label: "Linear", value: Texture.LINEAR_LINEAR }, // 2
18373
- { label: "Linear & linear mip", value: Texture.LINEAR_LINEAR_MIPLINEAR }, // 3
18374
- { label: "Linear & nearest mip", value: Texture.LINEAR_LINEAR_MIPNEAREST }, // 11
18375
- { label: "Nearest & linear mip", value: Texture.NEAREST_NEAREST_MIPLINEAR }, // 8
18376
- { label: "Nearest & nearest mip", value: Texture.NEAREST_NEAREST_MIPNEAREST }, // 4
18377
- { label: "Nearest/Linear", value: Texture.NEAREST_LINEAR }, // 7
18378
- { label: "Nearest/Linear & linear mip", value: Texture.NEAREST_LINEAR_MIPLINEAR }, // 6
18379
- { label: "Nearest/Linear & nearest mip", value: Texture.NEAREST_LINEAR_MIPNEAREST }, // 5
18380
- { label: "Linear/Nearest", value: Texture.LINEAR_NEAREST }, // 12
18381
- { label: "Linear/Nearest & linear mip", value: Texture.LINEAR_NEAREST_MIPLINEAR }, // 10
18382
- { label: "Linear/Nearest & nearest mip", value: Texture.LINEAR_NEAREST_MIPNEAREST }, // 9
18383
- ];
18384
- const ThinTextureGeneralProperties = (props) => {
18385
- const { texture } = props;
18386
- return (jsxs(Fragment, { children: [jsx(StringifiedPropertyLine, { label: "Width", value: texture.getSize().width, units: "px" }), jsx(StringifiedPropertyLine, { label: "Height", value: texture.getSize().height, units: "px" })] }));
18387
- };
18388
- const ThinTextureSamplingProperties = (props) => {
18389
- const { texture } = props;
18390
- const samplingMode = useObservableState(useCallback(() => texture.samplingMode, [texture]), useInterceptObservable("function", texture, "updateSamplingMode"));
18391
- return (jsx(Property, { component: NumberDropdownPropertyLine, label: "Sampling", propertyPath: "samplingMode", value: samplingMode, options: SamplingMode, onChange: (value) => texture.updateSamplingMode(value) }));
19045
+ });
19046
+ const spriteSectionContentRegistration = propertiesService.addSectionContent({
19047
+ key: "Sprite Properties",
19048
+ predicate: (entity) => entity instanceof Sprite,
19049
+ content: [
19050
+ {
19051
+ section: "General",
19052
+ component: ({ context }) => jsx(SpriteGeneralProperties, { sprite: context, selectionService: selectionService }),
19053
+ },
19054
+ {
19055
+ section: "Transform",
19056
+ component: ({ context }) => jsx(SpriteTransformProperties, { sprite: context }),
19057
+ },
19058
+ {
19059
+ section: "Cell",
19060
+ component: ({ context }) => jsx(SpriteCellProperties, { sprite: context }),
19061
+ },
19062
+ {
19063
+ section: "Animation",
19064
+ component: ({ context }) => jsx(SpriteAnimationProperties, { sprite: context }),
19065
+ },
19066
+ {
19067
+ section: "Other",
19068
+ component: ({ context }) => jsx(SpriteOtherProperties, { sprite: context }),
19069
+ },
19070
+ ],
19071
+ });
19072
+ return {
19073
+ dispose: () => {
19074
+ spriteManagerSectionContentRegistration.dispose();
19075
+ spriteSectionContentRegistration.dispose();
19076
+ },
19077
+ };
19078
+ },
18392
19079
  };
18393
19080
 
18394
19081
  // eslint-disable-next-line @typescript-eslint/naming-convention
@@ -19992,6 +20679,222 @@ const TextureEditorServiceDefinition = {
19992
20679
  },
19993
20680
  };
19994
20681
 
20682
+ /**
20683
+ * Creates a lazy component wrapper that only calls the async function to get the underlying component when the lazy component is actually mounted.
20684
+ * This allows deferring imports until they are needed. While the underlying component is being loaded, a spinner is displayed.
20685
+ * @param getComponentAsync A function that returns a promise resolving to the component.
20686
+ * @param defaultProps Options for the loading spinner.
20687
+ * @returns A React component that displays a spinner while loading the async component.
20688
+ */
20689
+ function MakeLazyComponent(getComponentAsync, defaultProps) {
20690
+ // eslint-disable-next-line @typescript-eslint/naming-convention
20691
+ const LazyComponent = lazy(async () => {
20692
+ const component = await getComponentAsync();
20693
+ return { default: component };
20694
+ });
20695
+ return forwardRef((props, ref) => {
20696
+ const { spinnerSize = defaultProps?.spinnerSize, spinnerLabel = defaultProps?.spinnerLabel, ...rest } = props;
20697
+ const componentProps = { ...rest, ref };
20698
+ return (jsx(Suspense, { fallback: jsx(Spinner, { ref: ref, size: spinnerSize, label: spinnerLabel }), children: jsx(LazyComponent, { ...componentProps }) }));
20699
+ });
20700
+ }
20701
+
20702
+ const AdvancedDynamicTextureGeneralProperties = MakeLazyComponent(async () => {
20703
+ // Defer importing anything from the gui package until this component is actually mounted.
20704
+ const { AdvancedDynamicTextureInstrumentation } = await import('@babylonjs/gui/2D/adtInstrumentation.js');
20705
+ return (props) => {
20706
+ const { texture } = props;
20707
+ const instrumentation = useResource(useCallback(() => {
20708
+ const instrumentation = new AdvancedDynamicTextureInstrumentation(texture);
20709
+ instrumentation.captureRenderTime = true;
20710
+ instrumentation.captureLayoutTime = true;
20711
+ return instrumentation;
20712
+ }, [texture]));
20713
+ const layoutTime = useObservableState(useCallback(() => instrumentation.layoutTimeCounter.current, [instrumentation.layoutTimeCounter]), usePollingObservable(1000));
20714
+ const renderTime = useObservableState(useCallback(() => instrumentation.renderTimeCounter.current, [instrumentation.renderTimeCounter]), usePollingObservable(1000));
20715
+ return (jsxs(Fragment, { children: [jsx(StringifiedPropertyLine, { label: "Last Layout Time", value: layoutTime, precision: 2, units: "ms" }), jsx(StringifiedPropertyLine, { label: "Last Render Time", value: renderTime, precision: 2, units: "ms" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Render Scale", target: texture, propertyKey: "renderScale", min: 0.1, max: 5, step: 0.1 }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Premultiply Alpha", target: texture, propertyKey: "premulAlpha" }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Ideal Width", target: texture, propertyKey: "idealWidth" }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Ideal Height", target: texture, propertyKey: "idealHeight" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Use Smallest Ideal", target: texture, propertyKey: "useSmallestIdeal" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Render at Ideal Size", target: texture, propertyKey: "renderAtIdealSize" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Invalidate Rect Optimization", target: texture, propertyKey: "useInvalidateRectOptimization" })] }));
20716
+ };
20717
+ }, { spinnerSize: "extra-tiny", spinnerLabel: "Loading..." });
20718
+ const AdvancedDynamicTexturePreviewProperties = (props) => {
20719
+ const { texture } = props;
20720
+ return (jsx(Fragment, { children: jsx(ButtonLine, { label: "Edit GUI", icon: EditRegular, onClick: async () => {
20721
+ const { GUIEditor } = await import('@babylonjs/gui-editor');
20722
+ await GUIEditor.Show({ liveGuiTexture: texture });
20723
+ } }) }));
20724
+ };
20725
+
20726
+ const TextureFormat = [
20727
+ { label: "Alpha", normalizable: false, value: Constants.TEXTUREFORMAT_ALPHA },
20728
+ { label: "Luminance", normalizable: false, value: Constants.TEXTUREFORMAT_LUMINANCE },
20729
+ { label: "Luminance/Alpha", normalizable: false, value: Constants.TEXTUREFORMAT_LUMINANCE_ALPHA },
20730
+ { label: "RGB", normalizable: true, value: Constants.TEXTUREFORMAT_RGB },
20731
+ { label: "RGBA", normalizable: true, value: Constants.TEXTUREFORMAT_RGBA },
20732
+ { label: "R (red)", normalizable: true, value: Constants.TEXTUREFORMAT_RED },
20733
+ { label: "RG (red/green)", normalizable: true, value: Constants.TEXTUREFORMAT_RG },
20734
+ { label: "R (red) integer", normalizable: false, value: Constants.TEXTUREFORMAT_RED_INTEGER },
20735
+ { label: "RG (red/green) integer", normalizable: false, value: Constants.TEXTUREFORMAT_RG_INTEGER },
20736
+ { label: "RGB integer", normalizable: false, value: Constants.TEXTUREFORMAT_RGB_INTEGER },
20737
+ { label: "RGBA integer", normalizable: false, value: Constants.TEXTUREFORMAT_RGBA_INTEGER },
20738
+ { label: "BGRA", normalizable: true, value: Constants.TEXTUREFORMAT_BGRA },
20739
+ { label: "Depth24/Stencil8", normalizable: false, hideType: true, value: Constants.TEXTUREFORMAT_DEPTH24_STENCIL8 },
20740
+ { label: "Depth32 float", normalizable: false, hideType: true, value: Constants.TEXTUREFORMAT_DEPTH32_FLOAT },
20741
+ { label: "Depth16", normalizable: false, value: Constants.TEXTUREFORMAT_DEPTH16 },
20742
+ { label: "Depth24", normalizable: false, value: Constants.TEXTUREFORMAT_DEPTH24 },
20743
+ { label: "Depth24Unorm/Stencil8", normalizable: false, hideType: true, value: Constants.TEXTUREFORMAT_DEPTH24UNORM_STENCIL8 },
20744
+ { label: "Depth32Float/Stencil8", normalizable: false, hideType: true, value: Constants.TEXTUREFORMAT_DEPTH32FLOAT_STENCIL8 },
20745
+ { label: "RGBA BPTC UNorm", normalizable: false, compressed: true, value: Constants.TEXTUREFORMAT_COMPRESSED_RGBA_BPTC_UNORM },
20746
+ { label: "RGB BPTC UFloat", normalizable: false, compressed: true, value: Constants.TEXTUREFORMAT_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT },
20747
+ { label: "RGB BPTC SFloat", normalizable: false, compressed: true, value: Constants.TEXTUREFORMAT_COMPRESSED_RGB_BPTC_SIGNED_FLOAT },
20748
+ { label: "RGBA S3TC DXT5", normalizable: false, compressed: true, value: Constants.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT5 },
20749
+ { label: "RGBA S3TC DXT3", normalizable: false, compressed: true, value: Constants.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT3 },
20750
+ { label: "RGBA S3TC DXT1", normalizable: false, compressed: true, value: Constants.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT1 },
20751
+ { label: "RGB S3TC DXT1", normalizable: false, compressed: true, value: Constants.TEXTUREFORMAT_COMPRESSED_RGB_S3TC_DXT1 },
20752
+ { label: "RGBA ASTC 4x4", normalizable: false, compressed: true, value: Constants.TEXTUREFORMAT_COMPRESSED_RGBA_ASTC_4x4 },
20753
+ ];
20754
+ function FindTextureFormat(format) {
20755
+ for (let i = 0; i < TextureFormat.length; ++i) {
20756
+ if (TextureFormat[i].value === format) {
20757
+ return TextureFormat[i];
20758
+ }
20759
+ }
20760
+ return null;
20761
+ }
20762
+ const TextureType = [
20763
+ { label: "unsigned byte", normalizable: true, value: Constants.TEXTURETYPE_UNSIGNED_BYTE },
20764
+ { label: "32-bit float", normalizable: false, value: Constants.TEXTURETYPE_FLOAT },
20765
+ { label: "16-bit float", normalizable: false, value: Constants.TEXTURETYPE_HALF_FLOAT },
20766
+ { label: "signed byte", normalizable: true, value: Constants.TEXTURETYPE_BYTE },
20767
+ { label: "signed short", normalizable: false, value: Constants.TEXTURETYPE_SHORT },
20768
+ { label: "unsigned short", normalizable: false, value: Constants.TEXTURETYPE_UNSIGNED_SHORT },
20769
+ { label: "signed int", normalizable: false, value: Constants.TEXTURETYPE_INT },
20770
+ { label: "unsigned int", normalizable: false, value: Constants.TEXTURETYPE_UNSIGNED_INTEGER },
20771
+ { label: "unsigned 4/4/4/4 short", normalizable: false, value: Constants.TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4 },
20772
+ { label: "unsigned 5/5/5/1 short", normalizable: false, value: Constants.TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1 },
20773
+ { label: "unsigned 5/6/5 short", normalizable: false, value: Constants.TEXTURETYPE_UNSIGNED_SHORT_5_6_5 },
20774
+ { label: "unsigned 2/10/10/10 int", normalizable: false, value: Constants.TEXTURETYPE_UNSIGNED_INT_2_10_10_10_REV },
20775
+ { label: "unsigned 24/8 int", normalizable: false, value: Constants.TEXTURETYPE_UNSIGNED_INT_24_8 },
20776
+ { label: "unsigned 10f/11f/11f int", normalizable: false, value: Constants.TEXTURETYPE_UNSIGNED_INT_10F_11F_11F_REV },
20777
+ { label: "unsigned 5/9/9/9 int", normalizable: false, value: Constants.TEXTURETYPE_UNSIGNED_INT_5_9_9_9_REV },
20778
+ { label: "32-bits with only 8-bit used (stencil)", normalizable: false, value: Constants.TEXTURETYPE_FLOAT_32_UNSIGNED_INT_24_8_REV },
20779
+ ];
20780
+ function FindTextureType(type) {
20781
+ for (let i = 0; i < TextureType.length; ++i) {
20782
+ if (TextureType[i].value === type) {
20783
+ return TextureType[i];
20784
+ }
20785
+ }
20786
+ return null;
20787
+ }
20788
+
20789
+ const BaseTexturePreviewProperties = (props) => {
20790
+ // eslint-disable-next-line @typescript-eslint/naming-convention
20791
+ const { texture, textureEditor: TextureEditor } = props;
20792
+ const texturePreviewImperativeRef = useRef(null);
20793
+ const childWindow = useRef(null);
20794
+ return (jsxs(Fragment, { children: [jsx(TexturePreview, { imperativeRef: texturePreviewImperativeRef, texture: texture }), jsx(TextureUpload, { texture: texture }), jsx(ButtonLine, { label: "Edit Texture", onClick: () => childWindow.current?.open(), icon: EditRegular }), jsx(ChildWindow, { id: "Texture Editor", imperativeRef: childWindow, children: jsx(TextureEditor, { texture: texture, onUpdate: async () => await texturePreviewImperativeRef.current?.refresh() }) })] }));
20795
+ };
20796
+ const BaseTextureGeneralProperties = (props) => {
20797
+ const { texture } = props;
20798
+ const internalTexture = useProperty(texture, "_texture");
20799
+ const internalUniqueId = useProperty(internalTexture, "uniqueId");
20800
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: TextInputPropertyLine, label: "Display Name", target: texture, propertyKey: "displayName" }), internalUniqueId != null ? (jsx(StringifiedPropertyLine, { label: "Internal Unique ID", description: "The unique ID of the internal texture.", value: internalUniqueId })) : (jsx(BooleanBadgePropertyLine, { label: "Internal Unique ID", description: "This texture has no internal texture.", value: false }))] }));
20801
+ };
20802
+ const CoordinatesMode = [
20803
+ { label: "Explicit", value: Texture.EXPLICIT_MODE },
20804
+ { label: "Cubic", value: Texture.CUBIC_MODE },
20805
+ { label: "Inverse cubic", value: Texture.INVCUBIC_MODE },
20806
+ { label: "Equirectangular", value: Texture.EQUIRECTANGULAR_MODE },
20807
+ { label: "Fixed equirectangular", value: Texture.FIXED_EQUIRECTANGULAR_MODE },
20808
+ { label: "Fixed equirectangular mirrored", value: Texture.FIXED_EQUIRECTANGULAR_MIRRORED_MODE },
20809
+ { label: "Planar", value: Texture.PLANAR_MODE },
20810
+ { label: "Projection", value: Texture.PROJECTION_MODE },
20811
+ { label: "Skybox", value: Texture.SKYBOX_MODE },
20812
+ { label: "Spherical", value: Texture.SPHERICAL_MODE },
20813
+ ];
20814
+ const BaseTextureCharacteristicProperties = (props) => {
20815
+ const { texture } = props;
20816
+ const internalTexture = useProperty(texture, "_texture");
20817
+ const format = useProperty(internalTexture, "format") ?? NaN;
20818
+ const type = useProperty(internalTexture, "type") ?? NaN;
20819
+ const depth = useProperty(internalTexture, "depth");
20820
+ const useSRGBBuffer = useProperty(internalTexture, "_useSRGBBuffer");
20821
+ const samples = useProperty(internalTexture, "samples") ?? "?";
20822
+ const displayFormat = FindTextureFormat(format === -1 ? Constants.TEXTUREFORMAT_RGBA : format);
20823
+ const displayType = FindTextureType(type === -1 ? Constants.TEXTURETYPE_UNSIGNED_BYTE : type);
20824
+ const maxAnisotropy = texture.getScene()?.getEngine().getCaps().maxAnisotropy ?? 1;
20825
+ return (jsxs(Fragment, { children: [texture.is2DArray && jsx(TextPropertyLine, { label: "Layers", value: depth?.toString() ?? "?" }), texture.is3D && jsx(TextPropertyLine, { label: "Depth", value: depth?.toString() ?? "?" }), jsx(TextPropertyLine, { label: "Format", value: displayFormat?.label ?? "unknown" }), !displayFormat?.hideType && !displayFormat?.compressed && jsx(TextPropertyLine, { label: "Type", value: displayType?.label ?? "unknown" }), !!displayFormat?.normalizable && !displayFormat?.compressed && displayType?.normalizable != undefined && (jsx(BooleanBadgePropertyLine, { label: "Normalized", value: displayType.normalizable })), jsx(BooleanBadgePropertyLine, { label: "Compressed", value: displayFormat?.compressed ?? false }), jsx(BooleanBadgePropertyLine, { label: "sRGB Buffers", value: useSRGBBuffer ?? false }), jsx(BoundProperty, { component: BooleanBadgePropertyLine, label: "Gamma Space", target: texture, propertyKey: "gammaSpace" }), jsx(BoundProperty, { component: BooleanBadgePropertyLine, label: "Has Alpha", target: texture, propertyKey: "hasAlpha" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Alpha from RGB", target: texture, propertyKey: "getAlphaFromRGB" }), jsx(BooleanBadgePropertyLine, { label: "3D", value: texture.is3D }), jsx(BooleanBadgePropertyLine, { label: "2D Array", value: texture.is2DArray }), jsx(BooleanBadgePropertyLine, { label: "Cube", value: texture.isCube }), jsx(BooleanBadgePropertyLine, { label: "Render Target", value: texture.isRenderTarget }), jsx(BooleanBadgePropertyLine, { label: "Mipmaps", value: !texture.noMipmap }), jsx(TextPropertyLine, { label: "Samples", value: samples.toString() }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "UV Set", target: texture, propertyKey: "coordinatesIndex", min: 0, max: 3, step: 1 }), jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Mode", target: texture, propertyKey: "coordinatesMode", options: CoordinatesMode }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Level", target: texture, propertyKey: "level", min: 0, max: 2, step: 0.01 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Anisotropy", target: texture, propertyKey: "anisotropicFilteringLevel", min: 1, max: maxAnisotropy, step: 1 })] }));
20826
+ };
20827
+ const BaseTextureTransformProperties = (props) => {
20828
+ const { texture } = props;
20829
+ return (jsxs(Fragment, { children: [texture.canRescale && (jsx(ButtonLine, { label: "Scale Up", onClick: () => {
20830
+ texture.scale(2);
20831
+ } })), texture.canRescale && (jsx(ButtonLine, { label: "Scale Down", onClick: () => {
20832
+ texture.scale(0.5);
20833
+ } }))] }));
20834
+ };
20835
+
20836
+ const CubeTextureTransformProperties = (props) => {
20837
+ const { texture } = props;
20838
+ const [toDisplayAngle, fromDisplayAngle] = useAngleConverters();
20839
+ return (jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Rotation Y", target: texture, propertyKey: "rotationY", min: 0, max: toDisplayAngle(Math.PI * 2), step: 0.0001, convertTo: (value) => toDisplayAngle(value, true), convertFrom: fromDisplayAngle }));
20840
+ };
20841
+
20842
+ const MultiRenderTargetGeneralProperties = (props) => {
20843
+ const { texture } = props;
20844
+ return (jsx(Fragment, { children: jsx(BoundProperty, { component: StringifiedPropertyLine, label: "Count", description: "The number of render target textures.", target: texture, propertyKey: "count" }) }));
20845
+ };
20846
+
20847
+ const RenderTargetTextureGeneralProperties = (props) => {
20848
+ const { texture } = props;
20849
+ const depthStencilTexture = useProperty(texture.renderTarget, "_depthStencilTexture");
20850
+ const depthStencilTextureDisplayFormat = depthStencilTexture ? FindTextureFormat(depthStencilTexture.format) : null;
20851
+ return (jsx(Fragment, { children: depthStencilTextureDisplayFormat ? (jsx(TextPropertyLine, { label: "Depth/Stencil Format", value: depthStencilTextureDisplayFormat.label })) : (jsx(BooleanBadgePropertyLine, { label: "Depth/Stencil Texture", value: false })) }));
20852
+ };
20853
+
20854
+ const TexturePreviewProperties = (props) => {
20855
+ const { texture } = props;
20856
+ const rawUrl = useProperty(texture, "url");
20857
+ const displayUrl = !rawUrl || rawUrl.substring(0, 4) === "data" || rawUrl.substring(0, 4) === "blob" ? "" : rawUrl;
20858
+ return (jsx(Fragment, { children: jsx(TextInputPropertyLine, { label: "URL", value: displayUrl, onChange: (value) => {
20859
+ texture.updateURL(value);
20860
+ } }) }));
20861
+ };
20862
+ const TextureGeneralProperties = (props) => {
20863
+ const { texture } = props;
20864
+ return (jsx(Fragment, { children: jsx(BooleanBadgePropertyLine, { label: "Invert Y", description: "If true, the texture is stored as inverted on Y", value: texture.invertY }) }));
20865
+ };
20866
+ const TextureTransformProperties = (props) => {
20867
+ const { texture } = props;
20868
+ const [toDisplayAngle, fromDisplayAngle] = useAngleConverters();
20869
+ const wrapU = useProperty(texture, "wrapU");
20870
+ const wrapV = useProperty(texture, "wrapV");
20871
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "U offset", target: texture, propertyKey: "uOffset", step: 0.01 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "V offset", target: texture, propertyKey: "vOffset", step: 0.01 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "U scale", target: texture, propertyKey: "uScale", step: 0.01 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "V scale", target: texture, propertyKey: "vScale", step: 0.01 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "U angle", target: texture, propertyKey: "uAng", min: 0, max: toDisplayAngle(Math.PI * 2), step: 0.01, convertTo: (value) => toDisplayAngle(value, true), convertFrom: fromDisplayAngle }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "V angle", target: texture, propertyKey: "vAng", min: 0, max: toDisplayAngle(Math.PI * 2), step: 0.01, convertTo: (value) => toDisplayAngle(value, true), convertFrom: fromDisplayAngle }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "W angle", target: texture, propertyKey: "wAng", min: 0, max: toDisplayAngle(Math.PI * 2), step: 0.01, convertTo: (value) => toDisplayAngle(value, true), convertFrom: fromDisplayAngle }), jsx(SwitchPropertyLine, { label: "Clamp U", value: wrapU === Constants.TEXTURE_CLAMP_ADDRESSMODE, onChange: (value) => (texture.wrapU = value ? Constants.TEXTURE_CLAMP_ADDRESSMODE : Constants.TEXTURE_WRAP_ADDRESSMODE) }), jsx(SwitchPropertyLine, { label: "Clamp V", value: wrapV === Constants.TEXTURE_CLAMP_ADDRESSMODE, onChange: (value) => (texture.wrapV = value ? Constants.TEXTURE_CLAMP_ADDRESSMODE : Constants.TEXTURE_WRAP_ADDRESSMODE) })] }));
20872
+ };
20873
+
20874
+ const SamplingMode = [
20875
+ { label: "Nearest", value: Texture.NEAREST_NEAREST }, // 1
20876
+ { label: "Linear", value: Texture.LINEAR_LINEAR }, // 2
20877
+ { label: "Linear & linear mip", value: Texture.LINEAR_LINEAR_MIPLINEAR }, // 3
20878
+ { label: "Linear & nearest mip", value: Texture.LINEAR_LINEAR_MIPNEAREST }, // 11
20879
+ { label: "Nearest & linear mip", value: Texture.NEAREST_NEAREST_MIPLINEAR }, // 8
20880
+ { label: "Nearest & nearest mip", value: Texture.NEAREST_NEAREST_MIPNEAREST }, // 4
20881
+ { label: "Nearest/Linear", value: Texture.NEAREST_LINEAR }, // 7
20882
+ { label: "Nearest/Linear & linear mip", value: Texture.NEAREST_LINEAR_MIPLINEAR }, // 6
20883
+ { label: "Nearest/Linear & nearest mip", value: Texture.NEAREST_LINEAR_MIPNEAREST }, // 5
20884
+ { label: "Linear/Nearest", value: Texture.LINEAR_NEAREST }, // 12
20885
+ { label: "Linear/Nearest & linear mip", value: Texture.LINEAR_NEAREST_MIPLINEAR }, // 10
20886
+ { label: "Linear/Nearest & nearest mip", value: Texture.LINEAR_NEAREST_MIPNEAREST }, // 9
20887
+ ];
20888
+ const ThinTextureGeneralProperties = (props) => {
20889
+ const { texture } = props;
20890
+ return (jsxs(Fragment, { children: [jsx(StringifiedPropertyLine, { label: "Width", value: texture.getSize().width, units: "px" }), jsx(StringifiedPropertyLine, { label: "Height", value: texture.getSize().height, units: "px" })] }));
20891
+ };
20892
+ const ThinTextureSamplingProperties = (props) => {
20893
+ const { texture } = props;
20894
+ const samplingMode = useObservableState(useCallback(() => texture.samplingMode, [texture]), useInterceptObservable("function", texture, "updateSamplingMode"));
20895
+ return (jsx(Property, { component: NumberDropdownPropertyLine, label: "Sampling", propertyPath: "samplingMode", value: samplingMode, options: SamplingMode, onChange: (value) => texture.updateSamplingMode(value) }));
20896
+ };
20897
+
19995
20898
  // Don't use instanceof in this case as we don't want to bring in the gui package just to check if the entity is an AdvancedDynamicTexture.
19996
20899
  function IsAdvancedDynamicTexture$1(entity) {
19997
20900
  return entity?.getClassName?.() === "AdvancedDynamicTexture";
@@ -22086,6 +22989,11 @@ function ShowInspector(scene, options = {}) {
22086
22989
  });
22087
22990
  // This array will contain all the default Inspector service definitions.
22088
22991
  const serviceDefinitions = [];
22992
+ // Ensure the inspectable bridge is running for this scene. The inspector's
22993
+ // ServiceContainer will use the inspectable container as a parent, inheriting
22994
+ // services like ISceneContext and IInspectableCommandRegistry.
22995
+ const inspectableToken = _StartInspectable(scene);
22996
+ disposeActions.push(() => inspectableToken.dispose());
22089
22997
  // Create a container element for the inspector UI.
22090
22998
  // This element will become the root React node, so it must be a new empty node
22091
22999
  // since React will completely take over its contents.
@@ -22135,23 +23043,13 @@ function ShowInspector(scene, options = {}) {
22135
23043
  disposeActions.push(() => {
22136
23044
  parentElement.removeChild(containerElement);
22137
23045
  });
22138
- // This service exposes the scene that was passed into Inspector through ISceneContext, which is used by other services that may be used in other contexts outside of Inspector.
22139
- const sceneContextServiceDefinition = {
22140
- friendlyName: "Inspector Scene Context",
22141
- produces: [SceneContextIdentity],
22142
- factory: () => {
22143
- return {
22144
- currentScene: scene,
22145
- currentSceneObservable: new Observable(),
22146
- };
22147
- },
22148
- };
22149
- serviceDefinitions.push(sceneContextServiceDefinition);
22150
23046
  if (options.autoResizeEngine) {
22151
23047
  const observer = scene.onBeforeRenderObservable.add(() => scene.getEngine().resize());
22152
23048
  disposeActions.push(() => observer.remove());
22153
23049
  }
22154
23050
  serviceDefinitions.push(
23051
+ // Watcher service for observing property changes.
23052
+ WatcherServiceDefinition,
22155
23053
  // Helps with managing gizmos and a shared utility layer.
22156
23054
  GizmoServiceDefinition,
22157
23055
  // Scene explorer tab and related services.
@@ -22180,6 +23078,8 @@ function ShowInspector(scene, options = {}) {
22180
23078
  HighlightServiceDefinition,
22181
23079
  // Adds entry points for user feedback on Inspector v2 (probably eventually will be removed).
22182
23080
  UserFeedbackServiceDefinition,
23081
+ // Shows CLI bridge connection status in the toolbar.
23082
+ CliConnectionStatusServiceDefinition,
22183
23083
  // Adds always present "mini stats" (like fps) to the toolbar, etc.
22184
23084
  MiniStatsServiceDefinition,
22185
23085
  // Legacy service to support custom inspectable properties on objects.
@@ -22187,6 +23087,7 @@ function ShowInspector(scene, options = {}) {
22187
23087
  const modularTool = MakeModularTool({
22188
23088
  namespace: "Inspector",
22189
23089
  containerElement,
23090
+ parentContainer: inspectableToken.serviceContainer,
22190
23091
  serviceDefinitions: [
22191
23092
  // Default Inspector services.
22192
23093
  ...serviceDefinitions,
@@ -23021,5 +23922,5 @@ const TextAreaPropertyLine = (props) => {
23021
23922
  // Attach Inspector v2 to Scene.debugLayer as a side effect for back compat.
23022
23923
  AttachDebugLayer();
23023
23924
 
23024
- export { GizmoServiceIdentity as $, Accordion as A, Button as B, CheckboxPropertyLine as C, ColorPickerPopup as D, ColorStepGradientComponent as E, ComboBox as F, ComboBoxPropertyLine as G, ConstructorFactory as H, ConvertOptions as I, DebugServiceIdentity as J, DetachDebugLayer as K, LinkToEntity as L, MessageBar as M, NumberInputPropertyLine as N, DraggableLine as O, Popover as P, Dropdown as Q, EntitySelector as R, SpinButtonPropertyLine as S, TextInputPropertyLine as T, ErrorBoundary as U, Vector3PropertyLine as V, ExtensibleAccordion as W, FactorGradientComponent as X, FactorGradientList as Y, FileUploadLine as Z, GetPropertyDescriptor as _, useInterceptObservable as a, UploadButton as a$, HexPropertyLine as a0, InfoLabel as a1, InputHexField as a2, InputHsvField as a3, Inspector as a4, InterceptFunction as a5, InterceptProperty as a6, IsPropertyReadonly as a7, LineContainer as a8, LinkPropertyLine as a9, SettingsStore as aA, SettingsStoreIdentity as aB, ShowInspector as aC, SidePaneContainer as aD, SkeletonSelector as aE, Slider as aF, SpinButton as aG, StatsServiceIdentity as aH, StringDropdown as aI, StringDropdownPropertyLine as aJ, StringifiedPropertyLine as aK, Switch as aL, SwitchPropertyLine as aM, SyncedSliderInput as aN, SyncedSliderPropertyLine as aO, TeachingMoment as aP, TextAreaPropertyLine as aQ, TextInput as aR, TextPropertyLine as aS, Textarea as aT, TextureSelector as aU, TextureUpload as aV, Theme as aW, ThemeServiceIdentity as aX, ToastProvider as aY, ToggleButton as aZ, Tooltip as a_, LinkToEntityPropertyLine as aa, List as ab, MakeDialogTeachingMoment as ac, MakeLazyComponent as ad, MakePopoverTeachingMoment as ae, MakePropertyHook as af, MakeTeachingMoment as ag, MaterialSelector as ah, NodeSelector as ai, NumberDropdown as aj, NumberDropdownPropertyLine as ak, ObservableCollection as al, Pane as am, PlaceholderPropertyLine as an, PositionedPopover as ao, PropertiesServiceIdentity as ap, Property as aq, PropertyContext as ar, PropertyLine as as, QuaternionPropertyLine as at, RotationVectorPropertyLine as au, SceneExplorerServiceIdentity as av, SearchBar as aw, SearchBox as ax, SelectionServiceDefinition as ay, SettingsServiceIdentity as az, useProperty as b, Vector2PropertyLine as b0, Vector4PropertyLine as b1, WatcherServiceIdentity as b2, useAngleConverters as b3, useAsyncResource as b4, useColor3Property as b5, useColor4Property as b6, useEventListener as b7, useEventfulState as b8, useKeyListener as b9, useKeyState as ba, useObservableCollection as bb, useOrderedObservableCollection as bc, usePollingObservable as bd, usePropertyChangedNotifier as be, useQuaternionProperty as bf, useResource as bg, useSetting as bh, useTheme as bi, useThemeMode as bj, useVector3Property as bk, ShellServiceIdentity as c, SceneContextIdentity as d, SelectionServiceIdentity as e, useObservableState as f, AccordionSection as g, ButtonLine as h, ToolsServiceIdentity as i, useExtensionManager as j, Link as k, AccordionSectionItem as l, AttachDebugLayer as m, BooleanBadgePropertyLine as n, BoundProperty as o, BuiltInsExtensionFeed as p, Checkbox as q, ChildWindow as r, Collapse as s, Color3GradientComponent as t, useToast as u, Color3GradientList as v, Color3PropertyLine as w, Color4GradientComponent as x, Color4GradientList as y, Color4PropertyLine as z };
23025
- //# sourceMappingURL=index-DuVF9zYN.js.map
23925
+ export { GizmoServiceIdentity as $, Accordion as A, Button as B, CheckboxPropertyLine as C, ColorPickerPopup as D, ColorStepGradientComponent as E, ComboBox as F, ComboBoxPropertyLine as G, ConstructorFactory as H, ConvertOptions as I, DebugServiceIdentity as J, DetachDebugLayer as K, Link as L, MessageBar as M, NumberInputPropertyLine as N, DraggableLine as O, Popover as P, Dropdown as Q, EntitySelector as R, ShellServiceIdentity as S, TextInputPropertyLine as T, ErrorBoundary as U, Vector3PropertyLine as V, ExtensibleAccordion as W, FactorGradientComponent as X, FactorGradientList as Y, FileUploadLine as Z, GetPropertyDescriptor as _, useToast as a, Tooltip as a$, HexPropertyLine as a0, InfoLabel as a1, InputHexField as a2, InputHsvField as a3, InspectableCommandRegistryIdentity as a4, Inspector as a5, InterceptFunction as a6, InterceptProperty as a7, IsPropertyReadonly as a8, LineContainer as a9, SettingsServiceIdentity as aA, SettingsStoreIdentity as aB, ShowInspector as aC, SidePaneContainer as aD, SkeletonSelector as aE, Slider as aF, SpinButton as aG, StartInspectable as aH, StatsServiceIdentity as aI, StringDropdown as aJ, StringDropdownPropertyLine as aK, StringifiedPropertyLine as aL, Switch as aM, SwitchPropertyLine as aN, SyncedSliderInput as aO, SyncedSliderPropertyLine as aP, TeachingMoment as aQ, TextAreaPropertyLine as aR, TextInput as aS, TextPropertyLine as aT, Textarea as aU, TextureSelector as aV, TextureUpload as aW, Theme as aX, ThemeServiceIdentity as aY, ToastProvider as aZ, ToggleButton as a_, LinkPropertyLine as aa, LinkToEntityPropertyLine as ab, List as ac, MakeDialogTeachingMoment as ad, MakeLazyComponent as ae, MakePopoverTeachingMoment as af, MakePropertyHook as ag, MakeTeachingMoment as ah, MaterialSelector as ai, NodeSelector as aj, NumberDropdown as ak, NumberDropdownPropertyLine as al, ObservableCollection as am, Pane as an, PlaceholderPropertyLine as ao, PositionedPopover as ap, PropertiesServiceIdentity as aq, Property as ar, PropertyContext as as, PropertyLine as at, QuaternionPropertyLine as au, RotationVectorPropertyLine as av, SceneExplorerServiceIdentity as aw, SearchBar as ax, SearchBox as ay, SelectionServiceDefinition as az, useInterceptObservable as b, UploadButton as b0, Vector2PropertyLine as b1, Vector4PropertyLine as b2, WatcherServiceIdentity as b3, useAngleConverters as b4, useAsyncResource as b5, useColor3Property as b6, useColor4Property as b7, useEventListener as b8, useEventfulState as b9, useKeyListener as ba, useKeyState as bb, useObservableCollection as bc, useOrderedObservableCollection as bd, usePollingObservable as be, usePropertyChangedNotifier as bf, useQuaternionProperty as bg, useResource as bh, useSetting as bi, useTheme as bj, useThemeMode as bk, useVector3Property as bl, LinkToEntity as c, SpinButtonPropertyLine as d, useProperty as e, SceneContextIdentity as f, SelectionServiceIdentity as g, useObservableState as h, AccordionSection as i, ButtonLine as j, ToolsServiceIdentity as k, AccordionSectionItem as l, AttachDebugLayer as m, BooleanBadgePropertyLine as n, BoundProperty as o, BuiltInsExtensionFeed as p, Checkbox as q, ChildWindow as r, Collapse as s, Color3GradientComponent as t, useExtensionManager as u, Color3GradientList as v, Color3PropertyLine as w, Color4GradientComponent as x, Color4GradientList as y, Color4PropertyLine as z };
23926
+ //# sourceMappingURL=index-FWuITINA.js.map