super-three 0.181.0 → 0.185.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 (693) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +3 -4
  3. package/build/three.cjs +21332 -19201
  4. package/build/three.core.js +20908 -19853
  5. package/build/three.core.min.js +2 -2
  6. package/build/three.module.js +1947 -862
  7. package/build/three.module.min.js +2 -2
  8. package/build/three.tsl.js +37 -16
  9. package/build/three.tsl.min.js +2 -2
  10. package/build/three.webgpu.js +33628 -25197
  11. package/build/three.webgpu.min.js +2 -2
  12. package/build/three.webgpu.nodes.js +33394 -25000
  13. package/build/three.webgpu.nodes.min.js +2 -2
  14. package/examples/jsm/Addons.js +14 -3
  15. package/examples/jsm/animation/CCDIKSolver.js +7 -3
  16. package/examples/jsm/controls/ArcballControls.js +13 -6
  17. package/examples/jsm/controls/DragControls.js +2 -2
  18. package/examples/jsm/controls/FirstPersonControls.js +127 -86
  19. package/examples/jsm/controls/FlyControls.js +4 -0
  20. package/examples/jsm/controls/MapControls.js +55 -1
  21. package/examples/jsm/controls/OrbitControls.js +111 -8
  22. package/examples/jsm/controls/TrackballControls.js +8 -8
  23. package/examples/jsm/controls/TransformControls.js +102 -17
  24. package/examples/jsm/csm/CSM.js +2 -1
  25. package/examples/jsm/csm/CSMFrustum.js +31 -6
  26. package/examples/jsm/csm/CSMShadowNode.js +10 -3
  27. package/examples/jsm/effects/AnaglyphEffect.js +102 -7
  28. package/examples/jsm/effects/AsciiEffect.js +14 -1
  29. package/examples/jsm/environments/ColorEnvironment.js +59 -0
  30. package/examples/jsm/environments/RoomEnvironment.js +3 -0
  31. package/examples/jsm/exporters/DRACOExporter.js +27 -6
  32. package/examples/jsm/exporters/EXRExporter.js +1 -1
  33. package/examples/jsm/exporters/GLTFExporter.js +266 -21
  34. package/examples/jsm/exporters/PLYExporter.js +286 -73
  35. package/examples/jsm/exporters/USDZExporter.js +353 -99
  36. package/examples/jsm/generators/CityGenerator.js +346 -0
  37. package/examples/jsm/generators/ForestGenerator.js +347 -0
  38. package/examples/jsm/generators/TerrainGenerator.js +504 -0
  39. package/examples/jsm/generators/TreeGenerator.js +377 -0
  40. package/examples/jsm/generators/city/SidewalkGenerator.js +253 -0
  41. package/examples/jsm/generators/city/SkyscraperGenerator.js +1357 -0
  42. package/examples/jsm/geometries/DecalGeometry.js +1 -1
  43. package/examples/jsm/geometries/LoftGeometry.js +355 -0
  44. package/examples/jsm/geometries/TextGeometry.js +18 -0
  45. package/examples/jsm/helpers/AnimationPathHelper.js +302 -0
  46. package/examples/jsm/helpers/LightProbeGridHelper.js +221 -0
  47. package/examples/jsm/helpers/LightProbeHelper.js +5 -4
  48. package/examples/jsm/helpers/LightProbeHelperGPU.js +1 -1
  49. package/examples/jsm/helpers/TextureHelperGPU.js +1 -1
  50. package/examples/jsm/helpers/ViewHelper.js +83 -14
  51. package/examples/jsm/inspector/Extension.js +13 -0
  52. package/examples/jsm/inspector/Inspector.js +244 -91
  53. package/examples/jsm/inspector/RendererInspector.js +165 -39
  54. package/examples/jsm/inspector/extensions/tsl-graph/TSLGraphEditor.js +916 -0
  55. package/examples/jsm/inspector/extensions/tsl-graph/TSLGraphLoader.js +281 -0
  56. package/examples/jsm/inspector/tabs/Console.js +241 -25
  57. package/examples/jsm/inspector/tabs/Memory.js +136 -0
  58. package/examples/jsm/inspector/tabs/Parameters.js +115 -7
  59. package/examples/jsm/inspector/tabs/Performance.js +24 -9
  60. package/examples/jsm/inspector/tabs/Settings.js +306 -0
  61. package/examples/jsm/inspector/tabs/Timeline.js +1696 -0
  62. package/examples/jsm/inspector/tabs/Viewer.js +723 -9
  63. package/examples/jsm/inspector/ui/Graph.js +162 -25
  64. package/examples/jsm/inspector/ui/Item.js +92 -13
  65. package/examples/jsm/inspector/ui/List.js +2 -2
  66. package/examples/jsm/inspector/ui/Profiler.js +1991 -44
  67. package/examples/jsm/inspector/ui/Style.js +1943 -543
  68. package/examples/jsm/inspector/ui/Tab.js +226 -5
  69. package/examples/jsm/inspector/ui/Values.js +126 -8
  70. package/examples/jsm/inspector/ui/utils.js +143 -10
  71. package/examples/jsm/interaction/InteractionManager.js +226 -0
  72. package/examples/jsm/libs/basis/README.md +0 -1
  73. package/examples/jsm/libs/demuxer_mp4.js +3 -3
  74. package/examples/jsm/libs/draco/README.md +0 -1
  75. package/examples/jsm/libs/meshopt_clusterizer.module.js +421 -0
  76. package/examples/jsm/libs/meshopt_decoder.module.js +9 -8
  77. package/examples/jsm/libs/meshopt_simplifier.module.js +627 -0
  78. package/examples/jsm/libs/mikktspace.module.js +34 -9
  79. package/examples/jsm/lighting/ClusteredLighting.js +55 -0
  80. package/examples/jsm/lighting/DynamicLighting.js +82 -0
  81. package/examples/jsm/lighting/LightProbeGrid.js +681 -0
  82. package/examples/jsm/lines/LineMaterial.js +45 -16
  83. package/examples/jsm/lines/LineSegments2.js +8 -0
  84. package/examples/jsm/lines/webgpu/LineSegments2.js +8 -0
  85. package/examples/jsm/loaders/3DMLoader.js +6 -5
  86. package/examples/jsm/loaders/3MFLoader.js +2 -2
  87. package/examples/jsm/loaders/AMFLoader.js +2 -2
  88. package/examples/jsm/loaders/ColladaLoader.js +24 -4026
  89. package/examples/jsm/loaders/DRACOLoader.js +51 -18
  90. package/examples/jsm/loaders/EXRLoader.js +701 -90
  91. package/examples/jsm/loaders/FBXLoader.js +235 -37
  92. package/examples/jsm/loaders/GCodeLoader.js +34 -8
  93. package/examples/jsm/loaders/GLTFLoader.js +146 -178
  94. package/examples/jsm/loaders/HDRLoader.js +7 -30
  95. package/examples/jsm/loaders/KMZLoader.js +5 -5
  96. package/examples/jsm/loaders/KTX2Loader.js +58 -17
  97. package/examples/jsm/loaders/LDrawLoader.js +49 -58
  98. package/examples/jsm/loaders/LUT3dlLoader.js +2 -2
  99. package/examples/jsm/loaders/LUTCubeLoader.js +2 -2
  100. package/examples/jsm/loaders/LWOLoader.js +11 -39
  101. package/examples/jsm/loaders/LottieLoader.js +1 -1
  102. package/examples/jsm/loaders/MaterialXLoader.js +31 -9
  103. package/examples/jsm/loaders/NRRDLoader.js +5 -5
  104. package/examples/jsm/loaders/PCDLoader.js +11 -9
  105. package/examples/jsm/loaders/PLYLoader.js +218 -55
  106. package/examples/jsm/loaders/SVGLoader.js +549 -497
  107. package/examples/jsm/loaders/TDSLoader.js +0 -2
  108. package/examples/jsm/loaders/TGALoader.js +0 -2
  109. package/examples/jsm/loaders/TTFLoader.js +1 -1
  110. package/examples/jsm/loaders/USDLoader.js +127 -43
  111. package/examples/jsm/loaders/UltraHDRLoader.js +285 -160
  112. package/examples/jsm/loaders/VOXLoader.js +660 -117
  113. package/examples/jsm/loaders/VRMLLoader.js +80 -3
  114. package/examples/jsm/loaders/VTKLoader.js +42 -25
  115. package/examples/jsm/loaders/collada/ColladaComposer.js +3044 -0
  116. package/examples/jsm/loaders/collada/ColladaParser.js +1977 -0
  117. package/examples/jsm/loaders/usd/USDAParser.js +504 -344
  118. package/examples/jsm/loaders/usd/USDCParser.js +1867 -6
  119. package/examples/jsm/loaders/usd/USDComposer.js +4627 -0
  120. package/examples/jsm/materials/LDrawConditionalLineNodeMaterial.js +2 -2
  121. package/examples/jsm/materials/WoodNodeMaterial.js +11 -11
  122. package/examples/jsm/math/Octree.js +131 -1
  123. package/examples/jsm/misc/GPUComputationRenderer.js +2 -0
  124. package/examples/jsm/misc/RollerCoaster.js +42 -4
  125. package/examples/jsm/misc/TileCreasedNormalsPlugin.js +270 -0
  126. package/examples/jsm/misc/Volume.js +2 -3
  127. package/examples/jsm/misc/VolumeSlice.js +0 -1
  128. package/examples/jsm/modifiers/TessellateModifier.js +1 -1
  129. package/examples/jsm/objects/GroundedSkybox.js +1 -1
  130. package/examples/jsm/objects/LensflareMesh.js +1 -1
  131. package/examples/jsm/objects/Reflector.js +72 -25
  132. package/examples/jsm/objects/Sky.js +90 -6
  133. package/examples/jsm/objects/SkyMesh.js +150 -16
  134. package/examples/jsm/objects/Water.js +4 -3
  135. package/examples/jsm/objects/Water2.js +5 -3
  136. package/examples/jsm/objects/Water2Mesh.js +1 -1
  137. package/examples/jsm/objects/WaterMesh.js +5 -7
  138. package/examples/jsm/physics/AmmoPhysics.js +27 -15
  139. package/examples/jsm/physics/JoltPhysics.js +10 -6
  140. package/examples/jsm/physics/RapierPhysics.js +36 -7
  141. package/examples/jsm/postprocessing/EffectComposer.js +7 -5
  142. package/examples/jsm/postprocessing/OutputPass.js +9 -0
  143. package/examples/jsm/postprocessing/RenderPass.js +10 -0
  144. package/examples/jsm/postprocessing/RenderTransitionPass.js +1 -1
  145. package/examples/jsm/postprocessing/UnrealBloomPass.js +48 -18
  146. package/examples/jsm/renderers/CSS3DRenderer.js +1 -1
  147. package/examples/jsm/renderers/Projector.js +246 -28
  148. package/examples/jsm/renderers/SVGRenderer.js +174 -60
  149. package/examples/jsm/shaders/GTAOShader.js +19 -6
  150. package/examples/jsm/shaders/HalftoneShader.js +12 -1
  151. package/examples/jsm/shaders/PoissonDenoiseShader.js +6 -2
  152. package/examples/jsm/shaders/SAOShader.js +17 -4
  153. package/examples/jsm/shaders/SSAOShader.js +11 -1
  154. package/examples/jsm/shaders/SSRShader.js +6 -5
  155. package/examples/jsm/shaders/UnpackDepthRGBAShader.js +2 -4
  156. package/examples/jsm/shaders/VignetteShader.js +1 -1
  157. package/examples/jsm/shaders/VolumeShader.js +31 -44
  158. package/examples/jsm/transpiler/AST.js +44 -0
  159. package/examples/jsm/transpiler/GLSLDecoder.js +65 -8
  160. package/examples/jsm/transpiler/Linker.js +1 -1
  161. package/examples/jsm/transpiler/ShaderToyDecoder.js +2 -0
  162. package/examples/jsm/transpiler/TSLEncoder.js +46 -3
  163. package/examples/jsm/transpiler/TranspilerUtils.js +3 -3
  164. package/examples/jsm/transpiler/WGSLEncoder.js +27 -0
  165. package/examples/jsm/tsl/WebGLNodesHandler.js +605 -0
  166. package/examples/jsm/tsl/display/AfterImageNode.js +11 -1
  167. package/examples/jsm/tsl/display/AnaglyphPassNode.js +458 -16
  168. package/examples/jsm/tsl/display/BilateralBlurNode.js +374 -0
  169. package/examples/jsm/tsl/display/BloomNode.js +75 -25
  170. package/examples/jsm/tsl/display/CRT.js +150 -0
  171. package/examples/jsm/tsl/display/ChromaticAberrationNode.js +3 -36
  172. package/examples/jsm/tsl/display/DenoiseNode.js +1 -1
  173. package/examples/jsm/tsl/display/DepthOfFieldNode.js +3 -3
  174. package/examples/jsm/tsl/display/DotScreenNode.js +1 -1
  175. package/examples/jsm/tsl/display/FSR1Node.js +477 -0
  176. package/examples/jsm/tsl/display/FXAANode.js +13 -14
  177. package/examples/jsm/tsl/display/GTAONode.js +40 -17
  178. package/examples/jsm/tsl/display/GaussianBlurNode.js +21 -2
  179. package/examples/jsm/tsl/display/GodraysNode.js +615 -0
  180. package/examples/jsm/tsl/display/ImportanceSampledEnvironment.js +560 -0
  181. package/examples/jsm/tsl/display/LensflareNode.js +1 -1
  182. package/examples/jsm/tsl/display/Lut3DNode.js +1 -1
  183. package/examples/jsm/tsl/display/OutlineNode.js +69 -19
  184. package/examples/jsm/tsl/display/ParallaxBarrierPassNode.js +2 -2
  185. package/examples/jsm/tsl/display/PixelationPassNode.js +9 -8
  186. package/examples/jsm/tsl/display/RGBShiftNode.js +2 -2
  187. package/examples/jsm/tsl/display/RecurrentDenoiseNode.js +912 -0
  188. package/examples/jsm/tsl/display/RetroPassNode.js +263 -0
  189. package/examples/jsm/tsl/display/SMAANode.js +2 -2
  190. package/examples/jsm/tsl/display/SSAAPassNode.js +15 -27
  191. package/examples/jsm/tsl/display/SSGINode.js +96 -62
  192. package/examples/jsm/tsl/display/SSRNode.js +831 -134
  193. package/examples/jsm/tsl/display/SSSNode.js +8 -6
  194. package/examples/jsm/tsl/display/Shape.js +29 -0
  195. package/examples/jsm/tsl/display/SharpenNode.js +283 -0
  196. package/examples/jsm/tsl/display/SobelOperatorNode.js +2 -2
  197. package/examples/jsm/tsl/display/StereoCompositePassNode.js +8 -1
  198. package/examples/jsm/tsl/display/StereoPassNode.js +1 -2
  199. package/examples/jsm/tsl/display/TAAUNode.js +835 -0
  200. package/examples/jsm/tsl/display/TRAANode.js +315 -126
  201. package/examples/jsm/tsl/display/TemporalReprojectNode.js +1023 -0
  202. package/examples/jsm/tsl/display/TransitionNode.js +1 -1
  203. package/examples/jsm/tsl/display/depthAwareBlend.js +80 -0
  204. package/examples/jsm/tsl/display/radialBlur.js +68 -0
  205. package/examples/jsm/tsl/lighting/ClusteredLightsNode.js +622 -0
  206. package/examples/jsm/tsl/lighting/DynamicLightsNode.js +300 -0
  207. package/examples/jsm/tsl/lighting/data/AmbientLightDataNode.js +61 -0
  208. package/examples/jsm/tsl/lighting/data/DirectionalLightDataNode.js +111 -0
  209. package/examples/jsm/tsl/lighting/data/HemisphereLightDataNode.js +99 -0
  210. package/examples/jsm/tsl/lighting/data/PointLightDataNode.js +134 -0
  211. package/examples/jsm/tsl/lighting/data/SpotLightDataNode.js +161 -0
  212. package/examples/jsm/tsl/math/Bayer.js +53 -3
  213. package/examples/jsm/tsl/math/curlNoise.js +107 -0
  214. package/examples/jsm/tsl/shadows/TileShadowNode.js +1 -1
  215. package/examples/jsm/tsl/shadows/TileShadowNodeHelper.js +3 -3
  216. package/examples/jsm/tsl/utils/GroundedSkybox.js +62 -0
  217. package/examples/jsm/tsl/utils/RNoise.js +51 -0
  218. package/examples/jsm/tsl/utils/SpecularHelpers.js +325 -0
  219. package/examples/jsm/utils/BufferGeometryUtils.js +128 -62
  220. package/examples/jsm/utils/ColorUtils.js +76 -0
  221. package/examples/jsm/utils/GeometryCompressionUtils.js +1 -1
  222. package/examples/jsm/utils/LDrawUtils.js +1 -1
  223. package/examples/jsm/utils/SceneOptimizer.js +1 -1
  224. package/examples/jsm/utils/ShadowMapViewer.js +24 -10
  225. package/examples/jsm/utils/ShadowMapViewerGPU.js +1 -1
  226. package/examples/jsm/utils/SkeletonUtils.js +14 -8
  227. package/examples/jsm/utils/WebGPUTextureUtils.js +1 -1
  228. package/examples/jsm/webxr/ARButton.js +1 -1
  229. package/examples/jsm/webxr/WebGLXRFallback.js +89 -0
  230. package/examples/jsm/webxr/XRControllerModelFactory.js +2 -2
  231. package/examples/jsm/webxr/XRHandMeshModel.js +36 -10
  232. package/examples/jsm/webxr/XRHandModelFactory.js +2 -1
  233. package/package.json +28 -34
  234. package/src/Three.Core.js +3 -1
  235. package/src/Three.TSL.js +35 -14
  236. package/src/Three.WebGPU.Nodes.js +5 -0
  237. package/src/Three.WebGPU.js +13 -0
  238. package/src/Three.js +1 -0
  239. package/src/animation/AnimationAction.js +18 -4
  240. package/src/animation/AnimationClip.js +0 -139
  241. package/src/animation/AnimationMixer.js +6 -0
  242. package/src/animation/AnimationUtils.js +1 -12
  243. package/src/animation/KeyframeTrack.js +47 -8
  244. package/src/animation/PropertyBinding.js +2 -2
  245. package/src/animation/PropertyMixer.js +4 -4
  246. package/src/animation/tracks/BooleanKeyframeTrack.js +1 -1
  247. package/src/animation/tracks/ColorKeyframeTrack.js +1 -1
  248. package/src/animation/tracks/NumberKeyframeTrack.js +1 -1
  249. package/src/animation/tracks/QuaternionKeyframeTrack.js +1 -1
  250. package/src/animation/tracks/StringKeyframeTrack.js +1 -1
  251. package/src/animation/tracks/VectorKeyframeTrack.js +1 -1
  252. package/src/audio/Audio.js +1 -1
  253. package/src/audio/AudioContext.js +2 -2
  254. package/src/audio/AudioListener.js +5 -3
  255. package/src/cameras/Camera.js +34 -4
  256. package/src/cameras/CubeCamera.js +20 -0
  257. package/src/cameras/StereoCamera.js +5 -2
  258. package/src/constants.js +90 -5
  259. package/src/core/BufferAttribute.js +13 -1
  260. package/src/core/BufferGeometry.js +43 -9
  261. package/src/core/Clock.js +7 -0
  262. package/src/core/Object3D.js +69 -11
  263. package/src/core/Raycaster.js +3 -3
  264. package/src/core/RenderTarget.js +19 -6
  265. package/src/extras/PMREMGenerator.js +13 -22
  266. package/src/extras/TextureUtils.js +6 -2
  267. package/src/extras/core/ShapePath.js +149 -160
  268. package/src/extras/curves/CatmullRomCurve3.js +3 -2
  269. package/src/geometries/ExtrudeGeometry.js +2 -2
  270. package/src/geometries/PolyhedronGeometry.js +1 -1
  271. package/src/geometries/SphereGeometry.js +8 -3
  272. package/src/geometries/TorusGeometry.js +8 -3
  273. package/src/helpers/CameraHelper.js +3 -0
  274. package/src/helpers/DirectionalLightHelper.js +6 -1
  275. package/src/helpers/HemisphereLightHelper.js +5 -0
  276. package/src/helpers/PointLightHelper.js +3 -25
  277. package/src/helpers/SpotLightHelper.js +4 -1
  278. package/src/lights/DirectionalLight.js +13 -0
  279. package/src/lights/HemisphereLight.js +10 -0
  280. package/src/lights/Light.js +1 -11
  281. package/src/lights/LightProbe.js +0 -15
  282. package/src/lights/LightShadow.js +15 -6
  283. package/src/lights/PointLight.js +15 -0
  284. package/src/lights/PointLightShadow.js +0 -86
  285. package/src/lights/SpotLight.js +22 -1
  286. package/src/lights/webgpu/IESSpotLight.js +2 -1
  287. package/src/loaders/AudioLoader.js +11 -1
  288. package/src/loaders/Cache.js +28 -0
  289. package/src/loaders/DataTextureLoader.js +75 -42
  290. package/src/loaders/FileLoader.js +2 -3
  291. package/src/loaders/ImageBitmapLoader.js +12 -9
  292. package/src/loaders/Loader.js +6 -0
  293. package/src/loaders/LoadingManager.js +5 -0
  294. package/src/loaders/MaterialLoader.js +37 -266
  295. package/src/loaders/ObjectLoader.js +48 -6
  296. package/src/loaders/nodes/NodeLoader.js +2 -2
  297. package/src/loaders/nodes/NodeObjectLoader.js +18 -0
  298. package/src/materials/LineBasicMaterial.js +4 -0
  299. package/src/materials/Material.js +198 -1
  300. package/src/materials/MeshBasicMaterial.js +24 -0
  301. package/src/materials/MeshDepthMaterial.js +10 -0
  302. package/src/materials/MeshDistanceMaterial.js +10 -0
  303. package/src/materials/MeshLambertMaterial.js +49 -1
  304. package/src/materials/MeshMatcapMaterial.js +25 -1
  305. package/src/materials/MeshNormalMaterial.js +9 -1
  306. package/src/materials/MeshPhongMaterial.js +49 -1
  307. package/src/materials/MeshPhysicalMaterial.js +38 -0
  308. package/src/materials/MeshStandardMaterial.js +42 -1
  309. package/src/materials/MeshToonMaterial.js +35 -2
  310. package/src/materials/PointsMaterial.js +7 -0
  311. package/src/materials/ShaderMaterial.js +106 -1
  312. package/src/materials/SpriteMaterial.js +7 -0
  313. package/src/materials/nodes/Line2NodeMaterial.js +380 -297
  314. package/src/materials/nodes/MeshBasicNodeMaterial.js +2 -2
  315. package/src/materials/nodes/MeshNormalNodeMaterial.js +2 -2
  316. package/src/materials/nodes/MeshPhongNodeMaterial.js +0 -9
  317. package/src/materials/nodes/MeshPhysicalNodeMaterial.js +3 -30
  318. package/src/materials/nodes/MeshSSSNodeMaterial.js +0 -13
  319. package/src/materials/nodes/MeshStandardNodeMaterial.js +5 -15
  320. package/src/materials/nodes/NodeMaterial.js +153 -89
  321. package/src/materials/nodes/SpriteNodeMaterial.js +0 -10
  322. package/src/materials/nodes/manager/NodeMaterialObserver.js +234 -100
  323. package/src/math/Box3.js +5 -0
  324. package/src/math/FrustumArray.js +103 -133
  325. package/src/math/Interpolant.js +1 -1
  326. package/src/math/Line3.js +6 -5
  327. package/src/math/MathUtils.js +12 -12
  328. package/src/math/Matrix2.js +13 -9
  329. package/src/math/Matrix3.js +24 -9
  330. package/src/math/Matrix4.js +112 -74
  331. package/src/math/Plane.js +4 -3
  332. package/src/math/Quaternion.js +3 -29
  333. package/src/math/Sphere.js +1 -1
  334. package/src/math/Triangle.js +1 -1
  335. package/src/math/Vector2.js +13 -9
  336. package/src/math/Vector3.js +17 -15
  337. package/src/math/Vector4.js +15 -11
  338. package/src/math/interpolants/BezierInterpolant.js +106 -0
  339. package/src/nodes/Nodes.js +86 -71
  340. package/src/nodes/TSL.js +15 -13
  341. package/src/nodes/accessors/Arrays.js +5 -5
  342. package/src/nodes/accessors/Batch.js +108 -0
  343. package/src/nodes/accessors/Bitangent.js +7 -7
  344. package/src/nodes/accessors/BufferAttributeNode.js +120 -17
  345. package/src/nodes/accessors/BufferNode.js +29 -2
  346. package/src/nodes/accessors/Camera.js +149 -28
  347. package/src/nodes/accessors/ClippingNode.js +5 -5
  348. package/src/nodes/accessors/CubeTextureNode.js +27 -2
  349. package/src/nodes/accessors/Instance.js +264 -0
  350. package/src/nodes/accessors/MaterialNode.js +27 -3
  351. package/src/nodes/accessors/MaterialProperties.js +6 -8
  352. package/src/nodes/accessors/MaterialReferenceNode.js +1 -2
  353. package/src/nodes/accessors/ModelNode.js +1 -1
  354. package/src/nodes/accessors/{MorphNode.js → Morph.js} +99 -112
  355. package/src/nodes/accessors/Normal.js +24 -24
  356. package/src/nodes/accessors/Object3DNode.js +1 -1
  357. package/src/nodes/accessors/Position.js +39 -3
  358. package/src/nodes/accessors/ReferenceBaseNode.js +6 -6
  359. package/src/nodes/accessors/ReferenceNode.js +9 -8
  360. package/src/nodes/accessors/ReflectVector.js +3 -3
  361. package/src/nodes/accessors/RendererReferenceNode.js +1 -2
  362. package/src/nodes/accessors/SceneProperties.js +47 -0
  363. package/src/nodes/accessors/Skinning.js +263 -0
  364. package/src/nodes/accessors/StorageBufferNode.js +16 -27
  365. package/src/nodes/accessors/StorageTexture3DNode.js +100 -0
  366. package/src/nodes/accessors/StorageTextureNode.js +74 -11
  367. package/src/nodes/accessors/Tangent.js +8 -18
  368. package/src/nodes/accessors/Texture3DNode.js +32 -35
  369. package/src/nodes/accessors/TextureNode.js +124 -25
  370. package/src/nodes/accessors/UniformArrayNode.js +6 -4
  371. package/src/nodes/accessors/UserDataNode.js +1 -2
  372. package/src/nodes/accessors/VertexColorNode.js +1 -2
  373. package/src/nodes/code/FunctionCallNode.js +1 -1
  374. package/src/nodes/code/FunctionNode.js +4 -19
  375. package/src/nodes/core/ArrayNode.js +21 -2
  376. package/src/nodes/core/AssignNode.js +3 -3
  377. package/src/nodes/core/AttributeNode.js +3 -3
  378. package/src/nodes/core/BypassNode.js +1 -1
  379. package/src/nodes/core/ContextNode.js +104 -5
  380. package/src/nodes/core/IndexNode.js +2 -1
  381. package/src/nodes/core/InputNode.js +1 -1
  382. package/src/nodes/core/InspectorNode.js +1 -1
  383. package/src/nodes/core/IsolateNode.js +1 -1
  384. package/src/nodes/core/MRTNode.js +55 -4
  385. package/src/nodes/core/Node.js +151 -17
  386. package/src/nodes/core/NodeBuilder.js +479 -101
  387. package/src/nodes/core/NodeError.js +28 -0
  388. package/src/nodes/core/NodeFrame.js +12 -4
  389. package/src/nodes/core/NodeUtils.js +20 -18
  390. package/src/nodes/core/OutputStructNode.js +13 -11
  391. package/src/nodes/core/OverrideContextNode.js +153 -0
  392. package/src/nodes/core/ParameterNode.js +4 -4
  393. package/src/nodes/core/PropertyNode.js +66 -4
  394. package/src/nodes/core/StackNode.js +91 -20
  395. package/src/nodes/core/StackTrace.js +139 -0
  396. package/src/nodes/core/StructNode.js +20 -8
  397. package/src/nodes/core/StructTypeNode.js +17 -23
  398. package/src/nodes/core/SubBuildNode.js +2 -2
  399. package/src/nodes/core/UniformGroupNode.js +36 -6
  400. package/src/nodes/core/UniformNode.js +21 -5
  401. package/src/nodes/core/VarNode.js +56 -23
  402. package/src/nodes/core/VaryingNode.js +14 -21
  403. package/src/nodes/display/BlendModes.js +1 -105
  404. package/src/nodes/display/BumpMapNode.js +6 -1
  405. package/src/nodes/display/ColorAdjustment.js +27 -9
  406. package/src/nodes/display/ColorSpaceNode.js +3 -3
  407. package/src/nodes/display/FrontFacingNode.js +38 -9
  408. package/src/nodes/display/NormalMapNode.js +41 -6
  409. package/src/nodes/display/PassNode.js +157 -56
  410. package/src/nodes/display/PremultiplyAlphaFunctions.js +39 -0
  411. package/src/nodes/display/RenderOutputNode.js +19 -10
  412. package/src/nodes/display/ScreenNode.js +4 -2
  413. package/src/nodes/display/ToneMappingNode.js +1 -1
  414. package/src/nodes/display/ToonOutlinePassNode.js +2 -2
  415. package/src/nodes/display/ViewportDepthNode.js +52 -4
  416. package/src/nodes/display/ViewportDepthTextureNode.js +11 -15
  417. package/src/nodes/display/ViewportTextureNode.js +39 -11
  418. package/src/nodes/fog/Fog.js +18 -35
  419. package/src/nodes/functions/BSDF/BRDF_GGX_Multiscatter.js +3 -3
  420. package/src/nodes/functions/BSDF/DFGLUT.js +56 -0
  421. package/src/nodes/functions/BSDF/EnvironmentBRDF.js +2 -2
  422. package/src/nodes/functions/BSDF/V_GGX_SmithCorrelated_Anisotropic.js +2 -2
  423. package/src/nodes/functions/PhysicalLightingModel.js +126 -45
  424. package/src/nodes/functions/VolumetricLightingModel.js +40 -7
  425. package/src/nodes/geometry/RangeNode.js +5 -3
  426. package/src/nodes/gpgpu/AtomicFunctionNode.js +1 -1
  427. package/src/nodes/gpgpu/BarrierNode.js +9 -0
  428. package/src/nodes/gpgpu/ComputeBuiltinNode.js +2 -3
  429. package/src/nodes/gpgpu/ComputeNode.js +74 -48
  430. package/src/nodes/gpgpu/SubgroupFunctionNode.js +2 -2
  431. package/src/nodes/gpgpu/WorkgroupInfoNode.js +5 -5
  432. package/src/nodes/lighting/AnalyticLightNode.js +53 -0
  433. package/src/nodes/lighting/EnvironmentNode.js +30 -5
  434. package/src/nodes/lighting/IESSpotLightNode.js +40 -2
  435. package/src/nodes/lighting/LightingContextNode.js +10 -2
  436. package/src/nodes/lighting/LightsNode.js +97 -65
  437. package/src/nodes/lighting/PointShadowNode.js +166 -148
  438. package/src/nodes/lighting/ShadowFilterNode.js +74 -117
  439. package/src/nodes/lighting/ShadowNode.js +149 -51
  440. package/src/nodes/materialx/lib/mx_noise.js +2 -2
  441. package/src/nodes/math/BitcastNode.js +1 -1
  442. package/src/nodes/math/BitcountNode.js +433 -0
  443. package/src/nodes/math/ConditionalNode.js +6 -6
  444. package/src/nodes/math/MathNode.js +115 -52
  445. package/src/nodes/math/OperatorNode.js +24 -23
  446. package/src/nodes/math/PackFloatNode.js +98 -0
  447. package/src/nodes/math/UnpackFloatNode.js +96 -0
  448. package/src/nodes/parsers/GLSLNodeFunction.js +1 -1
  449. package/src/nodes/pmrem/PMREMNode.js +34 -3
  450. package/src/nodes/pmrem/PMREMUtils.js +9 -15
  451. package/src/nodes/tsl/TSLBase.js +1 -1
  452. package/src/nodes/tsl/TSLCore.js +99 -33
  453. package/src/nodes/utils/ArrayElementNode.js +14 -1
  454. package/src/nodes/utils/ConvertNode.js +1 -1
  455. package/src/nodes/utils/DebugNode.js +12 -12
  456. package/src/nodes/utils/EquirectUV.js +30 -5
  457. package/src/nodes/utils/EventNode.js +31 -2
  458. package/src/nodes/utils/FlipNode.js +1 -1
  459. package/src/nodes/utils/FunctionOverloadingNode.js +2 -2
  460. package/src/nodes/utils/JoinNode.js +3 -3
  461. package/src/nodes/utils/LoopNode.js +1 -1
  462. package/src/nodes/utils/MemberNode.js +2 -2
  463. package/src/nodes/utils/Packing.js +48 -5
  464. package/src/nodes/utils/PostProcessingUtils.js +33 -1
  465. package/src/nodes/utils/RTTNode.js +41 -25
  466. package/src/nodes/utils/ReflectorNode.js +4 -4
  467. package/src/nodes/utils/Remap.js +48 -0
  468. package/src/nodes/utils/RotateNode.js +1 -1
  469. package/src/nodes/utils/SampleNode.js +1 -1
  470. package/src/nodes/utils/SetNode.js +1 -1
  471. package/src/nodes/utils/SplitNode.js +1 -1
  472. package/src/nodes/utils/SpriteSheetUV.js +35 -0
  473. package/src/nodes/utils/StorageArrayElementNode.js +1 -1
  474. package/src/nodes/utils/UVUtils.js +28 -0
  475. package/src/objects/BatchedMesh.js +66 -30
  476. package/src/objects/InstancedMesh.js +19 -3
  477. package/src/objects/Line.js +1 -1
  478. package/src/objects/Mesh.js +1 -1
  479. package/src/objects/Points.js +1 -1
  480. package/src/objects/SkinnedMesh.js +26 -9
  481. package/src/renderers/WebGLRenderer.js +344 -156
  482. package/src/renderers/common/Animation.js +3 -3
  483. package/src/renderers/common/Attributes.js +15 -1
  484. package/src/renderers/common/Backend.js +78 -13
  485. package/src/renderers/common/Background.js +26 -13
  486. package/src/renderers/common/BindGroup.js +1 -16
  487. package/src/renderers/common/Binding.js +11 -0
  488. package/src/renderers/common/Bindings.js +141 -57
  489. package/src/renderers/common/BlendMode.js +143 -0
  490. package/src/renderers/common/Buffer.js +49 -0
  491. package/src/renderers/common/BundleGroup.js +1 -1
  492. package/src/renderers/common/ChainMap.js +30 -6
  493. package/src/renderers/common/ClippingContext.js +10 -2
  494. package/src/renderers/common/ComputePipeline.js +1 -1
  495. package/src/renderers/common/CubeRenderTarget.js +51 -7
  496. package/src/renderers/common/Geometries.js +29 -3
  497. package/src/renderers/common/Info.js +374 -4
  498. package/src/renderers/common/InspectorBase.js +6 -1
  499. package/src/renderers/common/Lighting.js +51 -19
  500. package/src/renderers/common/Pipelines.js +40 -7
  501. package/src/renderers/common/PostProcessing.js +8 -206
  502. package/src/renderers/common/ReadbackBuffer.js +78 -0
  503. package/src/renderers/common/RenderBundle.js +3 -1
  504. package/src/renderers/common/RenderBundles.js +7 -3
  505. package/src/renderers/common/RenderContext.js +16 -0
  506. package/src/renderers/common/RenderContexts.js +33 -49
  507. package/src/renderers/common/RenderList.js +41 -4
  508. package/src/renderers/common/RenderLists.js +2 -1
  509. package/src/renderers/common/RenderObject.js +88 -16
  510. package/src/renderers/common/RenderObjectPipeline.js +40 -0
  511. package/src/renderers/common/RenderObjects.js +21 -5
  512. package/src/renderers/common/RenderPipeline.js +232 -17
  513. package/src/renderers/common/Renderer.js +700 -163
  514. package/src/renderers/common/Sampler.js +16 -48
  515. package/src/renderers/common/StorageBuffer.js +13 -1
  516. package/src/renderers/common/Textures.js +121 -9
  517. package/src/renderers/common/TimestampQueryPool.js +6 -4
  518. package/src/renderers/common/Uniform.js +8 -0
  519. package/src/renderers/common/UniformsGroup.js +84 -1
  520. package/src/renderers/common/XRManager.js +377 -51
  521. package/src/renderers/common/extras/PMREMGenerator.js +40 -61
  522. package/src/renderers/common/nodes/NodeBuilderState.js +10 -2
  523. package/src/renderers/common/nodes/NodeLibrary.js +4 -4
  524. package/src/renderers/common/nodes/{Nodes.js → NodeManager.js} +298 -106
  525. package/src/renderers/common/nodes/NodeStorageBuffer.js +13 -2
  526. package/src/renderers/common/nodes/NodeUniformBuffer.js +65 -0
  527. package/src/renderers/shaders/DFGLUTData.js +19 -34
  528. package/src/renderers/shaders/ShaderChunk/batching_pars_vertex.glsl.js +2 -2
  529. package/src/renderers/shaders/ShaderChunk/color_fragment.glsl.js +1 -5
  530. package/src/renderers/shaders/ShaderChunk/color_pars_fragment.glsl.js +1 -5
  531. package/src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl.js +1 -5
  532. package/src/renderers/shaders/ShaderChunk/color_vertex.glsl.js +8 -10
  533. package/src/renderers/shaders/ShaderChunk/common.glsl.js +13 -4
  534. package/src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl.js +0 -6
  535. package/src/renderers/shaders/ShaderChunk/envmap_common_pars_fragment.glsl.js +0 -1
  536. package/src/renderers/shaders/ShaderChunk/envmap_fragment.glsl.js +9 -13
  537. package/src/renderers/shaders/ShaderChunk/envmap_physical_pars_fragment.glsl.js +2 -2
  538. package/src/renderers/shaders/ShaderChunk/envmap_vertex.glsl.js +1 -1
  539. package/src/renderers/shaders/ShaderChunk/lightprobes_pars_fragment.glsl.js +80 -0
  540. package/src/renderers/shaders/ShaderChunk/lights_fragment_begin.glsl.js +13 -2
  541. package/src/renderers/shaders/ShaderChunk/lights_fragment_end.glsl.js +6 -0
  542. package/src/renderers/shaders/ShaderChunk/lights_fragment_maps.glsl.js +6 -2
  543. package/src/renderers/shaders/ShaderChunk/lights_pars_begin.glsl.js +3 -1
  544. package/src/renderers/shaders/ShaderChunk/lights_physical_fragment.glsl.js +8 -4
  545. package/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js +112 -53
  546. package/src/renderers/shaders/ShaderChunk/normal_fragment_begin.glsl.js +2 -2
  547. package/src/renderers/shaders/ShaderChunk/normal_fragment_maps.glsl.js +7 -0
  548. package/src/renderers/shaders/ShaderChunk/normal_vertex.glsl.js +6 -0
  549. package/src/renderers/shaders/ShaderChunk/packing.glsl.js +20 -4
  550. package/src/renderers/shaders/ShaderChunk/premultiplied_alpha_fragment.glsl.js +0 -1
  551. package/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl.js +225 -186
  552. package/src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl.js +12 -2
  553. package/src/renderers/shaders/ShaderChunk/shadowmask_pars_fragment.glsl.js +1 -1
  554. package/src/renderers/shaders/ShaderChunk/transmission_fragment.glsl.js +2 -2
  555. package/src/renderers/shaders/ShaderChunk.js +5 -3
  556. package/src/renderers/shaders/ShaderLib/backgroundCube.glsl.js +1 -2
  557. package/src/renderers/shaders/ShaderLib/depth.glsl.js +3 -0
  558. package/src/renderers/shaders/ShaderLib/{distanceRGBA.glsl.js → distance.glsl.js} +2 -3
  559. package/src/renderers/shaders/ShaderLib/meshlambert.glsl.js +2 -1
  560. package/src/renderers/shaders/ShaderLib/meshnormal.glsl.js +1 -2
  561. package/src/renderers/shaders/ShaderLib/meshphong.glsl.js +2 -1
  562. package/src/renderers/shaders/ShaderLib/meshphysical.glsl.js +4 -9
  563. package/src/renderers/shaders/ShaderLib/meshtoon.glsl.js +0 -1
  564. package/src/renderers/shaders/ShaderLib/shadow.glsl.js +1 -1
  565. package/src/renderers/shaders/ShaderLib/vsm.glsl.js +4 -6
  566. package/src/renderers/shaders/ShaderLib.js +7 -6
  567. package/src/renderers/shaders/UniformsLib.js +7 -5
  568. package/src/renderers/shaders/UniformsUtils.js +27 -5
  569. package/src/renderers/webgl/WebGLAnimation.js +2 -1
  570. package/src/renderers/webgl/WebGLBackground.js +15 -15
  571. package/src/renderers/webgl/WebGLBindingStates.js +99 -27
  572. package/src/renderers/webgl/WebGLBufferRenderer.js +0 -32
  573. package/src/renderers/webgl/WebGLCapabilities.js +9 -4
  574. package/src/renderers/webgl/WebGLEnvironments.js +228 -0
  575. package/src/renderers/webgl/WebGLGeometries.js +10 -7
  576. package/src/renderers/webgl/WebGLIndexedBufferRenderer.js +0 -32
  577. package/src/renderers/webgl/WebGLLights.js +18 -1
  578. package/src/renderers/webgl/WebGLMaterials.js +24 -13
  579. package/src/renderers/webgl/WebGLObjects.js +3 -1
  580. package/src/renderers/webgl/WebGLOutput.js +271 -0
  581. package/src/renderers/webgl/WebGLProgram.js +54 -113
  582. package/src/renderers/webgl/WebGLPrograms.js +74 -55
  583. package/src/renderers/webgl/WebGLRenderLists.js +24 -1
  584. package/src/renderers/webgl/WebGLRenderStates.js +13 -2
  585. package/src/renderers/webgl/WebGLShaderCache.js +5 -11
  586. package/src/renderers/webgl/WebGLShadowMap.js +188 -24
  587. package/src/renderers/webgl/WebGLState.js +75 -37
  588. package/src/renderers/webgl/WebGLTextures.js +230 -56
  589. package/src/renderers/webgl/WebGLUniforms.js +40 -3
  590. package/src/renderers/webgl/WebGLUniformsGroups.js +82 -35
  591. package/src/renderers/webgl/WebGLUtils.js +6 -2
  592. package/src/renderers/webgl-fallback/WebGLBackend.js +322 -92
  593. package/src/renderers/webgl-fallback/WebGLBufferRenderer.js +0 -41
  594. package/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js +295 -66
  595. package/src/renderers/webgl-fallback/utils/WebGLAttributeUtils.js +53 -19
  596. package/src/renderers/webgl-fallback/utils/WebGLCapabilities.js +25 -0
  597. package/src/renderers/webgl-fallback/utils/WebGLState.js +223 -6
  598. package/src/renderers/webgl-fallback/utils/WebGLTextureUtils.js +118 -73
  599. package/src/renderers/webgl-fallback/utils/WebGLTimestampQueryPool.js +10 -10
  600. package/src/renderers/webgl-fallback/utils/WebGLUtils.js +9 -3
  601. package/src/renderers/webgpu/WebGPUBackend.js +733 -302
  602. package/src/renderers/webgpu/WebGPURenderer.js +2 -1
  603. package/src/renderers/webgpu/descriptors/GPUBindGroupDescriptor.js +48 -0
  604. package/src/renderers/webgpu/descriptors/GPUBufferDescriptor.js +57 -0
  605. package/src/renderers/webgpu/descriptors/GPUCommandEncoderDescriptor.js +30 -0
  606. package/src/renderers/webgpu/descriptors/GPUComputePassDescriptor.js +38 -0
  607. package/src/renderers/webgpu/descriptors/GPUComputePipelineDescriptor.js +48 -0
  608. package/src/renderers/webgpu/descriptors/GPUCopyExternalImageDestInfo.js +47 -0
  609. package/src/renderers/webgpu/descriptors/GPUCopyExternalImageSourceInfo.js +50 -0
  610. package/src/renderers/webgpu/descriptors/GPUExtent3D.js +51 -0
  611. package/src/renderers/webgpu/descriptors/GPUPipelineLayoutDescriptor.js +39 -0
  612. package/src/renderers/webgpu/descriptors/GPUQuerySetDescriptor.js +47 -0
  613. package/src/renderers/webgpu/descriptors/GPURenderBundleEncoderDescriptor.js +74 -0
  614. package/src/renderers/webgpu/descriptors/GPURenderPassColorAttachment.js +72 -0
  615. package/src/renderers/webgpu/descriptors/GPURenderPassDepthStencilAttachment.js +99 -0
  616. package/src/renderers/webgpu/descriptors/GPURenderPassDescriptor.js +72 -0
  617. package/src/renderers/webgpu/descriptors/GPURenderPassTimestampWrites.js +49 -0
  618. package/src/renderers/webgpu/descriptors/GPURenderPipelineDescriptor.js +130 -0
  619. package/src/renderers/webgpu/descriptors/GPUSamplerDescriptor.js +119 -0
  620. package/src/renderers/webgpu/descriptors/GPUShaderModuleDescriptor.js +46 -0
  621. package/src/renderers/webgpu/descriptors/GPUTexelCopyBufferInfo.js +57 -0
  622. package/src/renderers/webgpu/descriptors/GPUTexelCopyBufferLayout.js +48 -0
  623. package/src/renderers/webgpu/descriptors/GPUTexelCopyTextureInfo.js +61 -0
  624. package/src/renderers/webgpu/descriptors/GPUTextureDescriptor.js +99 -0
  625. package/src/renderers/webgpu/descriptors/GPUTextureViewDescriptor.js +108 -0
  626. package/src/renderers/webgpu/nodes/StandardNodeLibrary.js +0 -1
  627. package/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +526 -93
  628. package/src/renderers/webgpu/nodes/WGSLNodeFunction.js +1 -1
  629. package/src/renderers/webgpu/utils/WebGPUAttributeUtils.js +194 -61
  630. package/src/renderers/webgpu/utils/WebGPUBindingUtils.js +395 -234
  631. package/src/renderers/webgpu/utils/WebGPUCapabilities.js +48 -0
  632. package/src/renderers/webgpu/utils/WebGPUConstants.js +10 -0
  633. package/src/renderers/webgpu/utils/WebGPUPipelineUtils.js +224 -91
  634. package/src/renderers/webgpu/utils/WebGPUTexturePassUtils.js +228 -213
  635. package/src/renderers/webgpu/utils/WebGPUTextureUtils.js +428 -175
  636. package/src/renderers/webgpu/utils/WebGPUTimestampQueryPool.js +40 -25
  637. package/src/renderers/webgpu/utils/WebGPUUtils.js +70 -13
  638. package/src/renderers/webxr/WebXRController.js +12 -0
  639. package/src/renderers/webxr/WebXRManager.js +4 -2
  640. package/src/textures/CubeDepthTexture.js +76 -0
  641. package/src/textures/DepthTexture.js +1 -1
  642. package/src/textures/ExternalTexture.js +0 -3
  643. package/src/textures/HTMLTexture.js +74 -0
  644. package/src/textures/Source.js +2 -2
  645. package/src/textures/Texture.js +16 -5
  646. package/src/utils.js +280 -3
  647. package/examples/fonts/LICENSE +0 -13
  648. package/examples/fonts/MPLUSRounded1c/MPLUSRounded1c-Regular.typeface.json.zip +0 -0
  649. package/examples/fonts/MPLUSRounded1c/OFL.txt +0 -91
  650. package/examples/fonts/README.md +0 -11
  651. package/examples/fonts/droid/NOTICE +0 -190
  652. package/examples/fonts/droid/README.txt +0 -18
  653. package/examples/fonts/droid/droid_sans_bold.typeface.json +0 -1
  654. package/examples/fonts/droid/droid_sans_mono_regular.typeface.json +0 -1
  655. package/examples/fonts/droid/droid_sans_regular.typeface.json +0 -1
  656. package/examples/fonts/droid/droid_serif_bold.typeface.json +0 -1
  657. package/examples/fonts/droid/droid_serif_regular.typeface.json +0 -1
  658. package/examples/fonts/gentilis_bold.typeface.json +0 -1
  659. package/examples/fonts/gentilis_regular.typeface.json +0 -1
  660. package/examples/fonts/helvetiker_bold.typeface.json +0 -1
  661. package/examples/fonts/helvetiker_regular.typeface.json +0 -1
  662. package/examples/fonts/optimer_bold.typeface.json +0 -1
  663. package/examples/fonts/optimer_regular.typeface.json +0 -1
  664. package/examples/fonts/ttf/README.md +0 -9
  665. package/examples/fonts/ttf/kenpixel.ttf +0 -0
  666. package/examples/jsm/libs/ammo.wasm.js +0 -822
  667. package/examples/jsm/libs/ammo.wasm.wasm +0 -0
  668. package/examples/jsm/libs/draco/draco_encoder.js +0 -33
  669. package/examples/jsm/libs/draco/gltf/draco_encoder.js +0 -33
  670. package/examples/jsm/libs/lottie_canvas.module.js +0 -14849
  671. package/examples/jsm/libs/opentype.module.js +0 -14506
  672. package/examples/jsm/libs/rhino3dm/rhino3dm.js +0 -21
  673. package/examples/jsm/libs/rhino3dm/rhino3dm.module.js +0 -16
  674. package/examples/jsm/libs/rhino3dm/rhino3dm.wasm +0 -0
  675. package/examples/jsm/lighting/TiledLighting.js +0 -42
  676. package/examples/jsm/materials/MeshGouraudMaterial.js +0 -434
  677. package/examples/jsm/materials/MeshPostProcessingMaterial.js +0 -167
  678. package/examples/jsm/shaders/GodRaysShader.js +0 -333
  679. package/examples/jsm/tsl/display/AnamorphicNode.js +0 -282
  680. package/examples/jsm/tsl/lighting/TiledLightsNode.js +0 -442
  681. package/src/nodes/accessors/BatchNode.js +0 -163
  682. package/src/nodes/accessors/InstanceNode.js +0 -244
  683. package/src/nodes/accessors/InstancedMeshNode.js +0 -50
  684. package/src/nodes/accessors/SceneNode.js +0 -145
  685. package/src/nodes/accessors/SkinningNode.js +0 -327
  686. package/src/nodes/code/ScriptableNode.js +0 -726
  687. package/src/nodes/code/ScriptableValueNode.js +0 -253
  688. package/src/nodes/display/PosterizeNode.js +0 -65
  689. package/src/nodes/functions/BSDF/DFGApprox.js +0 -71
  690. package/src/nodes/utils/RemapNode.js +0 -125
  691. package/src/nodes/utils/SpriteSheetUVNode.js +0 -90
  692. package/src/renderers/webgl/WebGLCubeMaps.js +0 -99
  693. package/src/renderers/webgl/WebGLCubeUVMaps.js +0 -134
