@babylonjs/inspector 8.49.2 → 8.49.4

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.
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import { createContext, forwardRef, useContext, useState, useCallback, Component, useMemo, useEffect, useRef, isValidElement, cloneElement, Children, useLayoutEffect, useImperativeHandle, createElement, Suspense, memo, Fragment as Fragment$1, useReducer, lazy } from 'react';
3
- import { tokens, makeStyles, Button as Button$1, Spinner, Link as Link$1, Caption1, Body1, Tooltip as Tooltip$1, ToggleButton as ToggleButton$1, InfoLabel as InfoLabel$1, Body1Strong, Checkbox as Checkbox$1, mergeClasses, Accordion as Accordion$1, AccordionItem, AccordionHeader, Subtitle2Stronger, AccordionPanel, Divider, createLightTheme, createDarkTheme, FluentProvider, TeachingPopover, TeachingPopoverSurface, TeachingPopoverHeader, TeachingPopoverBody, Portal, RendererProvider, createDOMRenderer, Menu, MenuTrigger, SplitButton, MenuPopover, MenuList, MenuItem, Toolbar as Toolbar$1, ToolbarRadioButton, MenuGroup, MenuGroupHeader, treeItemLevelToken, SearchBox as SearchBox$1, FlatTree, FlatTreeItem, TreeItemLayout, MenuDivider, MenuItemCheckbox, Switch as Switch$1, useId, SpinButton as SpinButton$1, Input, Dropdown as Dropdown$1, Option, Popover as Popover$1, PopoverTrigger, PopoverSurface, ColorPicker, ColorArea, ColorSlider, AlphaSlider, ColorSwatch, PresenceBadge, Slider, MenuItemRadio, Dialog, DialogSurface, DialogBody, DialogTitle, DialogContent, DialogActions, List as List$1, ListItem, Badge, Label, webDarkTheme, MessageBar as MessageBar$1, MessageBarBody, MessageBarTitle, Subtitle2, useComboboxFilter, Combobox, Textarea as Textarea$1, ToolbarButton, ToolbarDivider, Field } from '@fluentui/react-components';
4
- import { ErrorCircleRegular, ChevronCircleRight16Regular, ChevronCircleRight20Regular, ChevronCircleDown16Regular, ChevronCircleDown20Regular, Copy16Regular, CopyRegular, PanelLeftExpandRegular, PanelRightExpandRegular, PanelLeftContractRegular, PanelRightContractRegular, PictureInPictureEnterRegular, MoreHorizontalRegular, LayoutColumnTwoFocusLeftFilled, LayoutColumnTwoSplitLeftFocusTopLeftFilled, LayoutColumnTwoSplitLeftFocusBottomLeftFilled, LayoutColumnTwoFocusRightFilled, LayoutColumnTwoSplitRightFocusTopRightFilled, LayoutColumnTwoSplitRightFocusBottomRightFilled, DocumentTextRegular, createFluentIcon, FilterRegular, TextSortAscendingRegular, GlobeRegular, ArrowExpandAllRegular, ArrowCollapseAllRegular, CubeTreeRegular, BugRegular, SettingsRegular, ArrowUploadRegular, ArrowDownloadRegular, StopRegular, RecordRegular, DataBarHorizontalRegular, WrenchRegular, WeatherSunnyRegular, WeatherMoonRegular, ArrowRotateClockwiseRegular, ArrowExpandRegular, SelectObjectRegular, CubeRegular, AddRegular, DeleteRegular, FullScreenMaximizeRegular, ArrowMinimizeRegular, LineHorizontal1Regular, ChevronDoubleLeftRegular, ChevronDoubleRightRegular, StepsRegular, ChevronDownRegular, ChevronRightRegular, CircleSmallFilled, SaveRegular, PreviousRegular, ArrowPreviousRegular, TriangleLeftRegular, RecordStopRegular, PlayRegular, ArrowNextRegular, NextRegular, EditRegular, LinkDismissRegular, LinkEditRegular, ArrowUndoRegular, BracesRegular, BracesDismiss16Regular, EyeRegular, CloudArrowUpRegular, CloudArrowDownRegular, EyeOffFilled, EyeFilled, ArrowMoveFilled, StopFilled, PlayFilled, EyeOffRegular, LockOpenRegular, LockClosedRegular, ResizeRegular, ChevronUpRegular, ArrowResetRegular, CircleHalfFillRegular, EyedropperRegular, PaintBucketRegular, InkStrokeRegular, StackRegular, FilmstripRegular, PauseFilled, WeatherSunnyLowFilled, LayerRegular, FrameRegular, AppGenericRegular, RectangleLandscapeRegular, BorderOutsideRegular, BorderNoneRegular, MyLocationRegular, CameraRegular, LightbulbRegular, VideoFilled, VideoRegular, FlashlightRegular, FlashlightOffRegular, DropRegular, BlurRegular, PipelineRegular, PersonWalkingRegular, DataLineRegular, PersonSquareRegular, LayerDiagonalPersonRegular, ImageEditRegular, ImageRegular, TargetRegular, PersonFeedbackRegular, BranchRegular, DeleteFilled } from '@fluentui/react-icons';
3
+ import { tokens, makeStyles, Tooltip as Tooltip$1, Button as Button$1, Spinner, Link as Link$1, Caption1, Body1, ToggleButton as ToggleButton$1, InfoLabel as InfoLabel$1, Body1Strong, Checkbox as Checkbox$1, mergeClasses, Accordion as Accordion$1, AccordionItem, AccordionHeader, Subtitle2Stronger, AccordionPanel, Divider, createLightTheme, createDarkTheme, FluentProvider, TeachingPopover, TeachingPopoverSurface, TeachingPopoverHeader, TeachingPopoverBody, Portal, RendererProvider, createDOMRenderer, Menu, MenuTrigger, SplitButton, MenuPopover, MenuList, MenuItem, Toolbar as Toolbar$1, ToolbarRadioButton, MenuGroup, MenuGroupHeader, treeItemLevelToken, SearchBox as SearchBox$1, FlatTree, FlatTreeItem, TreeItemLayout, MenuDivider, MenuItemCheckbox, Switch as Switch$1, useId, SpinButton as SpinButton$1, Input, Dropdown as Dropdown$1, Option, Popover as Popover$1, PopoverTrigger, PopoverSurface, ColorPicker, ColorArea, ColorSlider, AlphaSlider, ColorSwatch, PresenceBadge, Slider, MenuItemRadio, Dialog, DialogSurface, DialogBody, DialogTitle, DialogContent, DialogActions, List as List$1, ListItem, Badge, Label, MessageBar as MessageBar$1, MessageBarBody, MessageBarTitle, Subtitle2, useComboboxFilter, Combobox, Textarea as Textarea$1, ToolbarButton, ToolbarDivider, Field } from '@fluentui/react-components';
4
+ import { ErrorCircleRegular, ChevronCircleRight16Regular, ChevronCircleRight20Regular, ChevronCircleDown16Regular, ChevronCircleDown20Regular, Copy16Regular, CopyRegular, PanelLeftExpandRegular, PanelRightExpandRegular, PanelLeftContractRegular, PanelRightContractRegular, PictureInPictureEnterRegular, MoreHorizontalRegular, LayoutColumnTwoFocusLeftFilled, LayoutColumnTwoSplitLeftFocusTopLeftFilled, LayoutColumnTwoSplitLeftFocusBottomLeftFilled, LayoutColumnTwoFocusRightFilled, LayoutColumnTwoSplitRightFocusTopRightFilled, LayoutColumnTwoSplitRightFocusBottomRightFilled, DocumentTextRegular, createFluentIcon, FilterRegular, TextSortAscendingRegular, GlobeRegular, ArrowExpandAllRegular, ArrowCollapseAllRegular, CubeTreeRegular, BugRegular, SettingsRegular, ArrowUploadRegular, ArrowDownloadRegular, StopRegular, RecordRegular, DataBarHorizontalRegular, WrenchRegular, WeatherSunnyRegular, WeatherMoonRegular, ArrowRotateClockwiseRegular, ArrowExpandRegular, SelectObjectRegular, CubeRegular, AddRegular, DeleteRegular, FullScreenMaximizeRegular, ChevronDownRegular, ChevronRightRegular, CircleSmallFilled, SaveRegular, PreviousRegular, ArrowPreviousRegular, TriangleLeftRegular, RecordStopRegular, PlayRegular, ArrowNextRegular, NextRegular, EditRegular, PauseRegular, LinkDismissRegular, LinkEditRegular, ArrowUndoRegular, BracesRegular, BracesDismiss16Regular, EyeRegular, CloudArrowUpRegular, CloudArrowDownRegular, EyeOffFilled, EyeFilled, ArrowMoveFilled, StopFilled, PlayFilled, EyeOffRegular, LockOpenRegular, LockClosedRegular, ResizeRegular, ChevronUpRegular, ArrowResetRegular, CircleHalfFillRegular, EyedropperRegular, PaintBucketRegular, InkStrokeRegular, StackRegular, FilmstripRegular, PauseFilled, WeatherSunnyLowFilled, LayerRegular, FrameRegular, AppGenericRegular, RectangleLandscapeRegular, BorderOutsideRegular, BorderNoneRegular, MyLocationRegular, CameraRegular, LightbulbRegular, VideoFilled, VideoRegular, FlashlightRegular, FlashlightOffRegular, DropRegular, BlurRegular, PipelineRegular, PersonWalkingRegular, DataLineRegular, SoundWaveCircleRegular, PersonSquareRegular, LayerDiagonalPersonRegular, ImageEditRegular, ImageRegular, TargetRegular, PersonFeedbackRegular, BranchRegular, DeleteFilled } from '@fluentui/react-icons';
5
5
  import { Color3, Color4 } from '@babylonjs/core/Maths/math.color.js';
6
6
  import { Vector3, Quaternion, Matrix, Vector2, Vector4, TmpVectors } from '@babylonjs/core/Maths/math.vector.js';
7
7
  import { Observable } from '@babylonjs/core/Misc/observable.js';
@@ -13,6 +13,7 @@ import { Deferred } from '@babylonjs/core/Misc/deferred.js';
13
13
  import { Logger } from '@babylonjs/core/Misc/logger.js';
14
14
  import { Clamp } from '@babylonjs/core/Maths/math.scalar.functions.js';
15
15
  import { VirtualizerScrollView } from '@fluentui-contrib/react-virtualizer';
16
+ import { UniqueIdGenerator } from '@babylonjs/core/Misc/uniqueIdGenerator.js';
16
17
  import { FontAsset } from '@babylonjs/addons/msdfText/fontAsset.js';
17
18
  import { TextRenderer } from '@babylonjs/addons/msdfText/textRenderer.js';
18
19
  import { PhysicsViewer } from '@babylonjs/core/Debug/physicsViewer.js';
@@ -48,7 +49,7 @@ import { Node } from '@babylonjs/core/node.js';
48
49
  import { AnimationGroup, TargetedAnimation } from '@babylonjs/core/Animations/animationGroup.js';
49
50
  import { Animation } from '@babylonjs/core/Animations/animation.js';
50
51
  import { AnimationPropertiesOverride } from '@babylonjs/core/Animations/animationPropertiesOverride.js';
51
- import { Atmosphere } from '@babylonjs/addons/atmosphere/atmosphere.js';
52
+ import { Sound } from '@babylonjs/core/Audio/sound.js';
52
53
  import { ArcRotateCamera } from '@babylonjs/core/Cameras/arcRotateCamera.js';
53
54
  import { FollowCamera } from '@babylonjs/core/Cameras/followCamera.js';
54
55
  import { FreeCamera } from '@babylonjs/core/Cameras/freeCamera.js';
@@ -145,7 +146,6 @@ import { FilesInput } from '@babylonjs/core/Misc/filesInput.js';
145
146
  import { GLTFLoaderAnimationStartMode, GLTFLoaderCoordinateSystemMode, GLTFLoaderDefaultOptions } from '@babylonjs/loaders/glTF/glTFFileLoader.js';
146
147
  import { GLTFValidation } from '@babylonjs/loaders/glTF/glTFValidation.js';
147
148
  import { EngineStore } from '@babylonjs/core/Engines/engineStore.js';
148
- import { UniqueIdGenerator } from '@babylonjs/core/Misc/uniqueIdGenerator.js';
149
149
  import { DebugLayer } from '@babylonjs/core/Debug/debugLayer.js';
150
150
  import { Lazy } from '@babylonjs/core/Misc/lazy.js';
151
151
 
@@ -234,7 +234,14 @@ function ValidateColorHex(val) {
234
234
  return val != "" && HEX_REGEX.test(val);
235
235
  }
236
236
 
237
- // import { Tooltip } from "./tooltip";
237
+ const Tooltip = (props) => {
238
+ const { content, children } = props;
239
+ if (!content) {
240
+ return children;
241
+ }
242
+ return (jsx(Tooltip$1, { relationship: "description", content: content, children: children }));
243
+ };
244
+
238
245
  const useButtonStyles = makeStyles({
239
246
  smallIcon: {
240
247
  fontSize: TokenMap.px16,
@@ -262,12 +269,7 @@ const Button = forwardRef((props, ref) => {
262
269
  }
263
270
  }, [onClick]);
264
271
  const iconClass = size === "small" ? classes.smallIcon : classes.mediumIcon;
265
- return (
266
- // TODO: Somehow wrapping Button in a Tooltip completely breaks rendering CurveEditor. Need to figure out why.
267
- // <Tooltip content={title}>
268
- jsx(Button$1, { ref: ref, title: title, iconPosition: "after", ...buttonProps, className: className, size: size, icon: isOnClickBusy ? jsx(Spinner, { size: "extra-tiny" }) : Icon && jsx(Icon, { className: iconClass }), onClick: handleOnClick, disabled: disabled || isOnClickBusy, children: label && props.label })
269
- // </Tooltip>
270
- );
272
+ return (jsx(Tooltip, { content: title ?? "", children: jsx(Button$1, { ref: ref, iconPosition: "after", ...buttonProps, className: className, size: size, icon: isOnClickBusy ? jsx(Spinner, { size: "extra-tiny" }) : Icon && jsx(Icon, { className: iconClass }), onClick: handleOnClick, disabled: disabled || isOnClickBusy, children: label && props.label }) }));
271
273
  });
272
274
  Button.displayName = "Button";
273
275
 
@@ -365,13 +367,7 @@ function usePropertyChangedNotifier() {
365
367
  }
366
368
 
367
369
  const InterceptorHooksMaps$1 = new WeakMap();