@@ -1,62 +1,339 @@
1
+ import { EventDispatcher } from 'three';
1
2
  import { Style } from './Style.js';
3
+ import { Graph } from './Graph.js';
4
+ import { getItem, setItem } from '../Inspector.js';
2
5
 
3
- export class Profiler {
6
+ export class Profiler extends EventDispatcher {
4
7
 
5
- constructor() {
8
+ constructor( inspector ) {
6
9
 
10
+ super();
11
+
12
+ this.inspector = inspector;
7
13
  this.tabs = {};
8
14
  this.activeTabId = null;
9
15
  this.isResizing = false;
10
- this.lastHeight = 350;
11
-
12
- Style.init();
16
+ this.lastHeightBottom = 350; // Height for bottom position
17
+ this.lastWidthRight = 450; // Width for right position
18
+ this.position = 'bottom'; // 'bottom' or 'right'
19
+ this.detachedWindows = []; // Array to store detached tab windows
20
+ this.maxZIndex = 1002; // Track the highest z-index for detached windows (starts at base z-index from CSS)
21
+ this.nextTabOriginalIndex = 0; // Track the original order of tabs as they are added
13
22
 
14
23
  this.setupShell();
15
24
  this.setupResizing();
16
25
 
26
+ Style.init( this.domElement );
27
+
28
+ // Setup window resize listener and update mobile status
29
+ this.setupWindowResizeListener();
30
+
31
+ // Setup orientation change listener for mobile devices
32
+ this.setupOrientationListener();
33
+
34
+ this.checkHeaderScroll();
35
+
36
+ this.panel.addEventListener( 'transitionend', ( e ) => {
37
+
38
+ if ( e.target === this.panel && ( e.propertyName === 'width' || e.propertyName === 'height' || e.propertyName === 'transform' ) ) {
39
+
40
+ this.checkHeaderScroll();
41
+
42
+ }
43
+
44
+ } );
45
+
46
+ }
47
+
48
+ getSize() {
49
+
50
+ if ( this.panel.classList.contains( 'visible' ) === false || this.panel.classList.contains( 'no-tabs' ) ) {
51
+
52
+ return { width: 0, height: 0 };
53
+
54
+ }
55
+
56
+ if ( this.position === 'right' ) {
57
+
58
+ return { width: this.panel.offsetWidth, height: 0 };
59
+
60
+ } else {
61
+
62
+ return { width: 0, height: this.panel.offsetHeight };
63
+
64
+ }
65
+
66
+ }
67
+
68
+ get isMobile() {
69
+
70
+ return this.detectMobile();
71
+
72
+ }
73
+
74
+ get isSmallScreen() {
75
+
76
+ return window.innerWidth <= 768;
77
+
78
+ }
79
+
80
+ detectMobile() {
81
+
82
+ // Check for mobile devices
83
+ const userAgent = navigator.userAgent || navigator.vendor || window.opera;
84
+ const isMobileUA = /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test( userAgent );
85
+ const isTouchDevice = ( 'ontouchstart' in window ) || ( navigator.maxTouchPoints > 0 );
86
+
87
+ return isMobileUA || ( isTouchDevice && this.isSmallScreen );
88
+
89
+ }
90
+
91
+ setupOrientationListener() {
92
+
93
+ const handleOrientationChange = () => {
94
+
95
+ if ( ! this.isMobile ) return;
96
+
97
+ // Check if device is in landscape or portrait mode
98
+ const isLandscape = window.innerWidth > window.innerHeight;
99
+
100
+ // In landscape mode, use right position (vertical panel)
101
+ // In portrait mode, use bottom position (horizontal panel)
102
+ const targetPosition = isLandscape ? 'right' : 'bottom';
103
+
104
+ if ( this.position !== targetPosition ) {
105
+
106
+ this.setPosition( targetPosition );
107
+
108
+ }
109
+
110
+ };
111
+
112
+ // Initial check
113
+ handleOrientationChange();
114
+
115
+ // Listen for orientation changes
116
+ window.addEventListener( 'orientationchange', handleOrientationChange );
117
+ window.addEventListener( 'resize', handleOrientationChange );
118
+
119
+ }
120
+
121
+ setupWindowResizeListener() {
122
+
123
+ const constrainDetachedWindows = () => {
124
+
125
+ this.detachedWindows.forEach( detachedWindow => {
126
+
127
+ this.constrainWindowToBounds( detachedWindow.panel );
128
+
129
+ } );
130
+
131
+ };
132
+
133
+ const constrainMainPanel = () => {
134
+
135
+ // Skip if panel is maximized (it should always fill the screen)
136
+ if ( this.panel.classList.contains( 'maximized' ) ) return;
137
+
138
+ const windowWidth = window.innerWidth;
139
+ const windowHeight = window.innerHeight;
140
+
141
+ if ( this.position === 'bottom' ) {
142
+
143
+ const currentHeight = this.panel.offsetHeight;
144
+ const maxHeight = windowHeight - 50; // Leave 50px margin
145
+
146
+ if ( currentHeight > maxHeight ) {
147
+
148
+ this.panel.style.height = `${ maxHeight }px`;
149
+ this.lastHeightBottom = maxHeight;
150
+
151
+ }
152
+
153
+ } else if ( this.position === 'right' ) {
154
+
155
+ const currentWidth = this.panel.offsetWidth;
156
+ const maxWidth = windowWidth - 50; // Leave 50px margin
157
+
158
+ if ( currentWidth > maxWidth ) {
159
+
160
+ this.panel.style.width = `${ maxWidth }px`;
161
+ this.lastWidthRight = maxWidth;
162
+
163
+ }
164
+
165
+ }
166
+
167
+ };
168
+
169
+ // Listen for window resize events
170
+ window.addEventListener( 'resize', () => {
171
+
172
+ if ( this.isSmallScreen ) {
173
+
174
+ this.floatingBtn.style.display = 'none';
175
+ this.panel.classList.add( 'hide-position-toggle' );
176
+
177
+ } else {
178
+
179
+ this.floatingBtn.style.display = '';
180
+ this.panel.classList.remove( 'hide-position-toggle' );
181
+
182
+ }
183
+
184
+ if ( this.isMobile ) {
185
+
186
+ this.panel.classList.add( 'is-mobile' );
187
+
188
+ } else {
189
+
190
+ this.panel.classList.remove( 'is-mobile' );
191
+
192
+ }
193
+
194
+ constrainDetachedWindows();
195
+ constrainMainPanel();
196
+ this.checkHeaderScroll();
197
+
198
+ } );
199
+
200
+ }
201
+
202
+ constrainWindowToBounds( windowPanel ) {
203
+
204
+ const windowWidth = window.innerWidth;
205
+ const windowHeight = window.innerHeight;
206
+
207
+ const panelWidth = windowPanel.offsetWidth;
208
+ const panelHeight = windowPanel.offsetHeight;
209
+
210
+ let left = parseFloat( windowPanel.style.left ) || windowPanel.offsetLeft || 0;
211
+ let top = parseFloat( windowPanel.style.top ) || windowPanel.offsetTop || 0;
212
+
213
+ // Allow window to extend half its width/height outside the screen
214
+ const halfWidth = panelWidth / 2;
215
+ const halfHeight = panelHeight / 2;
216
+
217
+ // Constrain horizontal position (allow half width to extend beyond right edge)
218
+ if ( left + panelWidth > windowWidth + halfWidth ) {
219
+
220
+ left = windowWidth + halfWidth - panelWidth;
221
+
222
+ }
223
+
224
+ // Constrain horizontal position (allow half width to extend beyond left edge)
225
+ if ( left < - halfWidth ) {
226
+
227
+ left = - halfWidth;
228
+
229
+ }
230
+
231
+ // Constrain vertical position (allow half height to extend beyond bottom edge)
232
+ if ( top + panelHeight > windowHeight + halfHeight ) {
233
+
234
+ top = windowHeight + halfHeight - panelHeight;
235
+
236
+ }
237
+
238
+ // Constrain vertical position (allow half height to extend beyond top edge)
239
+ if ( top < - halfHeight ) {
240
+
241
+ top = - halfHeight;
242
+
243
+ }
244
+
245
+ // Apply constrained position
246
+ windowPanel.style.left = `${ left }px`;
247
+ windowPanel.style.top = `${ top }px`;
248
+
17
249
  }
18
250
 
19
251
  setupShell() {
20
252
 
21
253
  this.domElement = document.createElement( 'div' );
22
- this.domElement.id = 'profiler-shell';
254
+
255
+ this.domElement.classList.add( 'three-inspector' );
23
256
 
24
257
  this.toggleButton = document.createElement( 'button' );
25
- this.toggleButton.id = 'profiler-toggle';
258
+ this.toggleButton.classList.add( 'profiler-toggle' );
26
259
  this.toggleButton.innerHTML = `
27
- <span id="toggle-text">
28
- <span id="fps-counter">-</span>
260
+ <span class="builtin-tabs-container"></span>
261
+ <span class="toggle-text">
262
+ <span class="fps-counter">-</span>
29
263
  <span class="fps-label">FPS</span>
30
264
  </span>
31
- <!-- <span class="toggle-separator"></span> -->
32
- <span id="toggle-icon">
265
+ <span class="toggle-icon">
33
266
  <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-device-ipad-horizontal-search"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M11.5 20h-6.5a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v5.5" /><path d="M9 17h2" /><path d="M18 18m-3 0a3 3 0 1 0 6 0a3 3 0 1 0 -6 0" /><path d="M20.2 20.2l1.8 1.8" /></svg>
267
+ <span class="console-badge-container">
268
+ <span class="console-badge error" style="display: none;">0</span>
269
+ <span class="console-badge warn" style="display: none;">0</span>
270
+ </span>
34
271
  </span>
35
272
  `;
36
273
  this.toggleButton.onclick = () => this.togglePanel();
37
274
 
275
+ this.builtinTabsContainer = this.toggleButton.querySelector( '.builtin-tabs-container' );
276
+
277
+ // Create mini-panel for builtin tabs (shown when panel is hidden)
278
+ this.miniPanel = document.createElement( 'div' );
279
+ this.miniPanel.classList.add( 'profiler-mini-panel' );
280
+ this.miniPanel.className = 'profiler-mini-panel';
281
+
38
282
  this.panel = document.createElement( 'div' );
39
- this.panel.id = 'profiler-panel';
283
+ this.panel.classList.add( 'profiler-panel' );
40
284
 
41
285
  const header = document.createElement( 'div' );
42
286
  header.className = 'profiler-header';
287
+
288
+ // Enable horizontal scrolling with vertical mouse wheel
289
+ header.addEventListener( 'wheel', ( e ) => {
290
+
291
+ if ( e.deltaY !== 0 ) {
292
+
293
+ e.preventDefault();
294
+ header.scrollLeft += e.deltaY * .25;
295
+
296
+ }
297
+
298
+ }, { passive: false } );
299
+
43
300
  this.tabsContainer = document.createElement( 'div' );
44
301
  this.tabsContainer.className = 'profiler-tabs';
45
302
 
46
303
  const controls = document.createElement( 'div' );
47
304
  controls.className = 'profiler-controls';
48
305
 
306
+ this.floatingBtn = document.createElement( 'button' );
307
+ this.floatingBtn.classList.add( 'floating-btn' );
308
+ this.floatingBtn.title = 'Switch to Right Side';
309
+ this.floatingBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="15" y1="3" x2="15" y2="21"></line></svg>';
310
+ this.floatingBtn.onclick = () => this.togglePosition();
311
+
312
+ // Hide position toggle button on small screens
313
+ if ( this.isSmallScreen ) {
314
+
315
+ this.floatingBtn.style.display = 'none';
316
+ this.panel.classList.add( 'hide-position-toggle' );
317
+
318
+ }
319
+
320
+ if ( this.isMobile ) {
321
+
322
+ this.panel.classList.add( 'is-mobile' );
323
+
324
+ }
325
+
49
326
  this.maximizeBtn = document.createElement( 'button' );
50
- this.maximizeBtn.id = 'maximize-btn';
327
+ this.maximizeBtn.classList.add( 'maximize-btn' );
51
328
  this.maximizeBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/></svg>';
52
329
  this.maximizeBtn.onclick = () => this.toggleMaximize();
53
330
 
54
331
  const hideBtn = document.createElement( 'button' );
55
- hideBtn.id = 'hide-panel-btn';
332
+ hideBtn.classList.add( 'hide-panel-btn' );
56
333
  hideBtn.textContent = '-';
57
334
  hideBtn.onclick = () => this.togglePanel();
58
335
 
59
- controls.append( this.maximizeBtn, hideBtn );
336
+ controls.append( this.floatingBtn, this.maximizeBtn, hideBtn );
60
337
  header.append( this.tabsContainer, controls );
61
338
 
62
339
  this.contentWrapper = document.createElement( 'div' );
@@ -67,7 +344,23 @@ export class Profiler {
67
344
 
68
345
  this.panel.append( resizer, header, this.contentWrapper );
69
346
 
70
- this.domElement.append( this.toggleButton, this.panel );
347
+ this.domElement.append( this.toggleButton, this.miniPanel, this.panel );
348
+
349
+ // Set initial position class
350
+ this.panel.classList.add( `position-${this.position}` );
351
+
352
+ if ( this.position === 'right' ) {
353
+
354
+ this.toggleButton.classList.add( 'position-right' );
355
+ this.miniPanel.classList.add( 'position-right' );
356
+
357
+ }
358
+
359
+ // Create a background performance graph for the toggle button
360
+ this.toggleGraph = new Graph( 80 );
361
+ this.toggleGraph.addLine( 'fps', '#4c4c6bff' );
362
+ this.toggleGraph.domElement.className = 'profiler-toggle-graph';
363
+ this.toggleButton.appendChild( this.toggleGraph.domElement );
71
364
 
72
365
  }
73
366
 
@@ -79,48 +372,82 @@ export class Profiler {
79
372
 
80
373
  this.isResizing = true;
81
374
  this.panel.classList.add( 'resizing' );
82
- const startY = e.clientY || e.touches[ 0 ].clientY;
375
+ resizer.setPointerCapture( e.pointerId );
376
+ const startX = e.clientX;
377
+ const startY = e.clientY;
83
378
  const startHeight = this.panel.offsetHeight;
379
+ const startWidth = this.panel.offsetWidth;
84
380
 
85
381
  const onMove = ( moveEvent ) => {
86
382
 
87
383
  if ( ! this.isResizing ) return;
88
384
  moveEvent.preventDefault();
89
- const currentY = moveEvent.clientY || moveEvent.touches[ 0 ].clientY;
90
- const newHeight = startHeight - ( currentY - startY );
91
- if ( newHeight > 100 && newHeight < window.innerHeight - 50 ) {
385
+ const currentX = moveEvent.clientX;
386
+ const currentY = moveEvent.clientY;
387
+
388
+ if ( this.position === 'bottom' ) {
389
+
390
+ // Vertical resize for bottom position
391
+ const newHeight = startHeight - ( currentY - startY );
392
+
393
+ if ( newHeight > 100 && newHeight < window.innerHeight - 50 ) {
394
+
395
+ this.panel.style.height = `${ newHeight }px`;
92
396
 
93
- this.panel.style.height = `${newHeight}px`;
397
+ }
398
+
399
+ } else if ( this.position === 'right' ) {
400
+
401
+ // Horizontal resize for right position
402
+ const newWidth = startWidth - ( currentX - startX );
403
+
404
+ if ( newWidth > 200 && newWidth < window.innerWidth - 50 ) {
405
+
406
+ this.panel.style.width = `${ newWidth }px`;
407
+
408
+ }
94
409
 
95
410
  }
96
411
 
412
+ this.dispatchEvent( { type: 'resize' } );
413
+ this.checkHeaderScroll();
414
+
97
415
  };
98
416
 
99
417
  const onEnd = () => {
100
418
 
101
419
  this.isResizing = false;
102
420
  this.panel.classList.remove( 'resizing' );
103
- document.removeEventListener( 'mousemove', onMove );
104
- document.removeEventListener( 'mouseup', onEnd );
105
- document.removeEventListener( 'touchmove', onMove );
106
- document.removeEventListener( 'touchend', onEnd );
421
+ resizer.removeEventListener( 'pointermove', onMove );
422
+ resizer.removeEventListener( 'pointerup', onEnd );
423
+ resizer.removeEventListener( 'pointercancel', onEnd );
107
424
  if ( ! this.panel.classList.contains( 'maximized' ) ) {
108
425
 
109
- this.lastHeight = this.panel.offsetHeight;
426
+ // Save dimensions based on current position
427
+ if ( this.position === 'bottom' ) {
428
+
429
+ this.lastHeightBottom = this.panel.offsetHeight;
430
+
431
+ } else if ( this.position === 'right' ) {
432
+
433
+ this.lastWidthRight = this.panel.offsetWidth;
434
+
435
+ }
436
+
437
+ // Save layout after resize
438
+ this.saveLayout();
110
439
 
111
440
  }
112
441
 
113
442
  };
114
443
 
115
- document.addEventListener( 'mousemove', onMove );
116
- document.addEventListener( 'mouseup', onEnd );
117
- document.addEventListener( 'touchmove', onMove, { passive: false } );
118
- document.addEventListener( 'touchend', onEnd );
444
+ resizer.addEventListener( 'pointermove', onMove );
445
+ resizer.addEventListener( 'pointerup', onEnd );
446
+ resizer.addEventListener( 'pointercancel', onEnd );
119
447
 
120
448
  };
121
449
 
122
- resizer.addEventListener( 'mousedown', onStart );
123
- resizer.addEventListener( 'touchstart', onStart );
450
+ resizer.addEventListener( 'pointerdown', onStart );
124
451
 
125
452
  }
126
453
 
@@ -129,41 +456,1661 @@ export class Profiler {
129
456
  if ( this.panel.classList.contains( 'maximized' ) ) {
130
457
 
131
458
  this.panel.classList.remove( 'maximized' );
132
- this.panel.style.height = `${ this.lastHeight }px`;
459
+ this.domElement.classList.remove( 'maximized' );
460
+
461
+ // Restore size based on current position
462
+ if ( this.position === 'bottom' ) {
463
+
464
+ this.panel.style.height = `${ this.lastHeightBottom }px`;
465
+ this.panel.style.width = '100%';
466
+
467
+ } else if ( this.position === 'right' ) {
468
+
469
+ this.panel.style.height = '100%';
470
+ this.panel.style.width = `${ this.lastWidthRight }px`;
471
+
472
+ }
473
+
133
474
  this.maximizeBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/></svg>';
134
475
 
135
476
  } else {
136
477
 
137
- this.lastHeight = this.panel.offsetHeight;
478
+ // Save current size before maximizing
479
+ if ( this.position === 'bottom' ) {
480
+
481
+ this.lastHeightBottom = this.panel.offsetHeight;
482
+
483
+ } else if ( this.position === 'right' ) {
484
+
485
+ this.lastWidthRight = this.panel.offsetWidth;
486
+
487
+ }
488
+
138
489
  this.panel.classList.add( 'maximized' );
139
- this.panel.style.height = '100vh';
490
+ this.domElement.classList.add( 'maximized' );
491
+
492
+ // Maximize based on current position
493
+ if ( this.position === 'bottom' ) {
494
+
495
+ this.panel.style.height = '100vh';
496
+ this.panel.style.width = '100%';
497
+
498
+ } else if ( this.position === 'right' ) {
499
+
500
+ this.panel.style.height = '100%';
501
+ this.panel.style.width = '100vw';
502
+
503
+ }
504
+
140
505
  this.maximizeBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="8" y="8" width="12" height="12" rx="2" ry="2"></rect><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"></path></svg>';
141
506
 
142
507
  }
143
508
 
509
+ this.dispatchEvent( { type: 'resize' } );
510
+
511
+ }
512
+
513
+ hide() {
514
+
515
+ this.miniPanel.classList.remove( 'visible' );
516
+
517
+ this.miniPanel.querySelectorAll( '.mini-panel-content' ).forEach( content => {
518
+
519
+ content.style.display = 'none';
520
+
521
+ } );
522
+
523
+ this.builtinTabsContainer.querySelectorAll( '.builtin-tab-btn' ).forEach( btn => {
524
+
525
+ btn.classList.remove( 'active' );
526
+
527
+ } );
528
+
529
+ }
530
+
531
+ show( tab ) {
532
+
533
+ this.hide();
534
+
535
+ tab.builtinButton.classList.add( 'active' );
536
+
537
+ if ( ! tab.miniContent.firstChild ) {
538
+
539
+ while ( tab.content.firstChild ) {
540
+
541
+ tab.miniContent.appendChild( tab.content.firstChild );
542
+
543
+ }
544
+
545
+ }
546
+
547
+ tab.miniContent.style.display = 'block';
548
+ this.miniPanel.classList.add( 'visible' );
549
+
144
550
  }
145
551
 
146
552
  addTab( tab ) {
147
553
 
148
554
  this.tabs[ tab.id ] = tab;
149
- tab.button.onclick = () => this.setActiveTab( tab.id );
150
- this.tabsContainer.appendChild( tab.button );
555
+
556
+ // Assign a permanent original index to this tab
557
+ tab.originalIndex = this.nextTabOriginalIndex ++;
558
+
559
+ // Add visual indicator for tabs that cannot be detached
560
+ if ( tab.allowDetach === false ) {
561
+
562
+ tab.button.classList.add( 'no-detach' );
563
+
564
+ }
565
+
566
+ // Set visibility change callback
567
+ tab.onVisibilityChange = () => this.updatePanelSize();
568
+
569
+ this.setupTabDragAndDrop( tab );
570
+
571
+ if ( ! tab.builtin ) {
572
+
573
+ this.tabsContainer.appendChild( tab.button );
574
+
575
+ }
576
+
151
577
  this.contentWrapper.appendChild( tab.content );
152
578
 
579
+ // Apply the current visibility state to the DOM elements
580
+ if ( ! tab.isVisible ) {
581
+
582
+ tab.button.style.display = 'none';
583
+ tab.content.style.display = 'none';
584
+
585
+ }
586
+
587
+ // If tab is builtin, add it to the profiler-toggle button
588
+ if ( tab.builtin ) {
589
+
590
+ this.addBuiltinTab( tab );
591
+
592
+ }
593
+
594
+ // Update panel size when tabs change
595
+ this.updatePanelSize();
596
+
597
+ // Set profiler reference
598
+ tab.profiler = this;
599
+
153
600
  }
154
601
 
155
- setActiveTab( id ) {
602
+ addBuiltinTab( tab ) {
156
603
 
157
- if ( this.activeTabId ) this.tabs[ this.activeTabId ].setActive( false );
158
- this.activeTabId = id;
159
- this.tabs[ id ].setActive( true );
604
+ // Create a button for the builtin tab in the profiler-toggle
605
+ const builtinButton = document.createElement( 'button' );
606
+ builtinButton.className = 'builtin-tab-btn';
607
+
608
+ // Use icon if provided, otherwise use first letter
609
+ if ( tab.icon ) {
610
+
611
+ builtinButton.innerHTML = tab.icon;
612
+
613
+ } else {
614
+
615
+ builtinButton.textContent = tab.button.textContent.charAt( 0 ).toUpperCase();
616
+
617
+ }
618
+
619
+ builtinButton.title = tab.button.textContent;
620
+
621
+ // Create mini-panel content container for this tab
622
+ const miniContent = document.createElement( 'div' );
623
+ miniContent.className = 'mini-panel-content';
624
+ miniContent.style.display = 'none';
625
+
626
+ // Store references in the tab object
627
+ tab.builtinButton = builtinButton;
628
+ tab.miniContent = miniContent;
629
+
630
+ this.miniPanel.appendChild( miniContent );
631
+
632
+ builtinButton.onclick = ( e ) => {
633
+
634
+ e.stopPropagation(); // Prevent toggle panel from triggering
635
+
636
+ // Toggle mini-panel for this tab
637
+ const isCurrentlyActive = miniContent.style.display !== 'none' && miniContent.children.length > 0;
638
+
639
+ if ( isCurrentlyActive ) {
640
+
641
+ this.hide();
642
+
643
+ } else {
644
+
645
+ this.show( tab );
646
+
647
+ }
648
+
649
+ };
650
+
651
+ this.builtinTabsContainer.appendChild( builtinButton );
652
+
653
+ // Store references
654
+ tab.builtinButton = builtinButton;
655
+ tab.miniContent = miniContent;
656
+
657
+ // If the tab was hidden before being added, hide the builtin button
658
+ if ( ! tab.isVisible ) {
659
+
660
+ builtinButton.style.display = 'none';
661
+ miniContent.style.display = 'none';
662
+
663
+ // Hide the builtin-tabs-container if all builtin buttons are hidden
664
+ const hasVisibleBuiltinButtons = Array.from( this.builtinTabsContainer.querySelectorAll( '.builtin-tab-btn' ) )
665
+ .some( btn => btn.style.display !== 'none' );
666
+
667
+ if ( ! hasVisibleBuiltinButtons ) {
668
+
669
+ this.builtinTabsContainer.style.display = 'none';
670
+
671
+ }
672
+
673
+ }
160
674
 
161
675
  }
162
676
 
163
- togglePanel() {
677
+ removeTab( tab ) {
164
678
 
165
- this.panel.classList.toggle( 'visible' );
166
- this.toggleButton.classList.toggle( 'hidden' );
679
+ if ( ! tab || this.tabs[ tab.id ] === undefined ) return;
680
+
681
+ delete this.tabs[ tab.id ];
682
+
683
+ if ( tab.isDetached && tab.detachedWindow ) {
684
+
685
+ if ( tab.detachedWindow.panel && tab.detachedWindow.panel.parentNode ) {
686
+
687
+ tab.detachedWindow.panel.parentNode.removeChild( tab.detachedWindow.panel );
688
+
689
+ }
690
+
691
+ const index = this.detachedWindows.indexOf( tab.detachedWindow );
692
+
693
+ if ( index !== - 1 ) {
694
+
695
+ this.detachedWindows.splice( index, 1 );
696
+
697
+ }
698
+
699
+ }
700
+
701
+ if ( ! tab.builtin ) {
702
+
703
+ if ( tab.button && tab.button.parentNode ) {
704
+
705
+ tab.button.parentNode.removeChild( tab.button );
706
+
707
+ }
708
+
709
+ } else {
710
+
711
+ if ( tab.builtinButton && tab.builtinButton.parentNode ) {
712
+
713
+ tab.builtinButton.parentNode.removeChild( tab.builtinButton );
714
+
715
+ }
716
+
717
+ if ( tab.miniContent && tab.miniContent.parentNode ) {
718
+
719
+ tab.miniContent.parentNode.removeChild( tab.miniContent );
720
+
721
+ }
722
+
723
+ // Clean up builtin container if empty
724
+ const hasVisibleBuiltinButtons = Array.from( this.builtinTabsContainer.querySelectorAll( '.builtin-tab-btn' ) )
725
+ .some( btn => btn.style.display !== 'none' );
726
+
727
+ if ( ! hasVisibleBuiltinButtons ) {
728
+
729
+ this.builtinTabsContainer.style.display = 'none';
730
+
731
+ }
732
+
733
+ }
734
+
735
+ if ( tab.content && tab.content.parentNode ) {
736
+
737
+ tab.content.parentNode.removeChild( tab.content );
738
+
739
+ }
740
+
741
+ if ( this.activeTabId === tab.id ) {
742
+
743
+ this.activeTabId = null;
744
+
745
+ // Try to activate another tab
746
+ const remainingTabs = Object.values( this.tabs ).filter( t => ! t.isDetached && t.isVisible );
747
+
748
+ if ( remainingTabs.length > 0 ) {
749
+
750
+ this.setActiveTab( remainingTabs[ 0 ].id );
751
+
752
+ } else {
753
+
754
+ this.updatePanelSize();
755
+
756
+ }
757
+
758
+ } else {
759
+
760
+ this.updatePanelSize();
761
+
762
+ }
763
+
764
+ tab.onVisibilityChange = null;
765
+ tab.profiler = null;
766
+
767
+ }
768
+
769
+ updatePanelSize() {
770
+
771
+ // Check if there are any visible tabs in the panel
772
+ const hasVisibleTabs = Object.values( this.tabs ).some( tab => ! tab.isDetached && tab.isVisible );
773
+
774
+ // Add or remove CSS class to indicate no tabs state
775
+ if ( ! hasVisibleTabs ) {
776
+
777
+ this.panel.classList.add( 'no-tabs' );
778
+
779
+ // If maximized and no tabs, restore to normal size
780
+ if ( this.panel.classList.contains( 'maximized' ) ) {
781
+
782
+ this.panel.classList.remove( 'maximized' );
783
+ this.domElement.classList.remove( 'maximized' );
784
+ this.maximizeBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/></svg>';
785
+
786
+ }
787
+
788
+ // No tabs visible - set to minimum size
789
+ if ( this.position === 'bottom' ) {
790
+
791
+ this.panel.style.height = '32px';
792
+
793
+ } else if ( this.position === 'right' ) {
794
+
795
+ // 45px = width of one button column
796
+ this.panel.style.width = '45px';
797
+
798
+ }
799
+
800
+ } else {
801
+
802
+ this.panel.classList.remove( 'no-tabs' );
803
+
804
+ if ( Object.keys( this.tabs ).length > 0 ) {
805
+
806
+ // Has tabs - restore to saved size only if we had set it to minimum before
807
+ if ( this.position === 'bottom' ) {
808
+
809
+ const currentHeight = parseInt( this.panel.style.height );
810
+ if ( currentHeight === 32 || currentHeight === 38 ) {
811
+
812
+ this.panel.style.height = `${ this.lastHeightBottom }px`;
813
+
814
+ }
815
+
816
+ } else if ( this.position === 'right' ) {
817
+
818
+ const currentWidth = parseInt( this.panel.style.width );
819
+ if ( currentWidth === 45 ) {
820
+
821
+ this.panel.style.width = `${ this.lastWidthRight }px`;
822
+
823
+ }
824
+
825
+ }
826
+
827
+ }
828
+
829
+ }
830
+
831
+ this.dispatchEvent( { type: 'resize' } );
832
+ this.checkHeaderScroll();
833
+
834
+ }
835
+
836
+ checkHeaderScroll() {
837
+
838
+ const header = this.panel.querySelector( '.profiler-header' );
839
+
840
+ if ( header ) {
841
+
842
+ const hasScroll = header.scrollWidth > header.clientWidth + 1;
843
+
844
+ if ( hasScroll ) {
845
+
846
+ this.panel.classList.add( 'has-horizontal-scroll' );
847
+
848
+ } else {
849
+
850
+ this.panel.classList.remove( 'has-horizontal-scroll' );
851
+
852
+ }
853
+
854
+ }
855
+
856
+ }
857
+
858
+ setupTabDragAndDrop( tab ) {
859
+
860
+ // Always handle basic click
861
+ tab.button.addEventListener( 'click', () => {
862
+
863
+ if ( ! isDragging ) {
864
+
865
+ this.setActiveTab( tab.id );
866
+
867
+ }
868
+
869
+ } );
870
+
871
+ // Disable drag and drop if tab doesn't allow detach
872
+ if ( tab.allowDetach === false ) {
873
+
874
+ tab.button.style.cursor = 'default';
875
+
876
+ return;
877
+
878
+ }
879
+
880
+ let isDragging = false;
881
+ let startX, startY;
882
+ let hasMoved = false;
883
+ let previewWindow = null;
884
+ const dragThreshold = 10; // pixels to move before starting drag
885
+
886
+ const onDragStart = ( e ) => {
887
+
888
+ startX = e.clientX;
889
+ startY = e.clientY;
890
+ isDragging = false;
891
+ hasMoved = false;
892
+ tab.button.setPointerCapture( e.pointerId );
893
+
894
+ };
895
+
896
+ const onDragMove = ( e ) => {
897
+
898
+ const currentX = e.clientX;
899
+ const currentY = e.clientY;
900
+
901
+ const deltaX = Math.abs( currentX - startX );
902
+ const deltaY = Math.abs( currentY - startY );
903
+
904
+ if ( ! isDragging && ( deltaX > dragThreshold || deltaY > dragThreshold ) ) {
905
+
906
+ isDragging = true;
907
+ tab.button.style.cursor = 'grabbing';
908
+ tab.button.style.opacity = '0.5';
909
+ tab.button.style.transform = 'scale(1.05)';
910
+
911
+ previewWindow = this.createPreviewWindow( tab, currentX, currentY );
912
+ previewWindow.style.opacity = '0.8';
913
+
914
+ }
915
+
916
+ if ( isDragging && previewWindow ) {
917
+
918
+ hasMoved = true;
919
+ e.preventDefault();
920
+
921
+ previewWindow.style.left = `${ currentX - 200 }px`;
922
+ previewWindow.style.top = `${ currentY - 20 }px`;
923
+
924
+ }
925
+
926
+ };
927
+
928
+ const onDragEnd = () => {
929
+
930
+ if ( isDragging && hasMoved && previewWindow ) {
931
+
932
+ const finalX = parseInt( previewWindow.style.left ) + 200;
933
+ const finalY = parseInt( previewWindow.style.top ) + 20;
934
+
935
+ if ( previewWindow.parentNode ) {
936
+
937
+ previewWindow.parentNode.removeChild( previewWindow );
938
+
939
+ }
940
+
941
+ this.detachTab( tab, finalX, finalY );
942
+
943
+ } else if ( ! hasMoved ) {
944
+
945
+ this.setActiveTab( tab.id );
946
+
947
+ if ( previewWindow && previewWindow.parentNode ) {
948
+
949
+ previewWindow.parentNode.removeChild( previewWindow );
950
+
951
+ }
952
+
953
+ } else if ( previewWindow ) {
954
+
955
+ if ( previewWindow.parentNode ) {
956
+
957
+ previewWindow.parentNode.removeChild( previewWindow );
958
+
959
+ }
960
+
961
+ }
962
+
963
+ tab.button.style.opacity = '';
964
+ tab.button.style.transform = '';
965
+ tab.button.style.cursor = '';
966
+ isDragging = false;
967
+ hasMoved = false;
968
+ previewWindow = null;
969
+
970
+ tab.button.removeEventListener( 'pointermove', onDragMove );
971
+ tab.button.removeEventListener( 'pointerup', onDragEnd );
972
+ tab.button.removeEventListener( 'pointercancel', onDragEnd );
973
+
974
+ };
975
+
976
+ tab.button.addEventListener( 'pointerdown', ( e ) => {
977
+
978
+ if ( this.isMobile && e.pointerType !== 'mouse' ) return;
979
+
980
+ onDragStart( e );
981
+ tab.button.addEventListener( 'pointermove', onDragMove );
982
+ tab.button.addEventListener( 'pointerup', onDragEnd );
983
+ tab.button.addEventListener( 'pointercancel', onDragEnd );
984
+
985
+ } );
986
+
987
+ // Set cursor to grab for tabs that can be detached
988
+ tab.button.style.cursor = 'grab';
989
+
990
+ }
991
+
992
+ createPreviewWindow( tab, x, y ) {
993
+
994
+ const windowPanel = document.createElement( 'div' );
995
+ windowPanel.className = 'detached-tab-panel';
996
+ windowPanel.style.left = `${ x - 200 }px`;
997
+ windowPanel.style.top = `${ y - 20 }px`;
998
+ windowPanel.style.pointerEvents = 'none'; // Preview only
999
+
1000
+ // Set z-index for preview window to be on top
1001
+ this.maxZIndex ++;
1002
+ windowPanel.style.setProperty( 'z-index', this.maxZIndex, 'important' );
1003
+
1004
+ const windowHeader = document.createElement( 'div' );
1005
+ windowHeader.className = 'detached-tab-header';
1006
+
1007
+ const title = document.createElement( 'span' );
1008
+ title.textContent = tab.button.textContent.replace( '⇱', '' ).trim();
1009
+ windowHeader.appendChild( title );
1010
+
1011
+ const headerControls = document.createElement( 'div' );
1012
+ headerControls.className = 'detached-header-controls';
1013
+
1014
+ const reattachBtn = document.createElement( 'button' );
1015
+ reattachBtn.className = 'detached-reattach-btn';
1016
+ reattachBtn.innerHTML = '↩';
1017
+ headerControls.appendChild( reattachBtn );
1018
+ windowHeader.appendChild( headerControls );
1019
+
1020
+ const windowContent = document.createElement( 'div' );
1021
+ windowContent.className = 'detached-tab-content';
1022
+
1023
+ const resizer = document.createElement( 'div' );
1024
+ resizer.className = 'detached-tab-resizer';
1025
+
1026
+ windowPanel.appendChild( resizer );
1027
+ windowPanel.appendChild( windowHeader );
1028
+ windowPanel.appendChild( windowContent );
1029
+
1030
+ this.domElement.appendChild( windowPanel );
1031
+
1032
+ return windowPanel;
1033
+
1034
+ }
1035
+
1036
+ detachTab( tab, x, y ) {
1037
+
1038
+ if ( tab.isDetached ) return;
1039
+
1040
+ // Check if tab allows detachment
1041
+ if ( tab.allowDetach === false ) return;
1042
+
1043
+ const allButtons = Array.from( this.tabsContainer.children );
1044
+
1045
+ const tabIdsInOrder = allButtons.map( btn => {
1046
+
1047
+ return Object.keys( this.tabs ).find( id => this.tabs[ id ].button === btn );
1048
+
1049
+ } ).filter( id => id !== undefined );
1050
+
1051
+ const currentIndex = tabIdsInOrder.indexOf( tab.id );
1052
+
1053
+ let newActiveTab = null;
1054
+
1055
+ if ( this.activeTabId === tab.id ) {
1056
+
1057
+ tab.setActive( false );
1058
+
1059
+ const remainingTabs = tabIdsInOrder.filter( id =>
1060
+ id !== tab.id &&
1061
+ ! this.tabs[ id ].isDetached &&
1062
+ this.tabs[ id ].isVisible
1063
+ );
1064
+
1065
+ if ( remainingTabs.length > 0 ) {
1066
+
1067
+ for ( let i = currentIndex - 1; i >= 0; i -- ) {
1068
+
1069
+ if ( remainingTabs.includes( tabIdsInOrder[ i ] ) ) {
1070
+
1071
+ newActiveTab = tabIdsInOrder[ i ];
1072
+ break;
1073
+
1074
+ }
1075
+
1076
+ }
1077
+
1078
+ if ( ! newActiveTab ) {
1079
+
1080
+ for ( let i = currentIndex + 1; i < tabIdsInOrder.length; i ++ ) {
1081
+
1082
+ if ( remainingTabs.includes( tabIdsInOrder[ i ] ) ) {
1083
+
1084
+ newActiveTab = tabIdsInOrder[ i ];
1085
+ break;
1086
+
1087
+ }
1088
+
1089
+ }
1090
+
1091
+ }
1092
+
1093
+ if ( ! newActiveTab ) {
1094
+
1095
+ newActiveTab = remainingTabs[ 0 ];
1096
+
1097
+ }
1098
+
1099
+ }
1100
+
1101
+ }
1102
+
1103
+ if ( tab.button.parentNode ) {
1104
+
1105
+ tab.button.parentNode.removeChild( tab.button );
1106
+
1107
+ }
1108
+
1109
+ if ( tab.content.parentNode ) {
1110
+
1111
+ tab.content.parentNode.removeChild( tab.content );
1112
+
1113
+ }
1114
+
1115
+ const detachedWindow = this.createDetachedWindow( tab, x, y );
1116
+ this.detachedWindows.push( detachedWindow );
1117
+
1118
+ tab.isDetached = true;
1119
+ tab.detachedWindow = detachedWindow;
1120
+
1121
+ if ( newActiveTab ) {
1122
+
1123
+ this.setActiveTab( newActiveTab );
1124
+
1125
+ } else if ( this.activeTabId === tab.id ) {
1126
+
1127
+ this.activeTabId = null;
1128
+
1129
+ }
1130
+
1131
+ // Update panel size after detaching
1132
+ this.updatePanelSize();
1133
+
1134
+ this.saveLayout();
1135
+
1136
+ }
1137
+
1138
+ createDetachedWindow( tab, x, y ) {
1139
+
1140
+ // Constrain initial position to window bounds
1141
+ const windowWidth = window.innerWidth;
1142
+ const windowHeight = window.innerHeight;
1143
+ const estimatedWidth = 400; // Default detached window width
1144
+ const estimatedHeight = 300; // Default detached window height
1145
+
1146
+ let constrainedX = x - 200;
1147
+ let constrainedY = y - 20;
1148
+
1149
+ if ( constrainedX + estimatedWidth > windowWidth ) {
1150
+
1151
+ constrainedX = windowWidth - estimatedWidth;
1152
+
1153
+ }
1154
+
1155
+ if ( constrainedX < 0 ) {
1156
+
1157
+ constrainedX = 0;
1158
+
1159
+ }
1160
+
1161
+ if ( constrainedY + estimatedHeight > windowHeight ) {
1162
+
1163
+ constrainedY = windowHeight - estimatedHeight;
1164
+
1165
+ }
1166
+
1167
+ if ( constrainedY < 0 ) {
1168
+
1169
+ constrainedY = 0;
1170
+
1171
+ }
1172
+
1173
+ const windowPanel = document.createElement( 'div' );
1174
+ windowPanel.className = 'detached-tab-panel';
1175
+ windowPanel.style.left = `${ constrainedX }px`;
1176
+ windowPanel.style.top = `${ constrainedY }px`;
1177
+
1178
+
1179
+
1180
+ // Hide detached window if tab is not visible
1181
+ if ( ! tab.isVisible ) {
1182
+
1183
+ windowPanel.style.display = 'none';
1184
+
1185
+ }
1186
+
1187
+ const windowHeader = document.createElement( 'div' );
1188
+ windowHeader.className = 'detached-tab-header';
1189
+
1190
+ const title = document.createElement( 'span' );
1191
+ title.textContent = tab.button.textContent.replace( '⇱', '' ).trim();
1192
+ windowHeader.appendChild( title );
1193
+
1194
+ const headerControls = document.createElement( 'div' );
1195
+ headerControls.className = 'detached-header-controls';
1196
+
1197
+ const reattachBtn = document.createElement( 'button' );
1198
+ reattachBtn.className = 'detached-reattach-btn';
1199
+ reattachBtn.innerHTML = '↩';
1200
+ reattachBtn.title = 'Reattach to main panel';
1201
+ reattachBtn.onclick = () => this.reattachTab( tab );
1202
+
1203
+ headerControls.appendChild( reattachBtn );
1204
+ windowHeader.appendChild( headerControls );
1205
+
1206
+ const windowContent = document.createElement( 'div' );
1207
+ windowContent.className = 'detached-tab-content';
1208
+ windowContent.appendChild( tab.content );
1209
+
1210
+ // Make sure content is visible
1211
+ tab.content.style.display = 'block';
1212
+ tab.content.classList.add( 'active' );
1213
+
1214
+ // Create resize handles for all edges
1215
+ const resizerTop = document.createElement( 'div' );
1216
+ resizerTop.className = 'detached-tab-resizer-top';
1217
+
1218
+ const resizerRight = document.createElement( 'div' );
1219
+ resizerRight.className = 'detached-tab-resizer-right';
1220
+
1221
+ const resizerBottom = document.createElement( 'div' );
1222
+ resizerBottom.className = 'detached-tab-resizer-bottom';
1223
+
1224
+ const resizerLeft = document.createElement( 'div' );
1225
+ resizerLeft.className = 'detached-tab-resizer-left';
1226
+
1227
+ const resizerCorner = document.createElement( 'div' );
1228
+ resizerCorner.className = 'detached-tab-resizer';
1229
+
1230
+ windowPanel.appendChild( resizerTop );
1231
+ windowPanel.appendChild( resizerRight );
1232
+ windowPanel.appendChild( resizerBottom );
1233
+ windowPanel.appendChild( resizerLeft );
1234
+ windowPanel.appendChild( resizerCorner );
1235
+ windowPanel.appendChild( windowHeader );
1236
+ windowPanel.appendChild( windowContent );
1237
+
1238
+ this.domElement.appendChild( windowPanel );
1239
+
1240
+ // Setup window dragging
1241
+ this.setupDetachedWindowDrag( windowPanel, windowHeader, tab );
1242
+
1243
+ // Setup window resizing
1244
+ this.setupDetachedWindowResize( windowPanel, resizerTop, resizerRight, resizerBottom, resizerLeft, resizerCorner );
1245
+
1246
+ // Use the same z-index that was set on the preview window
1247
+ windowPanel.style.setProperty( 'z-index', this.maxZIndex, 'important' );
1248
+
1249
+ return { panel: windowPanel, tab: tab };
1250
+
1251
+ }
1252
+
1253
+ bringWindowToFront( windowPanel ) {
1254
+
1255
+ // Increment the max z-index and apply it to the clicked window
1256
+ this.maxZIndex ++;
1257
+ windowPanel.style.setProperty( 'z-index', this.maxZIndex, 'important' );
1258
+
1259
+ }
1260
+
1261
+ setupDetachedWindowDrag( windowPanel, header, tab ) {
1262
+
1263
+ let isDragging = false;
1264
+ let startX, startY, startLeft, startTop;
1265
+
1266
+ // Bring window to front when clicking anywhere on it
1267
+ windowPanel.addEventListener( 'pointerdown', () => {
1268
+
1269
+ this.bringWindowToFront( windowPanel );
1270
+
1271
+ } );
1272
+
1273
+ const onDragStart = ( e ) => {
1274
+
1275
+ if ( e.target.classList.contains( 'detached-reattach-btn' ) ) {
1276
+
1277
+ return;
1278
+
1279
+ }
1280
+
1281
+ // Bring window to front when starting to drag
1282
+ this.bringWindowToFront( windowPanel );
1283
+
1284
+ isDragging = true;
1285
+ header.style.cursor = 'grabbing';
1286
+ header.setPointerCapture( e.pointerId );
1287
+
1288
+ startX = e.clientX;
1289
+ startY = e.clientY;
1290
+
1291
+ const rect = windowPanel.getBoundingClientRect();
1292
+ startLeft = rect.left;
1293
+ startTop = rect.top;
1294
+
1295
+ };
1296
+
1297
+ const onDragMove = ( e ) => {
1298
+
1299
+ if ( ! isDragging ) return;
1300
+
1301
+ e.preventDefault();
1302
+
1303
+ const currentX = e.clientX;
1304
+ const currentY = e.clientY;
1305
+
1306
+ const deltaX = currentX - startX;
1307
+ const deltaY = currentY - startY;
1308
+
1309
+ let newLeft = startLeft + deltaX;
1310
+ let newTop = startTop + deltaY;
1311
+
1312
+ // Constrain to window bounds (allow half width/height to extend outside)
1313
+ const windowWidth = window.innerWidth;
1314
+ const windowHeight = window.innerHeight;
1315
+ const panelWidth = windowPanel.offsetWidth;
1316
+ const panelHeight = windowPanel.offsetHeight;
1317
+ const halfWidth = panelWidth / 2;
1318
+ const halfHeight = panelHeight / 2;
1319
+
1320
+ // Allow window to extend half its width beyond right edge
1321
+ if ( newLeft + panelWidth > windowWidth + halfWidth ) {
1322
+
1323
+ newLeft = windowWidth + halfWidth - panelWidth;
1324
+
1325
+ }
1326
+
1327
+ // Allow window to extend half its width beyond left edge
1328
+ if ( newLeft < - halfWidth ) {
1329
+
1330
+ newLeft = - halfWidth;
1331
+
1332
+ }
1333
+
1334
+ // Allow window to extend half its height beyond bottom edge
1335
+ if ( newTop + panelHeight > windowHeight + halfHeight ) {
1336
+
1337
+ newTop = windowHeight + halfHeight - panelHeight;
1338
+
1339
+ }
1340
+
1341
+ // Allow window to extend half its height beyond top edge
1342
+ if ( newTop < - halfHeight ) {
1343
+
1344
+ newTop = - halfHeight;
1345
+
1346
+ }
1347
+
1348
+ windowPanel.style.left = `${ newLeft }px`;
1349
+ windowPanel.style.top = `${ newTop }px`;
1350
+
1351
+ // Check if cursor is over the inspector panel
1352
+ const panelRect = this.panel.getBoundingClientRect();
1353
+ const isOverPanel = currentX >= panelRect.left && currentX <= panelRect.right &&
1354
+ currentY >= panelRect.top && currentY <= panelRect.bottom;
1355
+
1356
+ if ( isOverPanel ) {
1357
+
1358
+ windowPanel.style.opacity = '0.5';
1359
+ this.panel.style.outline = '2px solid var(--accent-color)';
1360
+
1361
+ } else {
1362
+
1363
+ windowPanel.style.opacity = '';
1364
+ this.panel.style.outline = '';
1365
+
1366
+ }
1367
+
1368
+ };
1369
+
1370
+ const onDragEnd = ( e ) => {
1371
+
1372
+ if ( ! isDragging ) return;
1373
+
1374
+ isDragging = false;
1375
+ header.style.cursor = '';
1376
+ windowPanel.style.opacity = '';
1377
+ this.panel.style.outline = '';
1378
+
1379
+ // Check if dropped over the inspector panel
1380
+ const currentX = e.clientX;
1381
+ const currentY = e.clientY;
1382
+
1383
+ if ( currentX !== undefined && currentY !== undefined ) {
1384
+
1385
+ const panelRect = this.panel.getBoundingClientRect();
1386
+ const isOverPanel = currentX >= panelRect.left && currentX <= panelRect.right &&
1387
+ currentY >= panelRect.top && currentY <= panelRect.bottom;
1388
+
1389
+ if ( isOverPanel && tab ) {
1390
+
1391
+ // Reattach the tab
1392
+ this.reattachTab( tab );
1393
+
1394
+ } else {
1395
+
1396
+ // Save layout after moving detached window
1397
+ this.saveLayout();
1398
+
1399
+ }
1400
+
1401
+ }
1402
+
1403
+ header.removeEventListener( 'pointermove', onDragMove );
1404
+ header.removeEventListener( 'pointerup', onDragEnd );
1405
+ header.removeEventListener( 'pointercancel', onDragEnd );
1406
+
1407
+ };
1408
+
1409
+ header.addEventListener( 'pointerdown', ( e ) => {
1410
+
1411
+ onDragStart( e );
1412
+ header.addEventListener( 'pointermove', onDragMove );
1413
+ header.addEventListener( 'pointerup', onDragEnd );
1414
+ header.addEventListener( 'pointercancel', onDragEnd );
1415
+
1416
+ } );
1417
+
1418
+ header.style.cursor = 'grab';
1419
+
1420
+ }
1421
+
1422
+ setupDetachedWindowResize( windowPanel, resizerTop, resizerRight, resizerBottom, resizerLeft, resizerCorner ) {
1423
+
1424
+ const minWidth = 250;
1425
+ const minHeight = 150;
1426
+
1427
+ const setupResizer = ( resizer, direction ) => {
1428
+
1429
+ let isResizing = false;
1430
+ let startX, startY, startWidth, startHeight, startLeft, startTop;
1431
+
1432
+ const onResizeStart = ( e ) => {
1433
+
1434
+ e.preventDefault();
1435
+ e.stopPropagation();
1436
+ isResizing = true;
1437
+
1438
+ // Bring window to front when resizing
1439
+ this.bringWindowToFront( windowPanel );
1440
+
1441
+ resizer.setPointerCapture( e.pointerId );
1442
+
1443
+ startX = e.clientX;
1444
+ startY = e.clientY;
1445
+ startWidth = windowPanel.offsetWidth;
1446
+ startHeight = windowPanel.offsetHeight;
1447
+ startLeft = windowPanel.offsetLeft;
1448
+ startTop = windowPanel.offsetTop;
1449
+
1450
+ };
1451
+
1452
+ const onResizeMove = ( e ) => {
1453
+
1454
+ if ( ! isResizing ) return;
1455
+
1456
+ e.preventDefault();
1457
+
1458
+ const currentX = e.clientX;
1459
+ const currentY = e.clientY;
1460
+
1461
+ const deltaX = currentX - startX;
1462
+ const deltaY = currentY - startY;
1463
+
1464
+ const windowWidth = window.innerWidth;
1465
+ const windowHeight = window.innerHeight;
1466
+
1467
+ if ( direction === 'right' || direction === 'corner' ) {
1468
+
1469
+ const newWidth = startWidth + deltaX;
1470
+ const maxWidth = windowWidth - startLeft;
1471
+
1472
+ if ( newWidth >= minWidth && newWidth <= maxWidth ) {
1473
+
1474
+ windowPanel.style.width = `${ newWidth }px`;
1475
+
1476
+ }
1477
+
1478
+ }
1479
+
1480
+ if ( direction === 'bottom' || direction === 'corner' ) {
1481
+
1482
+ const newHeight = startHeight + deltaY;
1483
+ const maxHeight = windowHeight - startTop;
1484
+
1485
+ if ( newHeight >= minHeight && newHeight <= maxHeight ) {
1486
+
1487
+ windowPanel.style.height = `${ newHeight }px`;
1488
+
1489
+ }
1490
+
1491
+ }
1492
+
1493
+ if ( direction === 'left' ) {
1494
+
1495
+ const newWidth = startWidth - deltaX;
1496
+ const maxLeft = startLeft + startWidth - minWidth;
1497
+
1498
+ if ( newWidth >= minWidth ) {
1499
+
1500
+ const newLeft = startLeft + deltaX;
1501
+
1502
+ if ( newLeft >= 0 && newLeft <= maxLeft ) {
1503
+
1504
+ windowPanel.style.width = `${ newWidth }px`;
1505
+ windowPanel.style.left = `${ newLeft }px`;
1506
+
1507
+ }
1508
+
1509
+ }
1510
+
1511
+ }
1512
+
1513
+ if ( direction === 'top' ) {
1514
+
1515
+ const newHeight = startHeight - deltaY;
1516
+ const maxTop = startTop + startHeight - minHeight;
1517
+
1518
+ if ( newHeight >= minHeight ) {
1519
+
1520
+ const newTop = startTop + deltaY;
1521
+
1522
+ if ( newTop >= 0 && newTop <= maxTop ) {
1523
+
1524
+ windowPanel.style.height = `${ newHeight }px`;
1525
+ windowPanel.style.top = `${ newTop }px`;
1526
+
1527
+ }
1528
+
1529
+ }
1530
+
1531
+ }
1532
+
1533
+ this.dispatchEvent( { type: 'resize' } );
1534
+
1535
+ };
1536
+
1537
+ const onResizeEnd = () => {
1538
+
1539
+ isResizing = false;
1540
+
1541
+ resizer.removeEventListener( 'pointermove', onResizeMove );
1542
+ resizer.removeEventListener( 'pointerup', onResizeEnd );
1543
+ resizer.removeEventListener( 'pointercancel', onResizeEnd );
1544
+
1545
+ // Save layout after resizing detached window
1546
+ this.saveLayout();
1547
+
1548
+ };
1549
+
1550
+ resizer.addEventListener( 'pointerdown', ( e ) => {
1551
+
1552
+ onResizeStart( e );
1553
+ resizer.addEventListener( 'pointermove', onResizeMove );
1554
+ resizer.addEventListener( 'pointerup', onResizeEnd );
1555
+ resizer.addEventListener( 'pointercancel', onResizeEnd );
1556
+
1557
+ } );
1558
+
1559
+ };
1560
+
1561
+ // Setup all resizers
1562
+ setupResizer( resizerTop, 'top' );
1563
+ setupResizer( resizerRight, 'right' );
1564
+ setupResizer( resizerBottom, 'bottom' );
1565
+ setupResizer( resizerLeft, 'left' );
1566
+ setupResizer( resizerCorner, 'corner' );
1567
+
1568
+ }
1569
+
1570
+ reattachTab( tab ) {
1571
+
1572
+ if ( ! tab.isDetached ) return;
1573
+
1574
+ if ( tab.detachedWindow ) {
1575
+
1576
+ const index = this.detachedWindows.indexOf( tab.detachedWindow );
1577
+
1578
+ if ( index > - 1 ) {
1579
+
1580
+ this.detachedWindows.splice( index, 1 );
1581
+
1582
+ }
1583
+
1584
+ if ( tab.detachedWindow.panel.parentNode ) {
1585
+
1586
+ tab.detachedWindow.panel.parentNode.removeChild( tab.detachedWindow.panel );
1587
+
1588
+ }
1589
+
1590
+ tab.detachedWindow = null;
1591
+
1592
+ }
1593
+
1594
+ tab.isDetached = false;
1595
+
1596
+ // Get all tabs and sort by their original index to determine the correct order
1597
+ const allTabs = Object.values( this.tabs );
1598
+ const allTabsSorted = allTabs
1599
+ .filter( t => t.originalIndex !== undefined && t.isVisible )
1600
+ .sort( ( a, b ) => a.originalIndex - b.originalIndex );
1601
+
1602
+ // Get currently attached tab buttons
1603
+ const currentButtons = Array.from( this.tabsContainer.children );
1604
+
1605
+ // Find the correct position for this tab
1606
+ let insertIndex = 0;
1607
+ for ( const t of allTabsSorted ) {
1608
+
1609
+ if ( t.id === tab.id ) {
1610
+
1611
+ break;
1612
+
1613
+ }
1614
+
1615
+ // Count only non-detached, non-builtin tabs that come before this one
1616
+ if ( ! t.isDetached && ! t.builtin ) {
1617
+
1618
+ insertIndex ++;
1619
+
1620
+ }
1621
+
1622
+ }
1623
+
1624
+ // Insert the button at the correct position
1625
+ if ( insertIndex >= currentButtons.length || currentButtons.length === 0 ) {
1626
+
1627
+ // If insert index is beyond current buttons, or no buttons exist, append at the end
1628
+ this.tabsContainer.appendChild( tab.button );
1629
+
1630
+ } else {
1631
+
1632
+ // Insert before the button at the insert index
1633
+ this.tabsContainer.insertBefore( tab.button, currentButtons[ insertIndex ] );
1634
+
1635
+ }
1636
+
1637
+ this.contentWrapper.appendChild( tab.content );
1638
+
1639
+ this.setActiveTab( tab.id );
1640
+
1641
+ // Update panel size after reattaching
1642
+ this.updatePanelSize();
1643
+
1644
+ this.saveLayout();
1645
+
1646
+ }
1647
+
1648
+ setActiveTab( id ) {
1649
+
1650
+ if ( this.activeTabId && this.tabs[ this.activeTabId ] && ! this.tabs[ this.activeTabId ].isDetached ) {
1651
+
1652
+ this.tabs[ this.activeTabId ].setActive( false );
1653
+
1654
+ }
1655
+
1656
+ this.activeTabId = id;
1657
+
1658
+ if ( this.tabs[ id ] ) {
1659
+
1660
+ const tab = this.tabs[ id ];
1661
+
1662
+ if ( ! tab.isVisible ) {
1663
+
1664
+ tab.show();
1665
+
1666
+ }
1667
+
1668
+ tab.setActive( true );
1669
+
1670
+ }
1671
+
1672
+ this.saveLayout();
1673
+ this.checkHeaderScroll();
1674
+
1675
+ }
1676
+
1677
+ togglePanel() {
1678
+
1679
+ this.panel.classList.toggle( 'visible' );
1680
+ this.toggleButton.classList.toggle( 'panel-open' );
1681
+ this.miniPanel.classList.toggle( 'panel-open' );
1682
+
1683
+ const isVisible = this.panel.classList.contains( 'visible' );
1684
+
1685
+ if ( isVisible && this.activeTabId && this.tabs[ this.activeTabId ] ) {
1686
+
1687
+ this.tabs[ this.activeTabId ].setActive( true );
1688
+
1689
+ }
1690
+
1691
+ this.dispatchEvent( { type: 'resize' } );
1692
+
1693
+ this.saveLayout();
1694
+
1695
+ }
1696
+
1697
+ togglePosition() {
1698
+
1699
+ const newPosition = this.position === 'bottom' ? 'right' : 'bottom';
1700
+ this.setPosition( newPosition );
1701
+
1702
+ }
1703
+
1704
+ setPosition( targetPosition ) {
1705
+
1706
+ if ( this.position === targetPosition ) return;
1707
+
1708
+ this.panel.style.transition = 'none';
1709
+
1710
+ // Check if panel is currently maximized
1711
+ const isMaximized = this.panel.classList.contains( 'maximized' );
1712
+
1713
+ if ( targetPosition === 'right' ) {
1714
+
1715
+ this.position = 'right';
1716
+ this.floatingBtn.classList.add( 'active' );
1717
+ this.floatingBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><path d="M3 15h18"></path></svg>';
1718
+ this.floatingBtn.title = 'Switch to Bottom';
1719
+
1720
+ // Apply right position styles
1721
+ this.panel.classList.remove( 'position-bottom' );
1722
+ this.panel.classList.add( 'position-right' );
1723
+ this.toggleButton.classList.add( 'position-right' );
1724
+ this.miniPanel.classList.add( 'position-right' );
1725
+ this.panel.style.bottom = '';
1726
+ this.panel.style.top = '0';
1727
+ this.panel.style.right = '0';
1728
+ this.panel.style.left = '';
1729
+
1730
+ // Apply size based on maximized state
1731
+ if ( isMaximized ) {
1732
+
1733
+ this.panel.style.width = '100vw';
1734
+ this.panel.style.height = '100%';
1735
+
1736
+ } else {
1737
+
1738
+ this.panel.style.width = `${ this.lastWidthRight }px`;
1739
+ this.panel.style.height = '100%';
1740
+
1741
+ }
1742
+
1743
+ } else {
1744
+
1745
+ this.position = 'bottom';
1746
+ this.floatingBtn.classList.remove( 'active' );
1747
+ this.floatingBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="15" y1="3" x2="15" y2="21"></line></svg>';
1748
+ this.floatingBtn.title = 'Switch to Right Side';
1749
+
1750
+ // Apply bottom position styles
1751
+ this.panel.classList.remove( 'position-right' );
1752
+ this.panel.classList.add( 'position-bottom' );
1753
+ this.toggleButton.classList.remove( 'position-right' );
1754
+ this.miniPanel.classList.remove( 'position-right' );
1755
+ this.panel.style.top = '';
1756
+ this.panel.style.right = '';
1757
+ this.panel.style.bottom = '0';
1758
+ this.panel.style.left = '0';
1759
+
1760
+ // Apply size based on maximized state
1761
+ if ( isMaximized ) {
1762
+
1763
+ this.panel.style.width = '100%';
1764
+ this.panel.style.height = '100vh';
1765
+
1766
+ } else {
1767
+
1768
+ this.panel.style.width = '100%';
1769
+ this.panel.style.height = `${ this.lastHeightBottom }px`;
1770
+
1771
+ }
1772
+
1773
+ }
1774
+
1775
+ // Re-enable transition after a brief delay
1776
+ setTimeout( () => {
1777
+
1778
+ this.panel.style.transition = '';
1779
+
1780
+ }, 50 );
1781
+
1782
+ // Update panel size based on visible tabs
1783
+ this.updatePanelSize();
1784
+
1785
+ // Save layout after position change
1786
+ this.saveLayout();
1787
+
1788
+ }
1789
+
1790
+ saveLayout() {
1791
+
1792
+ if ( this.isLoadingLayout ) return;
1793
+
1794
+ const layout = {
1795
+ position: this.position,
1796
+ lastHeightBottom: this.lastHeightBottom,
1797
+ lastWidthRight: this.lastWidthRight,
1798
+ activeTabId: this.activeTabId,
1799
+ detachedTabs: [],
1800
+ isVisible: this.panel.classList.contains( 'visible' )
1801
+ };
1802
+
1803
+ // Save detached windows state
1804
+ this.detachedWindows.forEach( detachedWindow => {
1805
+
1806
+ const tab = detachedWindow.tab;
1807
+ const panel = detachedWindow.panel;
1808
+
1809
+ // Get position values, ensuring they're valid numbers
1810
+ const left = parseFloat( panel.style.left ) || panel.offsetLeft || 0;
1811
+ const top = parseFloat( panel.style.top ) || panel.offsetTop || 0;
1812
+ const width = panel.offsetWidth;
1813
+ const height = panel.offsetHeight;
1814
+
1815
+ layout.detachedTabs.push( {
1816
+ tabId: tab.id,
1817
+ originalIndex: tab.originalIndex !== undefined ? tab.originalIndex : 0,
1818
+ left: left,
1819
+ top: top,
1820
+ width: width,
1821
+ height: height
1822
+ } );
1823
+
1824
+ } );
1825
+
1826
+ try {
1827
+
1828
+ setItem( 'layout', layout );
1829
+
1830
+ } catch ( e ) {
1831
+
1832
+ console.warn( 'Failed to save profiler layout:', e );
1833
+
1834
+ }
1835
+
1836
+ }
1837
+
1838
+ loadLayout() {
1839
+
1840
+ this.isLoadingLayout = true;
1841
+
1842
+ try {
1843
+
1844
+ const layout = getItem( 'layout' );
1845
+
1846
+ if ( Object.keys( layout ).length === 0 ) return;
1847
+
1848
+ // Constrain detached tabs positions to current screen bounds
1849
+ if ( layout.detachedTabs && layout.detachedTabs.length > 0 ) {
1850
+
1851
+ const windowWidth = window.innerWidth;
1852
+ const windowHeight = window.innerHeight;
1853
+
1854
+ layout.detachedTabs = layout.detachedTabs.map( detachedTabData => {
1855
+
1856
+ let { left, top, width, height } = detachedTabData;
1857
+
1858
+ // Ensure width and height are within bounds
1859
+ if ( width > windowWidth ) {
1860
+
1861
+ width = windowWidth - 100; // Leave some margin
1862
+
1863
+ }
1864
+
1865
+ if ( height > windowHeight ) {
1866
+
1867
+ height = windowHeight - 100; // Leave some margin
1868
+
1869
+ }
1870
+
1871
+ // Allow window to extend half its width/height outside the screen
1872
+ const halfWidth = width / 2;
1873
+ const halfHeight = height / 2;
1874
+
1875
+ // Constrain horizontal position (allow half width to extend beyond right edge)
1876
+ if ( left + width > windowWidth + halfWidth ) {
1877
+
1878
+ left = windowWidth + halfWidth - width;
1879
+
1880
+ }
1881
+
1882
+ // Constrain horizontal position (allow half width to extend beyond left edge)
1883
+ if ( left < - halfWidth ) {
1884
+
1885
+ left = - halfWidth;
1886
+
1887
+ }
1888
+
1889
+ // Constrain vertical position (allow half height to extend beyond bottom edge)
1890
+ if ( top + height > windowHeight + halfHeight ) {
1891
+
1892
+ top = windowHeight + halfHeight - height;
1893
+
1894
+ }
1895
+
1896
+ // Constrain vertical position (allow half height to extend beyond top edge)
1897
+ if ( top < - halfHeight ) {
1898
+
1899
+ top = - halfHeight;
1900
+
1901
+ }
1902
+
1903
+ return {
1904
+ ...detachedTabData,
1905
+ left,
1906
+ top,
1907
+ width,
1908
+ height
1909
+ };
1910
+
1911
+ } );
1912
+
1913
+ }
1914
+
1915
+ // Restore position and dimensions
1916
+ if ( layout.position ) {
1917
+
1918
+ this.position = layout.position;
1919
+
1920
+ }
1921
+
1922
+ if ( layout.lastHeightBottom ) {
1923
+
1924
+ this.lastHeightBottom = layout.lastHeightBottom;
1925
+
1926
+ }
1927
+
1928
+ if ( layout.lastWidthRight ) {
1929
+
1930
+ this.lastWidthRight = layout.lastWidthRight;
1931
+
1932
+ }
1933
+
1934
+ // Constrain saved dimensions to current screen bounds
1935
+ const windowWidth = window.innerWidth;
1936
+ const windowHeight = window.innerHeight;
1937
+
1938
+ if ( this.lastHeightBottom > windowHeight - 50 ) {
1939
+
1940
+ this.lastHeightBottom = windowHeight - 50;
1941
+
1942
+ }
1943
+
1944
+ if ( this.lastWidthRight > windowWidth - 50 ) {
1945
+
1946
+ this.lastWidthRight = windowWidth - 50;
1947
+
1948
+ }
1949
+
1950
+ // Apply the saved position after shell is set up
1951
+ if ( this.position === 'right' ) {
1952
+
1953
+ this.floatingBtn.classList.add( 'active' );
1954
+ this.floatingBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><path d="M3 15h18"></path></svg>';
1955
+ this.floatingBtn.title = 'Switch to Bottom';
1956
+
1957
+ this.panel.classList.remove( 'position-bottom' );
1958
+ this.panel.classList.add( 'position-right' );
1959
+ this.toggleButton.classList.add( 'position-right' );
1960
+ this.miniPanel.classList.add( 'position-right' );
1961
+ this.panel.style.bottom = '';
1962
+ this.panel.style.top = '0';
1963
+ this.panel.style.right = '0';
1964
+ this.panel.style.left = '';
1965
+ this.panel.style.width = `${ this.lastWidthRight }px`;
1966
+ this.panel.style.height = '100%';
1967
+
1968
+ } else {
1969
+
1970
+ this.panel.style.height = `${ this.lastHeightBottom }px`;
1971
+
1972
+ }
1973
+
1974
+ if ( layout.isVisible ) {
1975
+
1976
+ this.panel.classList.add( 'visible' );
1977
+ this.toggleButton.classList.add( 'panel-open' );
1978
+
1979
+ }
1980
+
1981
+ if ( layout.activeTabId ) {
1982
+
1983
+ this.setActiveTab( layout.activeTabId );
1984
+
1985
+ }
1986
+
1987
+ if ( layout.detachedTabs && layout.detachedTabs.length > 0 ) {
1988
+
1989
+ this.pendingDetachedTabs = layout.detachedTabs;
1990
+ this.restoreDetachedTabs();
1991
+
1992
+ }
1993
+
1994
+ // Update panel size after loading layout
1995
+ this.updatePanelSize();
1996
+
1997
+ // Ensure initial open state applies to mini panel as well
1998
+ if ( this.panel.classList.contains( 'visible' ) ) {
1999
+
2000
+ this.miniPanel.classList.add( 'panel-open' );
2001
+
2002
+ }
2003
+
2004
+ } catch ( e ) {
2005
+
2006
+ console.warn( 'Failed to load profiler layout:', e );
2007
+
2008
+ } finally {
2009
+
2010
+ this.isLoadingLayout = false;
2011
+
2012
+ }
2013
+
2014
+ }
2015
+
2016
+ restoreDetachedTabs() {
2017
+
2018
+ if ( ! this.pendingDetachedTabs || this.pendingDetachedTabs.length === 0 ) return;
2019
+
2020
+ this.pendingDetachedTabs.forEach( detachedTabData => {
2021
+
2022
+ const tab = this.tabs[ detachedTabData.tabId ];
2023
+
2024
+ if ( ! tab || tab.isDetached ) return;
2025
+
2026
+ // Restore originalIndex if saved
2027
+ if ( detachedTabData.originalIndex !== undefined ) {
2028
+
2029
+ tab.originalIndex = detachedTabData.originalIndex;
2030
+
2031
+ }
2032
+
2033
+ if ( tab.button.parentNode ) {
2034
+
2035
+ tab.button.parentNode.removeChild( tab.button );
2036
+
2037
+ }
2038
+
2039
+ if ( tab.content.parentNode ) {
2040
+
2041
+ tab.content.parentNode.removeChild( tab.content );
2042
+
2043
+ }
2044
+
2045
+ const detachedWindow = this.createDetachedWindow( tab, 0, 0 );
2046
+
2047
+ detachedWindow.panel.style.left = `${ detachedTabData.left }px`;
2048
+ detachedWindow.panel.style.top = `${ detachedTabData.top }px`;
2049
+ detachedWindow.panel.style.width = `${ detachedTabData.width }px`;
2050
+ detachedWindow.panel.style.height = `${ detachedTabData.height }px`;
2051
+
2052
+ // Constrain window to bounds after restoring position and size
2053
+ this.constrainWindowToBounds( detachedWindow.panel );
2054
+
2055
+ this.detachedWindows.push( detachedWindow );
2056
+
2057
+ tab.isDetached = true;
2058
+ tab.detachedWindow = detachedWindow;
2059
+
2060
+ } );
2061
+
2062
+ this.pendingDetachedTabs = null;
2063
+
2064
+ // Update maxZIndex to be higher than all existing windows
2065
+ this.detachedWindows.forEach( detachedWindow => {
2066
+
2067
+ const currentZIndex = parseInt( getComputedStyle( detachedWindow.panel ).zIndex ) || 0;
2068
+ if ( currentZIndex > this.maxZIndex ) {
2069
+
2070
+ this.maxZIndex = currentZIndex;
2071
+
2072
+ }
2073
+
2074
+ } );
2075
+
2076
+ const needsNewActiveTab = ! this.activeTabId ||
2077
+ ! this.tabs[ this.activeTabId ] ||
2078
+ this.tabs[ this.activeTabId ].isDetached ||
2079
+ ! this.tabs[ this.activeTabId ].isVisible;
2080
+
2081
+ if ( needsNewActiveTab ) {
2082
+
2083
+ const tabIds = Object.keys( this.tabs );
2084
+ const availableTabs = tabIds.filter( id =>
2085
+ ! this.tabs[ id ].isDetached &&
2086
+ this.tabs[ id ].isVisible
2087
+ );
2088
+
2089
+ if ( availableTabs.length > 0 ) {
2090
+
2091
+ const buttons = Array.from( this.tabsContainer.children );
2092
+ const orderedTabIds = buttons.map( btn => {
2093
+
2094
+ return Object.keys( this.tabs ).find( id => this.tabs[ id ].button === btn );
2095
+
2096
+ } ).filter( id =>
2097
+ id !== undefined &&
2098
+ ! this.tabs[ id ].isDetached &&
2099
+ this.tabs[ id ].isVisible
2100
+ );
2101
+
2102
+ this.setActiveTab( orderedTabIds[ 0 ] || availableTabs[ 0 ] );
2103
+
2104
+ } else {
2105
+
2106
+ this.activeTabId = null;
2107
+
2108
+ }
2109
+
2110
+ }
2111
+
2112
+ // Update panel size after restoring detached tabs
2113
+ this.updatePanelSize();
167
2114
 
168
2115
  }
169
2116