368
- /**
369
- * Intercepts a function on an object and allows you to add hooks that will be called during function execution.
370
- * @param target The object containing the function to intercept.
371
- * @param propertyKey The key of the property that is a function (this is the function that will be intercepted).
372
- * @param hooks The hooks to call during the function execution.
373
- * @returns A disposable that removes the hooks when disposed and returns the object to its original state.
374
- */
370
+ /** @internal */
375
371
  function InterceptFunction(target, propertyKey, hooks) {
376
372
  if (!hooks.afterCall) {
377
373
  throw new Error("At least one hook must be provided.");
@@ -404,7 +400,7 @@ function InterceptFunction(target, propertyKey, hooks) {
404
400
  !Reflect.set(target, propertyKey, (...args) => {
405
401
  const result = Reflect.apply(originalFunction, target, args);
406
402
  for (const { afterCall } of hooksForKey) {
407
- afterCall?.();
403
+ afterCall?.(...args);
408
404
  }
409
405
  return result;
410
406
  })) {
@@ -476,13 +472,7 @@ function IsPropertyReadonly(propertyDescriptor) {
476
472
  return propertyDescriptor.writable === false || (propertyDescriptor.writable === undefined && !propertyDescriptor.set);
477
473
  }
478
474
  const InterceptorHooksMaps = new WeakMap();
479
- /**
480
- * Intercepts a property on an object and allows you to add hooks that will be called when the property is get or set.
481
- * @param target The object containing the property to intercept.
482
- * @param propertyKey The key of the property to intercept.
483
- * @param hooks The hooks to call when the property is get or set.
484
- * @returns A disposable that removes the hooks when disposed and returns the object to its original state.
485
- */
475
+ /** @internal */
486
476
  function InterceptProperty(target, propertyKey, hooks) {
487
477
  // Find the property descriptor and note the owning object (might be inherited through the prototype chain).
488
478
  const ownerAndDescriptor = GetPropertyDescriptor(target, propertyKey);
@@ -535,7 +525,7 @@ function InterceptProperty(target, propertyKey, hooks) {
535
525
  set: (newValue) => {
536
526
  setValue.call(target, newValue);
537
527
  for (const { afterSet } of hooksForKey) {
538
- afterSet?.();
528
+ afterSet?.(newValue);
539
529
  }
540
530
  },
541
531
  })) {
@@ -1057,14 +1047,6 @@ const Link = forwardRef((props, ref) => {
1057
1047
  });
1058
1048
  Link.displayName = "Link";
1059
1049
 
1060
- const Tooltip = (props) => {
1061
- const { content, children } = props;
1062
- if (!content) {
1063
- return children;
1064
- }
1065
- return (jsx(Tooltip$1, { relationship: "description", content: content, children: children }));
1066
- };
1067
-
1068
1050
  const useStyles$S = makeStyles({
1069
1051
  button: {
1070
1052
  display: "flex",
@@ -2983,9 +2965,20 @@ class GraphUtils {
2983
2965
  }
2984
2966
  }
2985
2967
 
2968
+ const SyntheticUniqueIds = new WeakMap();
2969
+ function GetEntityId(entity) {
2970
+ if (entity.uniqueId !== undefined) {
2971
+ return entity.uniqueId;
2972
+ }
2973
+ let id = SyntheticUniqueIds.get(entity);
2974
+ if (!id) {
2975
+ SyntheticUniqueIds.set(entity, (id = UniqueIdGenerator.UniqueId));
2976
+ }
2977
+ return id;
2978
+ }
2986
2979
  function ExpandOrCollapseAll(treeItem, open, openItems) {
2987
2980
  const addOrRemove = open ? openItems.add.bind(openItems) : openItems.delete.bind(openItems);
2988
- TraverseGraph([treeItem], (treeItem) => treeItem.children, (treeItem) => addOrRemove(treeItem.type === "entity" ? treeItem.entity.uniqueId : treeItem.sectionName));
2981
+ TraverseGraph([treeItem], (treeItem) => treeItem.children, (treeItem) => addOrRemove(treeItem.type === "entity" ? GetEntityId(treeItem.entity) : treeItem.sectionName));
2989
2982
  }
2990
2983
  function useCommandContextMenuState(commands) {
2991
2984
  const [checkedContextMenuItems, setCheckedContextMenuItems] = useState({ toggleCommands: [] });
@@ -3240,16 +3233,16 @@ const EntityTreeItem = (props) => {
3240
3233
  }, [inlineCommands]);
3241
3234
  const contextMenuCommands = useMemo(() => commands.filter((command) => command.mode === "contextMenu"), [commands]);
3242
3235
  const [checkedContextMenuItems, onContextMenuCheckedValueChange, contextMenuItems] = useCommandContextMenuState(contextMenuCommands);
3243
- return (jsxs(Menu, { openOnContext: true, checkedValues: checkedContextMenuItems, onCheckedValueChange: onContextMenuCheckedValueChange, children: [jsx(MenuTrigger, { disableButtonEnhancement: true, children: jsx(FlatTreeItem, { className: classes.treeItem, value: entityItem.entity.uniqueId,
3236
+ return (jsxs(Menu, { openOnContext: true, checkedValues: checkedContextMenuItems, onCheckedValueChange: onContextMenuCheckedValueChange, children: [jsx(MenuTrigger, { disableButtonEnhancement: true, children: jsx(FlatTreeItem, { className: classes.treeItem, value: GetEntityId(entityItem.entity),
3244
3237
  // Disable manual expand/collapse when a filter is active.
3245
- itemType: !isFiltering && hasChildren ? "branch" : "leaf", parentValue: entityItem.parent.type === "section" ? entityItem.parent.sectionName : entityItem.entity.uniqueId, "aria-level": entityItem.depth, "aria-setsize": 1, "aria-posinset": 1, onClick: select, style: { [treeItemLevelToken]: entityItem.depth }, children: jsx(TreeItemLayout, { iconBefore: entityItem.icon ? jsx(entityItem.icon, { entity: entityItem.entity }) : null, className: mergeClasses(hasChildren ? classes.treeItemLayoutBranch : classes.treeItemLayoutLeaf, compactMode ? classes.treeItemLayoutCompact : undefined), style: isSelected ? { backgroundColor: tokens.colorNeutralBackground1Selected } : undefined, actions: actions, aside: {
3238
+ itemType: !isFiltering && hasChildren ? "branch" : "leaf", parentValue: entityItem.parent.type === "section" ? entityItem.parent.sectionName : GetEntityId(entityItem.entity), "aria-level": entityItem.depth, "aria-setsize": 1, "aria-posinset": 1, onClick: select, style: { [treeItemLevelToken]: entityItem.depth }, children: jsx(TreeItemLayout, { iconBefore: entityItem.icon ? jsx(entityItem.icon, { entity: entityItem.entity }) : null, className: mergeClasses(hasChildren ? classes.treeItemLayoutBranch : classes.treeItemLayoutLeaf, compactMode ? classes.treeItemLayoutCompact : undefined), style: isSelected ? { backgroundColor: tokens.colorNeutralBackground1Selected } : undefined, actions: actions, aside: {
3246
3239
  // Match the gap and padding of the actions.
3247
3240
  className: classes.treeItemLayoutAside,
3248
3241
  children: aside,
3249
3242
  }, main: {
3250
3243
  // Prevent the "main" content (the Body1 below) from growing too large and pushing the actions/aside out of view.
3251
3244
  className: classes.treeItemLayoutMain,
3252
- }, children: jsx(Body1, { wrap: false, truncate: true, children: name }) }) }, entityItem.entity.uniqueId) }), jsx(MenuPopover, { hidden: !hasChildren && contextMenuCommands.length === 0, children: jsxs(MenuList, { children: [hasChildren && (jsxs(Fragment, { children: [jsx(MenuItem, { icon: jsx(ArrowExpandAllRegular, {}), onClick: expandAll, children: jsx(Body1, { children: "Expand All" }) }), jsx(MenuItem, { icon: jsx(ArrowCollapseAllRegular, {}), onClick: collapseAll, children: jsx(Body1, { children: "Collapse All" }) })] })), hasChildren && contextMenuCommands.length > 0 && jsx(MenuDivider, {}), contextMenuItems] }) })] }));
3245
+ }, children: jsx(Body1, { wrap: false, truncate: true, children: name }) }) }, GetEntityId(entityItem.entity)) }), jsx(MenuPopover, { hidden: !hasChildren && contextMenuCommands.length === 0, children: jsxs(MenuList, { children: [hasChildren && (jsxs(Fragment, { children: [jsx(MenuItem, { icon: jsx(ArrowExpandAllRegular, {}), onClick: expandAll, children: jsx(Body1, { children: "Expand All" }) }), jsx(MenuItem, { icon: jsx(ArrowCollapseAllRegular, {}), onClick: collapseAll, children: jsx(Body1, { children: "Collapse All" }) })] })), hasChildren && contextMenuCommands.length > 0 && jsx(MenuDivider, {}), contextMenuItems] }) })] }));
3253
3246
  };
3254
3247
  const SceneExplorer = (props) => {
3255
3248
  const classes = useStyles$L();
@@ -3274,7 +3267,7 @@ const SceneExplorer = (props) => {
3274
3267
  };
3275
3268
  const onSceneItemRemoved = (item) => {
3276
3269
  setSceneVersion((version) => version + 1);
3277
- if (openItems.delete(item.uniqueId)) {
3270
+ if (openItems.delete(GetEntityId(item))) {
3278
3271
  setOpenItems(new Set(openItems));
3279
3272
  }
3280
3273
  if (item === selectedEntity) {
@@ -3329,7 +3322,7 @@ const SceneExplorer = (props) => {
3329
3322
  parent.children = [];
3330
3323
  }
3331
3324
  parent.children.push(treeItemData);
3332
- allTreeItems.set(entity.uniqueId, treeItemData);
3325
+ allTreeItems.set(GetEntityId(entity), treeItemData);
3333
3326
  return treeItemData;
3334
3327
  };
3335
3328
  const rootEntityTreeItems = rootEntities.map((entity) => createEntityTreeItemData(entity, sectionTreeItem));
@@ -3377,7 +3370,7 @@ const SceneExplorer = (props) => {
3377
3370
  TraverseGraph(children,
3378
3371
  // Get children
3379
3372
  (treeItem) => {
3380
- if (filter || openItems.has(treeItem.entity.uniqueId)) {
3373
+ if (filter || openItems.has(GetEntityId(treeItem.entity))) {
3381
3374
  if (!treeItem.children) {
3382
3375
  return null;
3383
3376
  }
@@ -3428,8 +3421,8 @@ const SceneExplorer = (props) => {
3428
3421
  }, [sceneTreeItem, sectionTreeItems, allTreeItems, openItems, itemsFilter, isSorted]);
3429
3422
  const getParentStack = useCallback((entity) => {
3430
3423
  const parentStack = [];
3431
- for (let treeItem = allTreeItems.get(entity.uniqueId); treeItem; treeItem = treeItem?.type === "entity" ? treeItem.parent : undefined) {
3432
- parentStack.push(treeItem.type === "entity" ? treeItem.entity.uniqueId : treeItem.sectionName);
3424
+ for (let treeItem = allTreeItems.get(GetEntityId(entity)); treeItem; treeItem = treeItem?.type === "entity" ? treeItem.parent : undefined) {
3425
+ parentStack.push(treeItem.type === "entity" ? GetEntityId(treeItem.entity) : treeItem.sectionName);
3433
3426
  }
3434
3427
  // The first item will be the entity itself, so just remove it.
3435
3428
  parentStack.shift();
@@ -5912,7 +5905,7 @@ const DefaultInspectorExtensionFeed = new BuiltInsExtensionFeed("Inspector", [
5912
5905
  keywords: ["creation", "tools"],
5913
5906
  ...BabylonWebResources,
5914
5907
  author: { name: "Babylon.js", forumUserName: "" },
5915
- getExtensionModuleAsync: async () => await import('./quickCreateToolsService-CIYZ626Q.js'),
5908
+ getExtensionModuleAsync: async () => await import('./quickCreateToolsService-BljTQeJt.js'),
5916
5909
  },
5917
5910
  {
5918
5911
  name: "Reflector",
@@ -5920,7 +5913,7 @@ const DefaultInspectorExtensionFeed = new BuiltInsExtensionFeed("Inspector", [
5920
5913
  keywords: ["reflector", "bridge", "sync", "sandbox", "tools"],
5921
5914
  ...BabylonWebResources,
5922
5915
  author: { name: "Babylon.js", forumUserName: "" },
5923
- getExtensionModuleAsync: async () => await import('./reflectorService-Ck6IfTV-.js'),
5916
+ getExtensionModuleAsync: async () => await import('./reflectorService-8_EDaWNz.js'),
5924
5917
  },
5925
5918
  ]);
5926
5919
 
@@ -6750,7 +6743,7 @@ function MakeModularTool(options) {
6750
6743
  });
6751
6744
  // Register the extension list service (for browsing/installing extensions) if extension feeds are provided.
6752
6745
  if (extensionFeeds.length > 0) {
6753
- const { ExtensionListServiceDefinition } = await import('./extensionsListService-BdUP4DIf.js');
6746
+ const { ExtensionListServiceDefinition } = await import('./extensionsListService-DJfDekFP.js');
6754
6747
  await serviceContainer.addServiceAsync(ExtensionListServiceDefinition);
6755
6748
  }
6756
6749
  // Register the theme selector service (for selecting the theme) if theming is configured.
@@ -6931,6 +6924,11 @@ const GizmoServiceDefinition = {
6931
6924
  const MeshIcon = createFluentIcon("Mesh", "16", '<path d="M14.03,3.54l-5.11-2.07c-.61-.25-1.27-.25-1.88,0L1.93,3.54c-.57.23-.94.78-.94,1.39v6.15c0,.61.37,1.16.94,1.39l5.11,2.07c.3.12.62.18.94.18s.64-.06.94-.18l5.12-2.07c.57-.23.94-.78.94-1.39v-6.15c0-.61-.37-1.16-.94-1.39ZM13.97,7.71l-2.11.86v-2.71l2.11-.86v2.71ZM1.99,5l2.11.86v2.71l-2.11-.86v-2.71ZM11.35,4.98l-2.04-.83,1.78-.72,2.04.83-1.78.72ZM10.02,5.52l-2.04.83-2.04-.83,2.04-.83,2.04.83ZM4.6,4.98l-1.78-.72,2.04-.83,1.78.72-2.04.83ZM5.1,6.26l2.38.96v2.71l-2.38-.96v-2.71ZM8.48,7.22l2.38-.96v2.71l-2.38.96v-2.71ZM7.41,2.39c.18-.07.37-.11.56-.11s.38.04.56.11l1.22.49-1.78.72-1.79-.72,1.22-.49ZM1.99,11.07v-2.29l2.11.86v2.62l-1.8-.73c-.19-.08-.31-.26-.31-.46ZM5.1,12.67v-2.62l2.38.96v2.61s-.04,0-.06-.01l-2.31-.94ZM8.54,13.61s-.04,0-.06.01v-2.61l2.38-.96v2.62l-2.31.94ZM13.66,11.54l-1.8.73v-2.62l2.11-.86v2.29c0,.2-.12.39-.31.46Z"/>');
6932
6925
  const TranslateIcon = createFluentIcon("Translate", "24", '<path d="M20.16,12.98l-2.75-2.75c-.29-.29-.77-.29-1.06,0-.29.29-.29.77,0,1.06l1.47,1.47h-6.69v-6.69l1.47,1.47c.29.29.77.29,1.06,0,.29-.29.29-.77,0-1.06l-2.75-2.75c-.14-.14-.33-.22-.53-.22s-.39.08-.53.22l-2.75,2.75c-.29.29-.29.77,0,1.06.29.29.77.29,1.06,0l1.47-1.47v7.13l-3.52,3.52v-2.08c0-.41-.34-.75-.75-.75s-.75.34-.75.75v3.89c0,.2.08.39.22.53.14.14.33.22.53.22h3.89c.41,0,.75-.34.75-.75s-.34-.75-.75-.75h-2.08s3.52-3.52,3.52-3.52h7.13l-1.47,1.47c-.29.29-.29.77,0,1.06s.77.29,1.06,0l2.75-2.75c.14-.14.22-.33.22-.53s-.08-.39-.22-.53Z" />');
6933
6926
  const MaterialIcon = createFluentIcon("Material", "16", '<path d="M14.74,6.3c-.09-.36-.38-.64-.75-.72-.04-.09-.08-.18-.12-.27.1-.15.16-.32.16-.51,0-.18-.05-.34-.13-.48-1.23-1.97-3.41-3.28-5.9-3.28C4.16,1.04,1.04,4.16,1.04,7.99c0,.39.23.72.57.88.02.12.03.25.06.37-.18.18-.3.42-.3.7,0,.11.02.21.06.31.94,2.74,3.53,4.71,6.58,4.71,3.84,0,6.96-3.12,6.96-6.96,0-.59-.08-1.16-.22-1.7ZM2.07,8.58c-.02-.19-.03-.39-.03-.58,0-3.29,2.67-5.96,5.96-5.96,2.23,0,4.17,1.23,5.2,3.05.05.18-.07.45-.3.75-.57-.73-1.45-1.21-2.45-1.21-1.72,0-3.12,1.4-3.12,3.11,0,.33.07.65.16.95-3.05.82-5.17.52-5.42-.11ZM12.56,7.75c0,1.17-.95,2.11-2.11,2.11s-2.12-.95-2.12-2.11.95-2.11,2.12-2.11,2.11.95,2.11,2.11ZM8,13.96c-2.6,0-4.81-1.68-5.62-4.01.5.16,1.11.24,1.79.24,1.15,0,2.49-.22,3.79-.59.57.76,1.47,1.26,2.49,1.26,1.72,0,3.11-1.4,3.11-3.11,0-.34-.07-.65-.17-.96.13-.13.24-.26.34-.39.14.51.22,1.04.22,1.6,0,3.29-2.67,5.96-5.96,5.96Z"/>');
6927
+ const FlatTangentIcon = createFluentIcon("FlatTangent", "20", '<g transform="scale(0.5)"><path d="M28,18.5a1.51,1.51,0,0,0-1.41,1H11.5v1H26.59a1.5,1.5,0,1,0,1.41-2Z"/></g>');
6928
+ const LinearTangentIcon = createFluentIcon("LinearTangent", "20", '<g transform="scale(0.5)"><path d="M26,14a1.49,1.49,0,0,0-2.4,1.69l-10,10,.71.71,10-10A1.49,1.49,0,0,0,26,14Z"/></g>');
6929
+ const BreakTangentIcon = createFluentIcon("BreakTangent", "20", '<g transform="scale(0.5)"><path d="M27.11,13.05a1.49,1.49,0,0,0-2.05.53,1.47,1.47,0,0,0,.14,1.72l-4.78,8L20,22.92l-.42.42-4.78-8a1.47,1.47,0,0,0,.14-1.72,1.49,1.49,0,0,0-2-.53,1.5,1.5,0,0,0,1,2.76l4.91,8.26-1,1L20,27.16,22.12,25l-1-1,4.91-8.26a1.5,1.5,0,0,0,1.05-2.76Z"/></g>');
6930
+ const UnifyTangentIcon = createFluentIcon("UnifyTangent", "20", '<g transform="scale(0.5)"><path d="M27.94,18.28a1.49,1.49,0,0,0-1.41,1h-5l-1.62-1.63-1.62,1.63h-5a1.5,1.5,0,1,0,0,1h5l1.62,1.62,1.62-1.62h5a1.5,1.5,0,1,0,1.41-2Z"/></g>');
6931
+ const StepTangentIcon = createFluentIcon("StepTangent", "20", '<g transform="scale(0.5)"><path d="M29,16.71a1.5,1.5,0,1,0-2,1.41v5.67H11v1H28V18.12A1.51,1.51,0,0,0,29,16.71Z"/></g>');
6934
6932
 
6935
6933
  const useStyles$C = makeStyles({
6936
6934
  coordinatesModeButton: {
@@ -7493,7 +7491,7 @@ const TopBar = () => {
7493
7491
  const handleValueChange = useCallback((value) => {
7494
7492
  observables.onValueManuallyEntered.notifyObservers(value);
7495
7493
  }, [observables]);
7496
- return (jsxs("div", { className: styles.root, children: [jsx("span", { className: styles.title, children: state.title }), jsx(Divider, { vertical: true, className: styles.divider }), jsxs("div", { className: styles.inputGroup, children: [jsx("span", { className: styles.inputLabel, children: "Frame:" }), jsx(SpinButton, { className: styles.spinButton, value: keyFrameValue ?? 0, onChange: handleFrameChange, disabled: !hasActiveAnimations || !frameControlEnabled })] }), jsxs("div", { className: styles.inputGroup, children: [jsx("span", { className: styles.inputLabel, children: "Value:" }), jsx(SpinButton, { className: styles.spinButton, value: keyValue ?? 0, onChange: handleValueChange, disabled: !hasActiveAnimations || !valueControlEnabled, step: 0.1 })] }), jsx(Divider, { vertical: true, className: styles.divider }), jsxs("div", { className: styles.buttonGroup, children: [jsx(Button, { icon: AddRegular, appearance: "subtle", disabled: !hasActiveAnimations, onClick: () => observables.onCreateOrUpdateKeyPointRequired.notifyObservers(), title: "New key" }), jsx(Button, { icon: DeleteRegular, appearance: "subtle", disabled: !hasActiveKeyPoints, onClick: () => observables.onDeleteKeyActiveKeyPoints.notifyObservers(), title: "Delete key" }), jsx(Button, { icon: FullScreenMaximizeRegular, appearance: "subtle", onClick: () => observables.onFrameRequired.notifyObservers(), title: "Frame canvas" })] }), jsx(Divider, { vertical: true, className: styles.divider }), jsxs("div", { className: styles.buttonGroup, children: [jsx(Button, { icon: ArrowMinimizeRegular, appearance: "subtle", disabled: !hasActiveKeyPoints, onClick: () => observables.onFlattenTangentRequired.notifyObservers(), title: "Flatten tangent" }), jsx(Button, { icon: LineHorizontal1Regular, appearance: "subtle", disabled: !hasActiveKeyPoints, onClick: () => observables.onLinearTangentRequired.notifyObservers(), title: "Linear tangent" }), jsx(Button, { icon: ChevronDoubleLeftRegular, appearance: "subtle", disabled: !hasActiveKeyPoints, onClick: () => observables.onBreakTangentRequired.notifyObservers(), title: "Break tangent" }), jsx(Button, { icon: ChevronDoubleRightRegular, appearance: "subtle", disabled: !hasActiveKeyPoints, onClick: () => observables.onUnifyTangentRequired.notifyObservers(), title: "Unify tangent" }), jsx(Button, { icon: StepsRegular, appearance: "subtle", disabled: !hasActiveKeyPoints, onClick: () => observables.onStepTangentRequired.notifyObservers(), title: "Step tangent" })] })] }));
7494
+ return (jsxs("div", { className: styles.root, children: [jsx("div", { className: styles.title, children: state.title }), jsx(Divider, { vertical: true, className: styles.divider }), jsxs("div", { className: styles.inputGroup, children: [jsx("div", { className: styles.inputLabel, children: "Frame:" }), jsx(SpinButton, { className: styles.spinButton, value: keyFrameValue ?? 0, onChange: handleFrameChange, disabled: !hasActiveAnimations || !frameControlEnabled })] }), jsxs("div", { className: styles.inputGroup, children: [jsx("div", { className: styles.inputLabel, children: "Value:" }), jsx(SpinButton, { className: styles.spinButton, value: keyValue ?? 0, onChange: handleValueChange, disabled: !hasActiveAnimations || !valueControlEnabled, step: 0.1 })] }), jsx(Divider, { vertical: true, className: styles.divider }), jsxs("div", { className: styles.buttonGroup, children: [jsx(Button, { icon: AddRegular, appearance: "subtle", disabled: !hasActiveAnimations, onClick: () => observables.onCreateOrUpdateKeyPointRequired.notifyObservers(), title: "New key" }), jsx(Button, { icon: DeleteRegular, appearance: "subtle", disabled: !hasActiveKeyPoints, onClick: () => observables.onDeleteKeyActiveKeyPoints.notifyObservers(), title: "Delete key" }), jsx(Button, { icon: FullScreenMaximizeRegular, appearance: "subtle", onClick: () => observables.onFrameRequired.notifyObservers(), title: "Frame canvas" })] }), jsx(Divider, { vertical: true, className: styles.divider }), jsxs("div", { className: styles.buttonGroup, children: [jsx(Button, { icon: FlatTangentIcon, appearance: "subtle", disabled: !hasActiveKeyPoints, onClick: () => observables.onFlattenTangentRequired.notifyObservers(), title: "Flatten tangent" }), jsx(Button, { icon: LinearTangentIcon, appearance: "subtle", disabled: !hasActiveKeyPoints, onClick: () => observables.onLinearTangentRequired.notifyObservers(), title: "Linear tangent" }), jsx(Button, { icon: BreakTangentIcon, appearance: "subtle", disabled: !hasActiveKeyPoints, onClick: () => observables.onBreakTangentRequired.notifyObservers(), title: "Break tangent" }), jsx(Button, { icon: UnifyTangentIcon, appearance: "subtle", disabled: !hasActiveKeyPoints, onClick: () => observables.onUnifyTangentRequired.notifyObservers(), title: "Unify tangent" }), jsx(Button, { icon: StepTangentIcon, appearance: "subtle", disabled: !hasActiveKeyPoints, onClick: () => observables.onStepTangentRequired.notifyObservers(), title: "Step tangent" })] })] }));
7497
7495
  };
7498
7496
 
7499
7497
  /**
@@ -7533,18 +7531,6 @@ const ColorChannelColors = {
7533
7531
  * Default curve color for single-component (float) animations
7534
7532
  */
7535
7533
  const DefaultCurveColor = "#ffffff";
7536
- /**
7537
- * Graph UI colors
7538
- */
7539
- const GraphColors = {
7540
- /** Zero line color */
7541
- zeroLine: "#666666",
7542
- /** Selection rectangle stroke */
7543
- selectionStroke: "#ffffff",
7544
- /** Value axis label color */
7545
- valueAxisLabel: "#555555",
7546
- /** Value axis background */
7547
- valueAxisBackground: "#111111"};
7548
7534
 
7549
7535
  const useStyles$z = makeStyles({
7550
7536
  root: {
@@ -7757,7 +7743,7 @@ const AnimationEntry = ({ animation }) => {
7757
7743
  return [];
7758
7744
  }
7759
7745
  };
7760
- return (jsxs("div", { className: styles.root, children: [jsxs("div", { className: `${styles.entry} ${isActive ? styles.entryActive : ""}`, onClick: handleClick, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), children: [jsx("div", { className: styles.chevron, children: isExpandable ? (jsx(Button, { icon: isExpanded ? ChevronDownRegular : ChevronRightRegular, appearance: "transparent", onClick: handleExpandClick })) : (jsx(CircleSmallFilled, {})) }), jsx("span", { className: styles.name, title: animation.name, children: animation.name }), jsxs("div", { className: `${styles.actions} ${isHovered || isEditOpen ? styles.actionsVisible : ""}`, children: [jsx(Popover, { open: isEditOpen, onOpenChange: handleEditOpenChange, positioning: "after", trigger: jsx(Button, { icon: SettingsRegular, appearance: "transparent", title: "Edit animation" }), children: jsx(EditAnimationPanel, { animation: animation, onClose: () => setIsEditOpen(false) }) }), jsx(Button, { icon: DeleteRegular, appearance: "transparent", onClick: handleDelete, title: "Delete animation" })] })] }), isExpanded && getSubEntries().map((sub) => jsx(AnimationSubEntry, { animation: animation, subName: sub.name, color: sub.color }, sub.name))] }));
7746
+ return (jsxs("div", { className: styles.root, children: [jsxs("div", { className: `${styles.entry} ${isActive ? styles.entryActive : ""}`, onClick: handleClick, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), children: [jsx("div", { className: styles.chevron, children: isExpandable ? (jsx(Button, { icon: isExpanded ? ChevronDownRegular : ChevronRightRegular, appearance: "transparent", onClick: handleExpandClick })) : (jsx(CircleSmallFilled, {})) }), jsx("div", { className: styles.name, title: animation.name, children: animation.name }), jsxs("div", { className: `${styles.actions} ${isHovered || isEditOpen ? styles.actionsVisible : ""}`, children: [jsx(Popover, { open: isEditOpen, onOpenChange: handleEditOpenChange, positioning: "after", trigger: jsx(Button, { icon: SettingsRegular, appearance: "transparent", title: "Edit animation" }), children: jsx(EditAnimationPanel, { animation: animation, onClose: () => setIsEditOpen(false) }) }), jsx(Button, { icon: DeleteRegular, appearance: "transparent", onClick: handleDelete, title: "Delete animation" })] })] }), isExpanded && getSubEntries().map((sub) => jsx(AnimationSubEntry, { animation: animation, subName: sub.name, color: sub.color }, sub.name))] }));
7761
7747
  };
7762
7748
  /**
7763
7749
  * Sub-entry for vector/color animations (X, Y, Z, R, G, B, etc.)
@@ -7780,7 +7766,7 @@ const AnimationSubEntry = ({ animation, subName, color }) => {
7780
7766
  }
7781
7767
  observables.onActiveAnimationChanged.notifyObservers({});
7782
7768
  }, [animation, color, isThisChannelActive, actions, observables]);
7783
- return (jsxs("div", { className: `${styles.entry} ${styles.subEntry} ${!isEnabled ? styles.subEntryDisabled : ""}`, onClick: handleClick, children: [jsx("div", { className: styles.colorDot, style: { backgroundColor: color } }), jsx("span", { className: styles.name, children: subName })] }));
7769
+ return (jsxs("div", { className: `${styles.entry} ${styles.subEntry} ${!isEnabled ? styles.subEntryDisabled : ""}`, onClick: handleClick, children: [jsx("div", { className: styles.colorDot, style: { backgroundColor: color } }), jsx("div", { className: styles.name, children: subName })] }));
7784
7770
  };
7785
7771
  /**
7786
7772
  * Animation list component showing all animations
@@ -8054,7 +8040,7 @@ const AddAnimationPanel = ({ onClose }) => {
8054
8040
  onClose();
8055
8041
  observables.onAnimationsLoaded.notifyObservers();
8056
8042
  }, [name, currentProperty, currentType, loopMode, fps, minFrame, maxFrame, state, observables, onClose]);
8057
- return (jsxs("div", { className: styles.root, children: [jsx("div", { className: styles.header, children: jsx("span", { className: styles.title, children: "Add Animation" }) }), jsxs("div", { className: styles.form, children: [jsxs("div", { className: styles.row, children: [jsx(Label, { children: "Display Name" }), jsx(Input, { value: name, onChange: (_, data) => setName(data.value), placeholder: "Animation name" })] }), jsxs("div", { className: styles.row, children: [jsx(Label, { children: "Mode" }), jsx(Dropdown$1, { value: mode, selectedOptions: [mode], onOptionSelect: (_, data) => setMode(data.optionValue), disabled: properties.length === 0, children: MODES.map((m) => (jsx(Option, { value: m, children: m }, m))) })] }), jsxs("div", { className: styles.row, children: [jsx(Label, { children: "Property" }), isCustomMode ? (jsx(Input, { value: customProperty, onChange: (_, data) => setCustomProperty(data.value), placeholder: "e.g., position, rotation, scaling" })) : (jsx(Dropdown$1, { value: selectedProperty, selectedOptions: [selectedProperty], onOptionSelect: (_, data) => setSelectedProperty(data.optionValue), children: properties.map((prop) => (jsx(Option, { value: prop, children: prop }, prop))) }))] }), jsxs("div", { className: styles.row, children: [jsx(Label, { children: "Type" }), isCustomMode ? (jsx(Dropdown$1, { value: animationType, selectedOptions: [animationType], onOptionSelect: (_, data) => setAnimationType(data.optionValue), children: ANIMATION_TYPES.map((type) => (jsx(Option, { value: type, children: type }, type))) })) : (jsx("div", { className: styles.typeDisplay, children: inferredType }))] }), jsxs("div", { className: styles.row, children: [jsx(Label, { children: "Loop Mode" }), jsx(Dropdown$1, { value: loopMode, selectedOptions: [loopMode], onOptionSelect: (_, data) => setLoopMode(data.optionValue), children: LOOP_MODES.map((lm) => (jsx(Option, { value: lm, children: lm }, lm))) })] })] }), jsxs("div", { className: styles.buttons, children: [jsx(Button, { appearance: "primary", onClick: createAnimation, disabled: !isValid, label: "Create" }), jsx(Button, { appearance: "subtle", onClick: onClose, label: "Cancel" })] })] }));
8043
+ return (jsxs("div", { className: styles.root, children: [jsx("div", { className: styles.header, children: jsx("div", { className: styles.title, children: "Add Animation" }) }), jsxs("div", { className: styles.form, children: [jsxs("div", { className: styles.row, children: [jsx(Label, { children: "Display Name" }), jsx(Input, { value: name, onChange: (_, data) => setName(data.value), placeholder: "Animation name" })] }), jsxs("div", { className: styles.row, children: [jsx(Label, { children: "Mode" }), jsx(Dropdown$1, { value: mode, selectedOptions: [mode], onOptionSelect: (_, data) => setMode(data.optionValue), disabled: properties.length === 0, children: MODES.map((m) => (jsx(Option, { value: m, children: m }, m))) })] }), jsxs("div", { className: styles.row, children: [jsx(Label, { children: "Property" }), isCustomMode ? (jsx(Input, { value: customProperty, onChange: (_, data) => setCustomProperty(data.value), placeholder: "e.g., position, rotation, scaling" })) : (jsx(Dropdown$1, { value: selectedProperty, selectedOptions: [selectedProperty], onOptionSelect: (_, data) => setSelectedProperty(data.optionValue), children: properties.map((prop) => (jsx(Option, { value: prop, children: prop }, prop))) }))] }), jsxs("div", { className: styles.row, children: [jsx(Label, { children: "Type" }), isCustomMode ? (jsx(Dropdown$1, { value: animationType, selectedOptions: [animationType], onOptionSelect: (_, data) => setAnimationType(data.optionValue), children: ANIMATION_TYPES.map((type) => (jsx(Option, { value: type, children: type }, type))) })) : (jsx("div", { className: styles.typeDisplay, children: inferredType }))] }), jsxs("div", { className: styles.row, children: [jsx(Label, { children: "Loop Mode" }), jsx(Dropdown$1, { value: loopMode, selectedOptions: [loopMode], onOptionSelect: (_, data) => setLoopMode(data.optionValue), children: LOOP_MODES.map((lm) => (jsx(Option, { value: lm, children: lm }, lm))) })] })] }), jsxs("div", { className: styles.buttons, children: [jsx(Button, { appearance: "primary", onClick: createAnimation, disabled: !isValid, label: "Create" }), jsx(Button, { appearance: "subtle", onClick: onClose, label: "Cancel" })] })] }));
8058
8044
  };
8059
8045
 
8060
8046
  const useStyles$w = makeStyles({
@@ -8147,7 +8133,7 @@ const LoadAnimationPanel = ({ onClose }) => {
8147
8133
  setLoadError("Unable to load animations: " + err);
8148
8134
  }
8149
8135
  }, [snippetIdInput, loadAnimations]);
8150
- return (jsxs("div", { className: styles.root, children: [jsx("div", { className: styles.header, children: jsx("span", { className: styles.title, children: "Load Animations" }) }), jsxs("div", { className: styles.section, children: [jsx(Label, { children: "From Snippet Server" }), jsxs("div", { className: styles.row, children: [jsx(Input, { className: styles.input, placeholder: "Snippet ID", value: snippetIdInput, onChange: (_, data) => setSnippetIdInput(data.value) }), jsx(Button, { appearance: "primary", onClick: loadFromSnippetServer, disabled: !snippetIdInput.trim(), label: "Load" })] }), loadError && jsx("span", { className: styles.errorText, children: loadError })] }), jsxs("div", { className: styles.section, children: [jsx(Label, { children: "From File" }), jsx(UploadButton, { appearance: "subtle", onUpload: loadFromFile, accept: ".json", label: "Browse..." })] }), loadedSnippetId && jsxs("div", { className: styles.snippetId, children: ["Loaded Snippet ID: ", loadedSnippetId] })] }));
8136
+ return (jsxs("div", { className: styles.root, children: [jsx("div", { className: styles.header, children: jsx("div", { className: styles.title, children: "Load Animations" }) }), jsxs("div", { className: styles.section, children: [jsx(Label, { children: "From Snippet Server" }), jsxs("div", { className: styles.row, children: [jsx(Input, { className: styles.input, placeholder: "Snippet ID", value: snippetIdInput, onChange: (_, data) => setSnippetIdInput(data.value) }), jsx(Button, { appearance: "primary", onClick: loadFromSnippetServer, disabled: !snippetIdInput.trim(), label: "Load" })] }), loadError && jsx("div", { className: styles.errorText, children: loadError })] }), jsxs("div", { className: styles.section, children: [jsx(Label, { children: "From File" }), jsx(UploadButton, { appearance: "subtle", onUpload: loadFromFile, accept: ".json", label: "Browse..." })] }), loadedSnippetId && jsxs("div", { className: styles.snippetId, children: ["Loaded Snippet ID: ", loadedSnippetId] })] }));
8151
8137
  };
8152
8138
 
8153
8139
  class StringTools {
@@ -8350,11 +8336,11 @@ const SaveAnimationPanel = ({ onClose: _onClose }) => {
8350
8336
  };
8351
8337
  xmlHttp.send(JSON.stringify(dataToSend));
8352
8338
  }, [getJson, snippetId]);
8353
- return (jsxs("div", { className: styles.root, children: [jsx("div", { className: styles.header, children: jsx("span", { className: styles.title, children: "Save Animations" }) }), jsx("div", { className: styles.list, children: state.animations?.map((anim, i) => {
8339
+ return (jsxs("div", { className: styles.root, children: [jsx("div", { className: styles.header, children: jsx("div", { className: styles.title, children: "Save Animations" }) }), jsx("div", { className: styles.list, children: state.animations?.map((anim, i) => {
8354
8340
  const animation = getAnimation(anim);
8355
8341
  const isChecked = selectedAnimations.includes(animation);
8356
8342
  return jsx(Checkbox$1, { label: animation.name, checked: isChecked, onChange: (_, data) => toggleAnimation(animation, !!data.checked) }, i);
8357
- }) }), jsxs("div", { className: styles.buttons, children: [jsx(Button, { appearance: "primary", onClick: saveToSnippetServer, disabled: selectedAnimations.length === 0 || isSaving, label: isSaving ? "Saving..." : "Save to Snippet Server" }), jsx(Button, { appearance: "secondary", onClick: saveToFile, disabled: selectedAnimations.length === 0, label: "Save to File" })] }), saveError && jsx("span", { className: styles.errorText, children: saveError }), snippetId && jsxs("div", { className: styles.snippetId, children: ["Saved! Snippet ID: ", snippetId] })] }));
8343
+ }) }), jsxs("div", { className: styles.buttons, children: [jsx(Button, { appearance: "primary", onClick: saveToSnippetServer, disabled: selectedAnimations.length === 0 || isSaving, label: isSaving ? "Saving..." : "Save to Snippet Server" }), jsx(Button, { appearance: "secondary", onClick: saveToFile, disabled: selectedAnimations.length === 0, label: "Save to File" })] }), saveError && jsx("div", { className: styles.errorText, children: saveError }), snippetId && jsxs("div", { className: styles.snippetId, children: ["Saved! Snippet ID: ", snippetId] })] }));
8358
8344
  };
8359
8345
 
8360
8346
  const useStyles$u = makeStyles({
@@ -8475,7 +8461,7 @@ const SideBar = () => {
8475
8461
  }
8476
8462
  }
8477
8463
  }, [state.animations, state.useTargetAnimations]);
8478
- return (jsxs("div", { className: styles.root, children: [jsxs("div", { className: styles.menuBar, children: [!state.useTargetAnimations && (jsxs(Fragment, { children: [jsx(Popover, { open: openPopover === "add", onOpenChange: (open) => setOpenPopover(open ? "add" : null), positioning: "below-start", trigger: jsx(Button, { icon: AddRegular, appearance: openPopover === "add" ? "primary" : "subtle", title: "Add new animation" }), children: jsx(AddAnimationPanel, { onClose: () => setOpenPopover(null) }) }), jsx(Popover, { open: openPopover === "load", onOpenChange: (open) => setOpenPopover(open ? "load" : null), positioning: "below-start", trigger: jsx(Button, { icon: ArrowDownloadRegular, appearance: openPopover === "load" ? "primary" : "subtle", title: "Load animations" }), children: jsx(LoadAnimationPanel, { onClose: () => setOpenPopover(null) }) })] })), jsx(Popover, { open: openPopover === "save", onOpenChange: (open) => setOpenPopover(open ? "save" : null), positioning: "below-start", trigger: jsx(Button, { icon: SaveRegular, appearance: openPopover === "save" ? "primary" : "subtle", title: "Save current animations" }), children: jsx(SaveAnimationPanel, { onClose: () => setOpenPopover(null) }) }), jsxs("div", { className: styles.fpsInput, children: [jsx(SpinButton, { className: styles.spinButton, value: fps, onChange: handleFpsChange, min: 1, max: 120 }), jsx("span", { className: styles.fpsLabel, children: "fps" })] })] }), jsx("div", { className: styles.content, children: jsx(AnimationList, {}) })] }));
8464
+ return (jsxs("div", { className: styles.root, children: [jsxs("div", { className: styles.menuBar, children: [!state.useTargetAnimations && (jsxs(Fragment, { children: [jsx(Popover, { open: openPopover === "add", onOpenChange: (open) => setOpenPopover(open ? "add" : null), positioning: "below-start", trigger: jsx(Button, { icon: AddRegular, appearance: openPopover === "add" ? "primary" : "subtle", title: "Add new animation" }), children: jsx(AddAnimationPanel, { onClose: () => setOpenPopover(null) }) }), jsx(Popover, { open: openPopover === "load", onOpenChange: (open) => setOpenPopover(open ? "load" : null), positioning: "below-start", trigger: jsx(Button, { icon: ArrowDownloadRegular, appearance: openPopover === "load" ? "primary" : "subtle", title: "Load animations" }), children: jsx(LoadAnimationPanel, { onClose: () => setOpenPopover(null) }) })] })), jsx(Popover, { open: openPopover === "save", onOpenChange: (open) => setOpenPopover(open ? "save" : null), positioning: "below-start", trigger: jsx(Button, { icon: SaveRegular, appearance: openPopover === "save" ? "primary" : "subtle", title: "Save current animations" }), children: jsx(SaveAnimationPanel, { onClose: () => setOpenPopover(null) }) }), jsxs("div", { className: styles.fpsInput, children: [jsx(SpinButton, { className: styles.spinButton, value: fps, onChange: handleFpsChange, min: 1, max: 120 }), jsx("div", { className: styles.fpsLabel, children: "fps" })] })] }), jsx("div", { className: styles.content, children: jsx(AnimationList, {}) })] }));
8479
8465
  };
8480
8466
 
8481
8467
  class CurveData {
@@ -8762,10 +8748,15 @@ const KeyPointComponent = (props) => {
8762
8748
  const outVec = useRef(new Vector2());
8763
8749
  const storedLengthIn = useRef(0);
8764
8750
  const storedLengthOut = useRef(0);
8751
+ // Track current position in refs to avoid stale closure issues during drag
8752
+ const currentXRef = useRef(initialX);
8753
+ const currentYRef = useRef(initialY);
8765
8754
  // Update position when props change
8766
8755
  useEffect(() => {
8767
8756
  setCurrentX(initialX);
8768
8757
  setCurrentY(initialY);
8758
+ currentXRef.current = initialX;
8759
+ currentYRef.current = initialY;
8769
8760
  }, [initialX, initialY]);
8770
8761
  // Helper to compare curves by identity (animation + property) rather than reference
8771
8762
  const curvesMatch = useCallback((c1, c2) => {
@@ -8810,16 +8801,15 @@ const KeyPointComponent = (props) => {
8810
8801
  const keys = curve.keys;
8811
8802
  const keyValue = keys[keyId].value;
8812
8803
  const keyFrame = keys[keyId].frame;
8813
- // Work with a clone to avoid mutating the original vector
8814
- const workVec = vec.clone();
8815
- // Ensure vector points in the correct direction
8816
- if (isIn && workVec.x >= 0) {
8817
- workVec.x = -0.01;
8804
+ // Ensure vector points in the correct direction (modifies vec directly like v1)
8805
+ if (isIn && vec.x >= 0) {
8806
+ vec.x = -0.01;
8818
8807
  }
8819
- else if (!isIn && workVec.x <= 0) {
8820
- workVec.x = 0.01;
8808
+ else if (!isIn && vec.x <= 0) {
8809
+ vec.x = 0.01;
8821
8810
  }
8822
- const currentPosition = workVec.clone();
8811
+ // Clone AFTER direction correction (like v1)
8812
+ const currentPosition = vec.clone();
8823
8813
  currentPosition.normalize();
8824
8814
  currentPosition.scaleInPlace(storedLength);
8825
8815
  const value = isIn ? keyValue - invertY(currentPosition.y + currentY) : invertY(currentPosition.y + currentY) - keyValue;
@@ -9054,9 +9044,9 @@ const KeyPointComponent = (props) => {
9054
9044
  lockX.current = false;
9055
9045
  lockY.current = false;
9056
9046
  }
9057
- // Calculate new position
9058
- let newX = currentX + (lockX.current ? 0 : diffX * scale);
9059
- let newY = currentY + (lockY.current ? 0 : diffY * scale);
9047
+ // Calculate new position using refs to avoid stale closure
9048
+ let newX = currentXRef.current + (lockX.current ? 0 : diffX * scale);
9049
+ let newY = currentYRef.current + (lockY.current ? 0 : diffY * scale);
9060
9050
  // Constrain to valid frame range
9061
9051
  const previousX = getPreviousX();
9062
9052
  const nextX = getNextX();
@@ -9074,15 +9064,18 @@ const KeyPointComponent = (props) => {
9074
9064
  observables.onFrameSet.notifyObservers(frame);
9075
9065
  }
9076
9066
  else {
9077
- newX = currentX;
9067
+ newX = currentXRef.current;
9078
9068
  }
9079
9069
  // Check value lock constraints
9080
9070
  if (state.lockLastFrameValue && keyId === curve.keys.length - 1) {
9081
- newY = currentY;
9071
+ newY = currentYRef.current;
9082
9072
  }
9083
9073
  const value = invertY(newY);
9084
9074
  onKeyValueChanged(value);
9085
9075
  observables.onValueSet.notifyObservers(value);
9076
+ // Update both ref and state
9077
+ currentXRef.current = newX;
9078
+ currentYRef.current = newY;
9086
9079
  setCurrentX(newX);
9087
9080
  setCurrentY(newY);
9088
9081
  }
@@ -9104,35 +9097,41 @@ const KeyPointComponent = (props) => {
9104
9097
  }
9105
9098
  }
9106
9099
  if (controlMode.current === ControlMode.TangentLeft) {
9107
- // Calculate the moved vector position
9108
- const movedInVec = inVec.current.clone();
9109
- movedInVec.x += diffX * scale;
9110
- movedInVec.y += diffY * scale;
9111
- const newSlope = extractSlope(movedInVec, storedLengthIn.current, true);
9100
+ // Update the vector in place - multiply by scale to match v1 behavior
9101
+ inVec.current.x += diffX * scale;
9102
+ inVec.current.y += diffY * scale;
9103
+ const newSlope = extractSlope(inVec.current, storedLengthIn.current, true);
9112
9104
  curve.updateInTangentFromControlPoint(keyId, newSlope);
9113
9105
  if (isLockedTangent) {
9114
9106
  // Rotate the moved inVec by -angleDiff to get the outVec direction
9115
9107
  const tmpVector = new Vector2();
9116
- movedInVec.rotateToRef(-angleDiff, tmpVector);
9108
+ inVec.current.rotateToRef(-angleDiff, tmpVector);
9117
9109
  tmpVector.x = Math.abs(tmpVector.x); // Ensure out tangent points right
9118
9110
  const outSlope = extractSlope(tmpVector, storedLengthOut.current, false);
9119
9111
  curve.updateOutTangentFromControlPoint(keyId, outSlope);
9112
+ // Update outVec visually to match the rotated position
9113
+ outVec.current.copyFrom(tmpVector);
9114
+ outVec.current.normalize();
9115
+ outVec.current.scaleInPlace(100 * scale);
9120
9116
  }
9121
9117
  }
9122
9118
  else if (controlMode.current === ControlMode.TangentRight) {
9123
- // Calculate the moved vector position
9124
- const movedOutVec = outVec.current.clone();
9125
- movedOutVec.x += diffX * scale;
9126
- movedOutVec.y += diffY * scale;
9127
- const newSlope = extractSlope(movedOutVec, storedLengthOut.current, false);
9119
+ // Update the vector in place - multiply by scale to match v1 behavior
9120
+ outVec.current.x += diffX * scale;
9121
+ outVec.current.y += diffY * scale;
9122
+ const newSlope = extractSlope(outVec.current, storedLengthOut.current, false);
9128
9123
  curve.updateOutTangentFromControlPoint(keyId, newSlope);
9129
9124
  if (isLockedTangent) {
9130
9125
  // Rotate the moved outVec by angleDiff to get the inVec direction
9131
9126
  const tmpVector = new Vector2();
9132
- movedOutVec.rotateToRef(angleDiff, tmpVector);
9127
+ outVec.current.rotateToRef(angleDiff, tmpVector);
9133
9128
  tmpVector.x = -Math.abs(tmpVector.x); // Ensure in tangent points left
9134
9129
  const inSlope = extractSlope(tmpVector, storedLengthIn.current, true);
9135
9130
  curve.updateInTangentFromControlPoint(keyId, inSlope);
9131
+ // Update inVec visually to match the rotated position
9132
+ inVec.current.copyFrom(tmpVector);
9133
+ inVec.current.normalize();
9134
+ inVec.current.scaleInPlace(100 * scale);
9136
9135
  }
9137
9136
  }
9138
9137
  actions.refreshTarget();
@@ -9142,8 +9141,6 @@ const KeyPointComponent = (props) => {
9142
9141
  sourcePointerX.current = evt.nativeEvent.offsetX;
9143
9142
  sourcePointerY.current = evt.nativeEvent.offsetY;
9144
9143
  }, [
9145
- currentX,
9146
- currentY,
9147
9144
  selectedState,
9148
9145
  getPreviousX,
9149
9146
  getNextX,
@@ -9195,6 +9192,7 @@ const KeyPointComponent = (props) => {
9195
9192
  const hasDefinedInTangent = curve.hasDefinedInTangent(keyId);
9196
9193
  const hasDefinedOutTangent = curve.hasDefinedOutTangent(keyId);
9197
9194
  // Calculate tangent vectors from curve data (like v1's render method)
9195
+ // This recalculates on every render, including after tangent updates
9198
9196
  const convertedX = invertX(currentX);
9199
9197
  const convertedY = invertY(currentY);
9200
9198
  if (hasDefinedInTangent) {
@@ -9254,30 +9252,38 @@ const useStyles$t = makeStyles({
9254
9252
  strokeDasharray: "4 4",
9255
9253
  },
9256
9254
  zeroLine: {
9257
- stroke: GraphColors.zeroLine,
9255
+ stroke: tokens.colorNeutralStroke1,
9258
9256
  strokeWidth: "1px",
9259
9257
  },
9260
9258
  selectionRect: {
9261
9259
  fill: "rgba(255, 255, 255, 0.1)",
9262
- stroke: GraphColors.selectionStroke,
9260
+ stroke: tokens.colorNeutralForeground1,
9263
9261
  strokeWidth: "1px",
9264
9262
  strokeDasharray: "4 4",
9265
9263
  },
9266
9264
  valueAxisLabel: {
9267
- fill: GraphColors.valueAxisLabel,
9265
+ fill: tokens.colorNeutralForeground3,
9268
9266
  fontSize: "10px",
9269
9267
  fontFamily: "acumin-pro-condensed, sans-serif",
9270
9268
  userSelect: "none",
9271
9269
  },
9272
9270
  valueAxisBackground: {
9273
- fill: GraphColors.valueAxisBackground,
9271
+ fill: tokens.colorNeutralBackground1,
9274
9272
  },
9275
9273
  activeRangeOverlay: {
9276
9274
  position: "absolute",
9277
9275
  top: 0,
9278
9276
  height: "100%",
9279
- backgroundColor: "rgba(38, 82, 128, 0.3)",
9280
- border: "1px solid rgba(78, 140, 206, 0.5)",
9277
+ backgroundColor: tokens.colorBrandBackground2,
9278
+ opacity: 0.3,
9279
+ pointerEvents: "none",
9280
+ },
9281
+ inactiveRangeOverlay: {
9282
+ position: "absolute",
9283
+ top: 0,
9284
+ height: "100%",
9285
+ backgroundColor: tokens.colorNeutralBackgroundStatic,
9286
+ opacity: 0.6,
9281
9287
  pointerEvents: "none",
9282
9288
  },
9283
9289
  });
@@ -9671,7 +9677,17 @@ const Graph = ({ width, height }) => {
9671
9677
  const activeRangeLeft = frameToX(state.fromKey);
9672
9678
  const activeRangeRight = frameToX(state.toKey);
9673
9679
  const activeRangeWidth = activeRangeRight - activeRangeLeft;
9674
- return (jsxs("div", { className: styles.root, children: [state.activeAnimations.length > 0 && activeRangeWidth > 0 && (jsx("div", { className: styles.activeRangeOverlay, style: {
9680
+ // Calculate inactive (outside range) overlay positions
9681
+ const leftInactiveWidth = Math.max(0, activeRangeLeft - graphOffsetX);
9682
+ const rightInactiveLeft = Math.min(activeRangeRight, safeWidth);
9683
+ const rightInactiveWidth = Math.max(0, safeWidth - rightInactiveLeft);
9684
+ return (jsxs("div", { className: styles.root, children: [state.activeAnimations.length > 0 && leftInactiveWidth > 0 && (jsx("div", { className: styles.inactiveRangeOverlay, style: {
9685
+ left: graphOffsetX,
9686
+ width: leftInactiveWidth,
9687
+ } })), state.activeAnimations.length > 0 && rightInactiveWidth > 0 && (jsx("div", { className: styles.inactiveRangeOverlay, style: {
9688
+ left: rightInactiveLeft,
9689
+ width: rightInactiveWidth,
9690
+ } })), state.activeAnimations.length > 0 && activeRangeWidth > 0 && (jsx("div", { className: styles.activeRangeOverlay, style: {
9675
9691
  left: Math.max(graphOffsetX, activeRangeLeft),
9676
9692
  width: Math.min(activeRangeWidth, safeWidth - Math.max(graphOffsetX, activeRangeLeft)),
9677
9693
  } })), jsxs("svg", { ref: svgRef, className: styles.svg, width: safeWidth, height: safeHeight, onPointerDown: handlePointerDown, onPointerMove: handlePointerMove, onPointerUp: handlePointerUp, onWheel: handleWheel, children: [renderGrid(), curves.map((curve) => {
@@ -9709,7 +9725,7 @@ const useStyles$s = makeStyles({
9709
9725
  position: "absolute",
9710
9726
  width: "2px",
9711
9727
  height: "100%",
9712
- backgroundColor: tokens.colorNeutralForegroundOnBrand,
9728
+ backgroundColor: tokens.colorBrandForeground1,
9713
9729
  pointerEvents: "auto",
9714
9730
  cursor: "ew-resize",
9715
9731
  },
@@ -9718,7 +9734,7 @@ const useStyles$s = makeStyles({
9718
9734
  top: 0,
9719
9735
  width: "20px",
9720
9736
  height: "20px",
9721
- backgroundColor: tokens.colorNeutralForegroundOnBrand,
9737
+ backgroundColor: tokens.colorBrandBackground,
9722
9738
  borderRadius: "50%",
9723
9739
  border: `2px solid ${tokens.colorNeutralBackground1}`,
9724
9740
  pointerEvents: "auto",
@@ -9726,7 +9742,7 @@ const useStyles$s = makeStyles({
9726
9742
  display: "flex",
9727
9743
  alignItems: "center",
9728
9744
  justifyContent: "center",
9729
- color: tokens.colorNeutralBackground1,
9745
+ color: tokens.colorNeutralForegroundOnBrand,
9730
9746
  fontSize: "9px",
9731
9747
  fontWeight: "bold",
9732
9748
  userSelect: "none",
@@ -9972,7 +9988,7 @@ const FrameBar = ({ width }) => {
9972
9988
  for (let frame = startFrame; frame <= endFrame; frame += tickSpacing) {
9973
9989
  const x = graphOffsetX + ((frame - referenceMinFrame) / range) * viewWidth * scale + offsetX;
9974
9990
  if (x >= graphOffsetX && x <= width) {
9975
- ticks.push(jsx("div", { className: styles.tick, style: { left: x } }, `tick-${frame}`), jsx("span", { className: styles.label, style: { left: x - 10 }, children: frame }, `label-${frame}`));
9991
+ ticks.push(jsx("div", { className: styles.tick, style: { left: x } }, `tick-${frame}`), jsx("div", { className: styles.label, style: { left: x - 10 }, children: frame }, `label-${frame}`));
9976
9992
  }
9977
9993
  }
9978
9994
  return ticks;
@@ -9985,7 +10001,7 @@ const useStyles$q = makeStyles({
9985
10001
  display: "flex",
9986
10002
  flexDirection: "column",
9987
10003
  height: "100%",
9988
- backgroundColor: "#222222",
10004
+ backgroundColor: tokens.colorNeutralBackground2,
9989
10005
  overflow: "hidden",
9990
10006
  userSelect: "none",
9991
10007
  pointerEvents: "none",
@@ -9995,13 +10011,13 @@ const useStyles$q = makeStyles({
9995
10011
  height: "100%",
9996
10012
  },
9997
10013
  tickLabel: {
9998
- fill: "#555555",
10014
+ fill: tokens.colorNeutralForeground3,
9999
10015
  fontSize: "12px",
10000
10016
  fontFamily: "acumin-pro-condensed, sans-serif",
10001
10017
  textAnchor: "middle",
10002
10018
  },
10003
10019
  tickLine: {
10004
- stroke: "#333333",
10020
+ stroke: tokens.colorNeutralStroke2,
10005
10021
  strokeWidth: "0.5px",
10006
10022
  },
10007
10023
  keyframeLine: {
@@ -10009,8 +10025,8 @@ const useStyles$q = makeStyles({
10009
10025
  strokeWidth: "0.5px",
10010
10026
  },
10011
10027
  activeFrameLine: {
10012
- stroke: "#ffffff",
10013
- strokeWidth: "0.5px",
10028
+ stroke: tokens.colorBrandForeground1,
10029
+ strokeWidth: "1px",
10014
10030
  },
10015
10031
  });
10016
10032
  const TICK_DISTANCE = 25;
@@ -10212,7 +10228,7 @@ const useStyles$o = makeStyles({
10212
10228
  display: "flex",
10213
10229
  flexDirection: "column",
10214
10230
  justifyContent: "space-between",
10215
- "& span": {
10231
+ "& div": {
10216
10232
  height: "2px",
10217
10233
  width: "100%",
10218
10234
  backgroundColor: tokens.colorNeutralForeground3,
@@ -10359,7 +10375,7 @@ const RangeSelector = () => {
10359
10375
  return (jsx("div", { ref: containerRef, className: styles.root, children: jsxs("div", { ref: scrollbarRef, className: styles.scrollbar, style: {
10360
10376
  left: `${leftPos}px`,
10361
10377
  right: `${rightPos}px`,
10362
- }, onPointerDown: handlePointerDown, onPointerMove: handlePointerMove, onPointerUp: handlePointerUp, onPointerCancel: handlePointerCancel, children: [jsx("div", { id: "left-handle", className: styles.handle, children: jsxs("div", { className: styles.handleIcon, children: [jsx("span", {}), jsx("span", {}), jsx("span", {})] }) }), jsx("div", { className: styles.label, children: Math.floor(state.fromKey) }), jsx("div", { className: styles.label, children: Math.floor(state.toKey) }), jsx("div", { id: "right-handle", className: styles.handle, children: jsxs("div", { className: styles.handleIcon, children: [jsx("span", {}), jsx("span", {}), jsx("span", {})] }) })] }) }));
10378
+ }, onPointerDown: handlePointerDown, onPointerMove: handlePointerMove, onPointerUp: handlePointerUp, onPointerCancel: handlePointerCancel, children: [jsx("div", { id: "left-handle", className: styles.handle, children: jsxs("div", { className: styles.handleIcon, children: [jsx("div", {}), jsx("div", {}), jsx("div", {})] }) }), jsx("div", { className: styles.label, children: Math.floor(state.fromKey) }), jsx("div", { className: styles.label, children: Math.floor(state.toKey) }), jsx("div", { id: "right-handle", className: styles.handle, children: jsxs("div", { className: styles.handleIcon, children: [jsx("div", {}), jsx("div", {}), jsx("div", {})] }) })] }) }));
10363
10379
  };
10364
10380
 
10365
10381
  const useStyles$n = makeStyles({
@@ -10518,16 +10534,10 @@ const BottomBar = () => {
10518
10534
  }
10519
10535
  }, [state.clipLength, observables]);
10520
10536
  const hasActiveAnimations = state.activeAnimations.length > 0;
10521
- return (jsxs("div", { className: styles.root, children: [jsx("div", { className: styles.mediaControls, children: jsx(MediaControls, { hasActiveAnimations: hasActiveAnimations, isPlaying: state.isPlaying, forwardAnimation: state.forwardAnimation, onPlayForward: handlePlayForward, onPlayBackward: handlePlayBackward, onStop: handleStop, onPrevKey: handlePrevKey, onNextKey: handleNextKey, onFirstFrame: handleFirstFrame, onLastFrame: handleLastFrame }) }), jsxs("div", { className: styles.frameDisplay, children: [jsx("span", { className: styles.frameLabel, children: "Frame:" }), jsx(SpinButton, { className: styles.spinButton, value: displayFrame, onChange: handleFrameChange, min: state.fromKey, max: state.toKey, disabled: !hasActiveAnimations })] }), jsx(RangeSelector, {}), jsxs("div", { className: styles.clipLengthSection, children: [jsx("span", { className: styles.frameLabel, children: "Clip Length:" }), jsx(SpinButton, { className: styles.spinButton, value: clipLength, onChange: handleClipLengthChange, min: 1, disabled: !hasActiveAnimations })] })] }));
10537
+ return (jsxs("div", { className: styles.root, children: [jsx("div", { className: styles.mediaControls, children: jsx(MediaControls, { hasActiveAnimations: hasActiveAnimations, isPlaying: state.isPlaying, forwardAnimation: state.forwardAnimation, onPlayForward: handlePlayForward, onPlayBackward: handlePlayBackward, onStop: handleStop, onPrevKey: handlePrevKey, onNextKey: handleNextKey, onFirstFrame: handleFirstFrame, onLastFrame: handleLastFrame }) }), jsxs("div", { className: styles.frameDisplay, children: [jsx("div", { className: styles.frameLabel, children: "Frame:" }), jsx(SpinButton, { className: styles.spinButton, value: displayFrame, onChange: handleFrameChange, min: state.fromKey, max: state.toKey, disabled: !hasActiveAnimations })] }), jsx(RangeSelector, {}), jsxs("div", { className: styles.clipLengthSection, children: [jsx("div", { className: styles.frameLabel, children: "Clip Length:" }), jsx(SpinButton, { className: styles.spinButton, value: clipLength, onChange: handleClipLengthChange, min: 1, disabled: !hasActiveAnimations })] })] }));
10522
10538
  };
10523
10539
 
10524
10540
  const useStyles$m = makeStyles({
10525
- fluentProvider: {
10526
- width: "100%",
10527
- height: "100%",
10528
- display: "flex",
10529
- flexDirection: "column",
10530
- },
10531
10541
  root: {
10532
10542
  display: "flex",
10533
10543
  flexDirection: "column",
@@ -10667,8 +10677,7 @@ const CurveEditorContent = () => {
10667
10677
  */
10668
10678
  const CurveEditor = (props) => {
10669
10679
  const { scene, target, animations, rootAnimationGroup, title, useTargetAnimations } = props;
10670
- const styles = useStyles$m();
10671
- return (jsx(FluentProvider, { theme: webDarkTheme, className: styles.fluentProvider, children: jsx(CurveEditorProvider, { scene: scene, target: target, animations: animations, rootAnimationGroup: rootAnimationGroup, title: title, useTargetAnimations: useTargetAnimations, children: jsx(CurveEditorContent, {}) }) }));
10680
+ return (jsx(CurveEditorProvider, { scene: scene, target: target, animations: animations, rootAnimationGroup: rootAnimationGroup, title: title, useTargetAnimations: useTargetAnimations, children: jsx(CurveEditorContent, {}) }));
10672
10681
  };
10673
10682
 
10674
10683
  /**
@@ -10896,7 +10905,7 @@ const AtmospherePropertiesServiceDefinition = {
10896
10905
  }
10897
10906
  const atmosphereContentRegistration = propertiesService.addSectionContent({
10898
10907
  key: "Atmosphere Properties",
10899
- predicate: (entity) => entity instanceof Atmosphere,
10908
+ predicate: (entity) => entity.getClassName?.() === "Atmosphere",
10900
10909
  content: [
10901
10910
  {
10902
10911
  section: "General",
@@ -10932,6 +10941,63 @@ const AtmospherePropertiesServiceDefinition = {
10932
10941
  },
10933
10942
  };
10934
10943
 
10944
+ function useSoundState(sound) {
10945
+ const stateChangedObservables = [
10946
+ useInterceptObservable("function", sound, "play"),
10947
+ useInterceptObservable("function", sound, "pause"),
10948
+ useInterceptObservable("function", sound, "stop"),
10949
+ ];
10950
+ const isPaused = useObservableState(useCallback(() => sound.isPaused, [sound]), ...stateChangedObservables);
10951
+ const isPlaying = useObservableState(useCallback(() => sound.isPlaying, [sound]), ...stateChangedObservables);
10952
+ return isPaused ? "Paused" : isPlaying ? "Playing" : "Stopped";
10953
+ }
10954
+ const SoundGeneralProperties = (props) => {
10955
+ const { sound } = props;
10956
+ const soundState = useSoundState(sound);
10957
+ return (jsx(Fragment, { children: jsx(TextPropertyLine, { label: "Status", value: soundState }) }));
10958
+ };
10959
+ const SoundCommandProperties = (props) => {
10960
+ const { sound } = props;
10961
+ const soundState = useSoundState(sound);
10962
+ const volume = useObservableState(useCallback(() => sound.getVolume(), [sound]), useInterceptObservable("function", sound, "setVolume"));
10963
+ return (jsxs(Fragment, { children: [jsx(ButtonLine, { label: soundState === "Playing" ? "Pause" : "Play", icon: soundState === "Playing" ? PauseRegular : PlayRegular, onClick: () => {
10964
+ if (soundState === "Playing") {
10965
+ sound.pause();
10966
+ }
10967
+ else {
10968
+ sound.play();
10969
+ }
10970
+ } }), jsx(Property, { component: SyncedSliderPropertyLine, label: "Volume", functionPath: "setVolume", value: volume, min: 0, max: 5, step: 0.1, onChange: (value) => {
10971
+ sound.setVolume(value);
10972
+ } }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Loop", target: sound, propertyKey: "loop" })] }));
10973
+ };
10974
+
10975
+ const AudioPropertiesServiceDefinition = {
10976
+ friendlyName: "Audio Properties",
10977
+ consumes: [PropertiesServiceIdentity],
10978
+ factory: (propertiesService) => {
10979
+ const soundContentRegistration = propertiesService.addSectionContent({
10980
+ key: "Sound General Properties",
10981
+ predicate: (entity) => entity instanceof Sound,
10982
+ content: [
10983
+ {
10984
+ section: "General",
10985
+ component: ({ context }) => jsx(SoundGeneralProperties, { sound: context }),
10986
+ },
10987
+ {
10988
+ section: "Commands",
10989
+ component: ({ context }) => jsx(SoundCommandProperties, { sound: context }),
10990
+ },
10991
+ ],
10992
+ });
10993
+ return {
10994
+ dispose: () => {
10995
+ soundContentRegistration.dispose();
10996
+ },
10997
+ };
10998
+ },
10999
+ };
11000
+
10935
11001
  const ArcRotateCameraTransformProperties = (props) => {
10936
11002
  const { camera, settings } = props;
10937
11003
  const [toDisplayAngle, fromDisplayAngle, useDegrees] = useAngleConverters(settings);
@@ -13350,6 +13416,23 @@ const MeshDisplayProperties = (props) => {
13350
13416
  { value: Constants.MATERIAL_CounterClockWiseSideOrientation, label: "CounterClockwise" },
13351
13417
  ] })] }));
13352
13418
  };
13419
+ const MeshMorphTargetsProperties = (props) => {
13420
+ const { mesh } = props;
13421
+ if (!mesh.morphTargetManager) {
13422
+ return null;
13423
+ }
13424
+ const morphTargets = [];
13425
+ for (let index = 0; index < mesh.morphTargetManager.numTargets; index++) {
13426
+ const target = mesh.morphTargetManager.getTarget(index);
13427
+ if (target.hasPositions) {
13428
+ morphTargets.push(target);
13429
+ }
13430
+ }
13431
+ if (morphTargets.length === 0) {
13432
+ return null;
13433
+ }
13434
+ return (jsx(Fragment, { children: morphTargets.map((target, index) => (jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: target.name || `Target ${index}`, description: `Influence of morph target "${target.name || `Target ${index}`}"`, target: target, propertyKey: "influence", min: 0, max: 1, step: 0.01 }, index))) }));
13435
+ };
13353
13436
 
13354
13437
  const NodeGeneralProperties = (props) => {
13355
13438
  const { node, selectionService } = props;
@@ -13418,6 +13501,10 @@ const NodePropertiesServiceDefinition = {
13418
13501
  section: "Display",
13419
13502
  component: ({ context }) => jsx(MeshDisplayProperties, { mesh: context }),
13420
13503
  },
13504
+ {
13505
+ section: "Morph Targets",
13506
+ component: ({ context }) => jsx(MeshMorphTargetsProperties, { mesh: context }),
13507
+ },
13421
13508
  ],
13422
13509
  });
13423
13510
  const gaussianSplattingContentRegistration = propertiesService.addSectionContent({
@@ -18626,6 +18713,66 @@ const SkeletonExplorerServiceDefinition = {
18626
18713
  },
18627
18714
  };
18628
18715
 
18716
+ const SoundExplorerServiceDefinition = {
18717
+ friendlyName: "Sound Explorer",
18718
+ consumes: [SceneExplorerServiceIdentity, SceneContextIdentity],
18719
+ factory: (sceneExplorerService, sceneContext) => {
18720
+ const scene = sceneContext.currentScene;
18721
+ if (!scene) {
18722
+ return undefined;
18723
+ }
18724
+ const soundAddedObservable = new Observable();
18725
+ const soundRemovedObservable = new Observable();
18726
+ const addSoundHook = InterceptFunction(scene.mainSoundTrack, "addSound", {
18727
+ afterCall: (sound) => soundAddedObservable.notifyObservers(sound),
18728
+ });
18729
+ const removeSoundHook = InterceptFunction(scene.mainSoundTrack, "removeSound", {
18730
+ afterCall: (sound) => soundRemovedObservable.notifyObservers(sound),
18731
+ });
18732
+ const sectionRegistration = sceneExplorerService.addSection({
18733
+ displayName: "Sounds",
18734
+ order: 1400 /* DefaultSectionsOrder.Sounds */,
18735
+ getRootEntities: () => scene.mainSoundTrack.soundCollection,
18736
+ getEntityDisplayInfo: (sound) => {
18737
+ const onChangeObservable = new Observable();
18738
+ const displayNameHookToken = InterceptProperty(sound, "name", {
18739
+ afterSet: () => {
18740
+ onChangeObservable.notifyObservers();
18741
+ },
18742
+ });
18743
+ const nameHookToken = InterceptProperty(sound, "name", {
18744
+ afterSet: () => {
18745
+ onChangeObservable.notifyObservers();
18746
+ },
18747
+ });
18748
+ return {
18749
+ get name() {
18750
+ return sound.name;
18751
+ },
18752
+ onChange: onChangeObservable,
18753
+ dispose: () => {
18754
+ nameHookToken.dispose();
18755
+ displayNameHookToken.dispose();
18756
+ onChangeObservable.clear();
18757
+ },
18758
+ };
18759
+ },
18760
+ entityIcon: () => jsx(SoundWaveCircleRegular, {}),
18761
+ getEntityAddedObservables: () => [soundAddedObservable],
18762
+ getEntityRemovedObservables: () => [soundRemovedObservable],
18763
+ });
18764
+ return {
18765
+ dispose: () => {
18766
+ addSoundHook.dispose();
18767
+ removeSoundHook.dispose();
18768
+ soundAddedObservable.clear();
18769
+ soundRemovedObservable.clear();
18770
+ sectionRegistration.dispose();
18771
+ },
18772
+ };
18773
+ },
18774
+ };
18775
+
18629
18776
  const SpriteManagerExplorerServiceDefinition = {
18630
18777
  friendlyName: "Sprite Manager Explorer",
18631
18778
  consumes: [SceneExplorerServiceIdentity, SceneContextIdentity],
@@ -19554,9 +19701,9 @@ function ShowInspector(scene, options = {}) {
19554
19701
  // Helps with managing gizmos and a shared utility layer.
19555
19702
  GizmoServiceDefinition,
19556
19703
  // Scene explorer tab and related services.
19557
- SceneExplorerServiceDefinition, NodeExplorerServiceDefinition, SkeletonExplorerServiceDefinition, MaterialExplorerServiceDefinition, TextureExplorerServiceDefinition, PostProcessExplorerServiceDefinition, RenderingPipelineExplorerServiceDefinition, EffectLayerExplorerServiceDefinition, ParticleSystemExplorerServiceDefinition, SpriteManagerExplorerServiceDefinition, AnimationGroupExplorerServiceDefinition, GuiExplorerServiceDefinition, FrameGraphExplorerServiceDefinition, AtmosphereExplorerServiceDefinition,
19704
+ SceneExplorerServiceDefinition, NodeExplorerServiceDefinition, SkeletonExplorerServiceDefinition, MaterialExplorerServiceDefinition, TextureExplorerServiceDefinition, PostProcessExplorerServiceDefinition, RenderingPipelineExplorerServiceDefinition, EffectLayerExplorerServiceDefinition, ParticleSystemExplorerServiceDefinition, SpriteManagerExplorerServiceDefinition, AnimationGroupExplorerServiceDefinition, GuiExplorerServiceDefinition, FrameGraphExplorerServiceDefinition, AtmosphereExplorerServiceDefinition, SoundExplorerServiceDefinition,
19558
19705
  // Properties pane tab and related services.
19559
- ScenePropertiesServiceDefinition, PropertiesServiceDefinition, TexturePropertiesServiceDefinition, CommonPropertiesServiceDefinition, TransformPropertiesServiceDefinition, AnimationPropertiesServiceDefinition, NodePropertiesServiceDefinition, PhysicsPropertiesServiceDefinition, SkeletonPropertiesServiceDefinition, MaterialPropertiesServiceDefinition, LightPropertiesServiceDefinition, SpritePropertiesServiceDefinition, ParticleSystemPropertiesServiceDefinition, CameraPropertiesServiceDefinition, PostProcessPropertiesServiceDefinition, RenderingPipelinePropertiesServiceDefinition, EffectLayerPropertiesServiceDefinition, FrameGraphPropertiesServiceDefinition, AnimationGroupPropertiesServiceDefinition, MetadataPropertiesServiceDefinition, AtmospherePropertiesServiceDefinition,
19706
+ ScenePropertiesServiceDefinition, PropertiesServiceDefinition, TexturePropertiesServiceDefinition, CommonPropertiesServiceDefinition, TransformPropertiesServiceDefinition, AnimationPropertiesServiceDefinition, NodePropertiesServiceDefinition, PhysicsPropertiesServiceDefinition, SkeletonPropertiesServiceDefinition, MaterialPropertiesServiceDefinition, LightPropertiesServiceDefinition, SpritePropertiesServiceDefinition, ParticleSystemPropertiesServiceDefinition, CameraPropertiesServiceDefinition, PostProcessPropertiesServiceDefinition, RenderingPipelinePropertiesServiceDefinition, EffectLayerPropertiesServiceDefinition, FrameGraphPropertiesServiceDefinition, AnimationGroupPropertiesServiceDefinition, MetadataPropertiesServiceDefinition, AtmospherePropertiesServiceDefinition, AudioPropertiesServiceDefinition,
19560
19707
  // Texture editor and related services.
19561
19708
  TextureEditorServiceDefinition,
19562
19709
  // Debug pane tab and related services.
@@ -19800,16 +19947,7 @@ function ConvertOptions(v1Options) {
19800
19947
  const sceneExplorerSectionRegistrations = additionalNodes.map((node) => sceneExplorerService.addSection({
19801
19948
  displayName: node.name,
19802
19949
  order: Number.MAX_SAFE_INTEGER,
19803
- getRootEntities: () => {
19804
- const children = node.getContent();
19805
- for (const child of children) {
19806
- const entity = child;
19807
- if (!entity.uniqueId) {
19808
- entity.uniqueId = UniqueIdGenerator.UniqueId;
19809
- }
19810
- }
19811
- return children;
19812
- },
19950
+ getRootEntities: () => node.getContent(),
19813
19951
  getEntityDisplayInfo: (entity) => {
19814
19952
  const onChangeObservable = new Observable();
19815
19953
  const nameHookToken = InterceptProperty(entity, "name", {
@@ -20398,4 +20536,4 @@ const TextAreaPropertyLine = (props) => {
20398
20536
  AttachDebugLayer();
20399
20537
 
20400
20538
  export { MakeTeachingMoment as $, Accordion as A, Button as B, CheckboxPropertyLine as C, DebugServiceIdentity as D, ExtensibleAccordion as E, useColor3Property as F, useColor4Property as G, useQuaternionProperty as H, Inspector as I, MakePropertyHook as J, useInterceptObservable as K, Link as L, MessageBar as M, NumberInputPropertyLine as N, useEventfulState as O, Popover as P, useObservableCollection as Q, useOrderedObservableCollection as R, SpinButtonPropertyLine as S, TextInputPropertyLine as T, usePollingObservable as U, Vector3PropertyLine as V, useResource as W, useAsyncResource as X, useCompactMode as Y, useSidePaneDockOverrides as Z, useAngleConverters as _, ShellServiceIdentity as a, MakeDialogTeachingMoment as a0, InterceptFunction as a1, GetPropertyDescriptor as a2, IsPropertyReadonly as a3, InterceptProperty as a4, ObservableCollection as a5, ConstructorFactory as a6, SelectionServiceIdentity as a7, SelectionServiceDefinition as a8, SettingsContextIdentity as a9, ToggleButton as aA, ChildWindow as aB, FileUploadLine as aC, FactorGradientList as aD, Color3GradientList as aE, Color4GradientList as aF, Pane as aG, BooleanBadgePropertyLine as aH, Color3PropertyLine as aI, Color4PropertyLine as aJ, HexPropertyLine as aK, LinkPropertyLine as aL, PropertyLine as aM, LineContainer as aN, PlaceholderPropertyLine as aO, StringifiedPropertyLine as aP, SwitchPropertyLine as aQ, SyncedSliderPropertyLine as aR, TextAreaPropertyLine as aS, TextPropertyLine as aT, RotationVectorPropertyLine as aU, QuaternionPropertyLine as aV, Vector2PropertyLine as aW, Vector4PropertyLine as aX, ShowInspector as aa, Checkbox as ab, Collapse as ac, ColorPickerPopup as ad, InputHexField as ae, InputHsvField as af, ComboBox as ag, DraggableLine as ah, Dropdown as ai, NumberDropdown as aj, StringDropdown as ak, FactorGradientComponent as al, Color3GradientComponent as am, Color4GradientComponent as an, ColorStepGradientComponent as ao, InfoLabel as ap, MakeLazyComponent as aq, List as ar, PositionedPopover as as, SearchBar as at, SearchBox as au, SpinButton as av, Switch as aw, SyncedSliderInput as ax, Textarea as ay, TextInput as az, SceneContextIdentity as b, useObservableState as c, AccordionSection as d, ButtonLine as e, ToolsServiceIdentity as f, useExtensionManager as g, MakePopoverTeachingMoment as h, TeachingMoment as i, SidePaneContainer as j, PropertiesServiceIdentity as k, SceneExplorerServiceIdentity as l, SettingsServiceIdentity as m, StatsServiceIdentity as n, ConvertOptions as o, AttachDebugLayer as p, DetachDebugLayer as q, NumberDropdownPropertyLine as r, StringDropdownPropertyLine as s, BoundProperty as t, useProperty as u, Property as v, LinkToEntityPropertyLine as w, Theme as x, BuiltInsExtensionFeed as y, useVector3Property as z };
20401
- //# sourceMappingURL=index-DB8sTGSM.js.map
20539
+ //# sourceMappingURL=index-CyX6FYTL.js.map