@babylonjs/inspector 8.31.0-preview → 8.31.1-preview

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -3,9 +3,9 @@ import { useMemo, useEffect, useState, useRef, useCallback, forwardRef, createCo
3
3
  import { Color3, Color4 } from '@babylonjs/core/Maths/math.color.js';
4
4
  import { Vector3, Quaternion, Matrix, Vector2, Vector4, TmpVectors } from '@babylonjs/core/Maths/math.vector.js';
5
5
  import { Observable } from '@babylonjs/core/Misc/observable.js';
6
- import { makeStyles, ToggleButton as ToggleButton$1, Button as Button$1, tokens, Link, InfoLabel as InfoLabel$1, Body1Strong, Checkbox as Checkbox$1, Body1, Accordion as Accordion$1, AccordionItem, AccordionHeader, Subtitle2Stronger, AccordionPanel, Divider, mergeClasses, TeachingPopover, TeachingPopoverSurface, TeachingPopoverHeader, TeachingPopoverBody, createLightTheme, createDarkTheme, FluentProvider, Toolbar as Toolbar$1, Tooltip, ToolbarRadioButton, SearchBox as SearchBox$1, FlatTree, FlatTreeItem, TreeItemLayout, Menu, MenuTrigger, MenuPopover, MenuList, MenuItem, Switch as Switch$1, PresenceBadge, Spinner, Dialog, DialogTrigger, DialogSurface, DialogBody, DialogTitle, TabList, Tab, DialogContent, SplitButton, MenuItemRadio, DialogActions, List as List$1, ListItem, useId, Input, SpinButton as SpinButton$1, Slider, MessageBar as MessageBar$1, MessageBarBody, MessageBarTitle, Badge, Dropdown as Dropdown$1, Option, Popover, PopoverTrigger, ColorSwatch, PopoverSurface, ColorPicker, ColorArea, ColorSlider, AlphaSlider, Textarea as Textarea$1, ToolbarButton, useComboboxFilter, Combobox, Field } from '@fluentui/react-components';
6
+ import { makeStyles, ToggleButton as ToggleButton$1, Button as Button$1, tokens, Link, InfoLabel as InfoLabel$1, Body1Strong, Checkbox as Checkbox$1, Body1, Accordion as Accordion$1, AccordionItem, AccordionHeader, Subtitle2Stronger, AccordionPanel, Divider, mergeClasses, TeachingPopover, TeachingPopoverSurface, TeachingPopoverHeader, TeachingPopoverBody, createLightTheme, createDarkTheme, FluentProvider, Toolbar as Toolbar$1, Tooltip, ToolbarRadioButton, SearchBox as SearchBox$1, FlatTree, FlatTreeItem, TreeItemLayout, Menu, MenuTrigger, MenuPopover, MenuList, MenuItem, Switch as Switch$1, PresenceBadge, Spinner, Dialog, DialogTrigger, DialogSurface, DialogBody, DialogTitle, TabList, Tab, DialogContent, SplitButton, MenuItemRadio, DialogActions, List as List$1, ListItem, Badge, useId, Input, SpinButton as SpinButton$1, Slider, MessageBar as MessageBar$1, MessageBarBody, MessageBarTitle, Dropdown as Dropdown$1, Option, Popover, PopoverTrigger, ColorSwatch, PopoverSurface, ColorPicker, ColorArea, ColorSlider, AlphaSlider, Textarea as Textarea$1, ToolbarButton, useComboboxFilter, Combobox, Field } from '@fluentui/react-components';
7
7
  export { Link } from '@fluentui/react-components';
8
- import { ChevronCircleRight20Regular, ChevronCircleDown20Regular, CopyRegular, PanelLeftExpandRegular, PanelLeftContractRegular, PanelRightExpandRegular, PanelRightContractRegular, DocumentTextRegular, createFluentIcon, FilterRegular, GlobeRegular, ArrowExpandAllRegular, CubeTreeRegular, BugRegular, SettingsRegular, ArrowUploadRegular, DataBarHorizontalRegular, WrenchRegular, AppsAddInRegular, DismissRegular, WeatherSunnyRegular, WeatherMoonRegular, ErrorCircleRegular, ArrowRotateClockwiseRegular, ArrowExpandRegular, SelectObjectRegular, CubeRegular, SaveRegular, ArrowUndoRegular, BracesRegular, BracesDismiss16Regular, DeleteRegular, EyeOffFilled, EyeFilled, ArrowMoveFilled, StopFilled, PlayFilled, StackRegular, FilmstripRegular, PauseFilled, WeatherSunnyLowFilled, LayerRegular, FrameRegular, PlayRegular, AppGenericRegular, MyLocationRegular, CameraRegular, LightbulbRegular, BorderOutsideRegular, BorderNoneRegular, EyeRegular, EyeOffRegular, VideoFilled, VideoRegular, FlashlightRegular, FlashlightOffRegular, DropRegular, BlurRegular, PipelineRegular, PersonWalkingRegular, DataLineRegular, PersonSquareRegular, LayerDiagonalPersonRegular, ImageEditRegular, ImageRegular, TargetRegular, DeleteFilled } from '@fluentui/react-icons';
8
+ import { ChevronCircleRight20Regular, ChevronCircleDown20Regular, CopyRegular, PanelLeftExpandRegular, PanelLeftContractRegular, PanelRightExpandRegular, PanelRightContractRegular, DocumentTextRegular, createFluentIcon, FilterRegular, GlobeRegular, ArrowExpandAllRegular, CubeTreeRegular, BugRegular, SettingsRegular, ArrowUploadRegular, DataBarHorizontalRegular, WrenchRegular, AppsAddInRegular, DismissRegular, WeatherSunnyRegular, WeatherMoonRegular, ErrorCircleRegular, ArrowRotateClockwiseRegular, ArrowExpandRegular, SelectObjectRegular, CubeRegular, SaveRegular, ArrowUndoRegular, BracesRegular, BracesDismiss16Regular, DeleteRegular, EyeOffFilled, EyeFilled, ArrowMoveFilled, StopFilled, PlayFilled, StackRegular, FilmstripRegular, PauseFilled, WeatherSunnyLowFilled, LayerRegular, FrameRegular, PlayRegular, AppGenericRegular, MyLocationRegular, CameraRegular, LightbulbRegular, BorderOutsideRegular, BorderNoneRegular, EyeRegular, EyeOffRegular, VideoFilled, VideoRegular, FlashlightRegular, FlashlightOffRegular, DropRegular, BlurRegular, PipelineRegular, PersonWalkingRegular, DataLineRegular, PersonSquareRegular, LayerDiagonalPersonRegular, ImageEditRegular, ImageRegular, TargetRegular, PersonFeedbackRegular, DeleteFilled } from '@fluentui/react-icons';
9
9
  import { Collapse as Collapse$1 } from '@fluentui/react-motion-components-preview';
10
10
  import '@babylonjs/core/Misc/typeStore.js';
11
11
  import { useLocalStorage, useTernaryDarkMode } from 'usehooks-ts';
@@ -94,6 +94,7 @@ import { CubeTexture } from '@babylonjs/core/Materials/Textures/cubeTexture.js';
94
94
  import { ImageProcessingConfiguration } from '@babylonjs/core/Materials/imageProcessingConfiguration.js';
95
95
  import { Skeleton } from '@babylonjs/core/Bones/skeleton.js';
96
96
  import { Sprite } from '@babylonjs/core/Sprites/sprite.js';
97
+ import { SpriteManager } from '@babylonjs/core/Sprites/spriteManager.js';
97
98
  import { BaseTexture } from '@babylonjs/core/Materials/Textures/baseTexture.js';
98
99
  import { MultiRenderTarget } from '@babylonjs/core/Materials/Textures/multiRenderTarget.js';
99
100
  import { RenderTargetTexture } from '@babylonjs/core/Materials/Textures/renderTargetTexture.js';
@@ -699,15 +700,53 @@ function HandleKeyDown(event) {
699
700
  event.preventDefault();
700
701
  }
701
702
  }
703
+ /**
704
+ * Fluent's CalculatePrecision function
705
+ * https://github.com/microsoft/fluentui/blob/dcbf775d37938eacffa37922fc0b43a3cdd5753f/packages/utilities/src/math.ts#L91C1
706
+ *
707
+ * Calculates a number's precision based on the number of trailing
708
+ * zeros if the number does not have a decimal indicated by a negative
709
+ * precision. Otherwise, it calculates the number of digits after
710
+ * the decimal point indicated by a positive precision.
711
+ *
712
+ * @param value - the value to determine the precision of
713
+ * @returns the calculated precision
714
+ */
715
+ function CalculatePrecision(value) {
716
+ /**
717
+ * Group 1:
718
+ * [1-9]([0]+$) matches trailing zeros
719
+ * Group 2:
720
+ * \.([0-9]*) matches all digits after a decimal point.
721
+ */ const groups = /[1-9]([0]+$)|\.([0-9]*)/.exec(String(value));
722
+ if (!groups) {
723
+ return 0;
724
+ }
725
+ if (groups[1]) {
726
+ return -groups[1].length;
727
+ }
728
+ if (groups[2]) {
729
+ return groups[2].length;
730
+ }
731
+ return 0;
732
+ }
733
+ const HEX_REGEX = RegExp(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}|[A-Fa-f0-9]{8})$/);
734
+ function ColorHexValidatorFn(val) {
735
+ return val != "" && HEX_REGEX.test(val);
736
+ }
702
737
 
703
738
  const usePropertyLineStyles = makeStyles({
704
739
  container: {
705
740
  width: "100%",
706
741
  display: "flex",
707
742
  flexDirection: "column", // Stack line + expanded content
743
+ minHeight: CustomTokens.lineHeight,
744
+ boxSizing: "border-box",
745
+ justifyContent: "center",
746
+ paddingTop: tokens.spacingVerticalXXS,
747
+ paddingBottom: tokens.spacingVerticalXXS,
708
748
  },
709
749
  baseLine: {
710
- height: CustomTokens.lineHeight,
711
750
  display: "flex",
712
751
  alignItems: "center",
713
752
  justifyContent: "flex-start",
@@ -741,6 +780,9 @@ const usePropertyLineStyles = makeStyles({
741
780
  copy: {
742
781
  marginRight: CustomTokens.rightAlignOffset, // Accounts for the padding baked into fluent button / ensures propertyLine looks visually aligned at the right
743
782
  },
783
+ expandedContentDiv: {
784
+ overflow: "hidden",
785
+ },
744
786
  });
745
787
  /**
746
788
  * A reusable component that renders a property line with a label and child content, and an optional description, copy button, and expandable section.
@@ -778,7 +820,7 @@ const PropertyLine = forwardRef((props, ref) => {
778
820
  cachedVal.current = props.value;
779
821
  props.onChange(null);
780
822
  }
781
- }, title: "Toggle null state" })), processedChildren, onCopy && !disableCopy && (jsx(Button, { className: classes.copy, title: "Copy to clipboard", appearance: "transparent", icon: CopyRegular, onClick: () => copyCommandToClipboard(onCopy()) }))] })] }), expandedContent && (jsx(Collapse, { visible: !!expanded, children: jsx("div", { children: expandedContent }) }))] }));
823
+ }, title: "Toggle null state" })), processedChildren, onCopy && !disableCopy && (jsx(Button, { className: classes.copy, title: "Copy to clipboard", appearance: "transparent", icon: CopyRegular, onClick: () => copyCommandToClipboard(onCopy()) }))] })] }), expandedContent && (jsx(Collapse, { visible: !!expanded, children: jsx("div", { className: classes.expandedContentDiv, children: expandedContent }) }))] }));
782
824
  });
783
825
  const LineContainer = forwardRef((props, ref) => {
784
826
  const classes = usePropertyLineStyles();
@@ -813,11 +855,11 @@ const LinkToEntityPropertyLine = (props) => {
813
855
  !linkedEntity.reservedDataStore?.hidden && jsx(LinkPropertyLine, { ...rest, value: linkedEntity.name, onLink: () => (selectionService.selectedEntity = linkedEntity) }));
814
856
  };
815
857
 
816
- const useStyles$g = makeStyles({
858
+ const useStyles$i = makeStyles({
817
859
  accordion: {
818
860
  overflowX: "hidden",
819
861
  overflowY: "auto",
820
- paddingTop: tokens.spacingVerticalM, // ensures the first section header has the same padding as the others (due to divider)
862
+ paddingBottom: tokens.spacingVerticalM, // bottom padding since there is no divider at the bottom
821
863
  display: "flex",
822
864
  flexDirection: "column",
823
865
  height: "100%",
@@ -834,12 +876,12 @@ const useStyles$g = makeStyles({
834
876
  });
835
877
  const AccordionSection = (props) => {
836
878
  AccordionSection.displayName = "AccordionSection";
837
- const classes = useStyles$g();
879
+ const classes = useStyles$i();
838
880
  return jsx("div", { className: classes.panelDiv, children: props.children });
839
881
  };
840
882
  const Accordion = (props) => {
841
883
  Accordion.displayName = "Accordion";
842
- const classes = useStyles$g();
884
+ const classes = useStyles$i();
843
885
  const { children, ...rest } = props;
844
886
  const validChildren = useMemo(() => {
845
887
  return (Children.map(children, (child) => {
@@ -880,8 +922,8 @@ const Accordion = (props) => {
880
922
  setOpenItems((prev) => prev.filter((item) => item !== data.value));
881
923
  }
882
924
  }, []);
883
- return (jsx(Accordion$1, { className: classes.accordion, collapsible: true, multiple: true, onToggle: onToggle, openItems: openItems, ...rest, children: validChildren.map((child) => {
884
- return (jsxs(AccordionItem, { value: child.title, children: [jsx(AccordionHeader, { children: jsx(Subtitle2Stronger, { children: child.title }) }), jsx(AccordionPanel, { children: jsx("div", { className: classes.panelDiv, children: child.content }) }), jsx(Divider, { inset: true, className: classes.divider })] }, child.content.key));
925
+ return (jsx(Accordion$1, { className: classes.accordion, collapsible: true, multiple: true, onToggle: onToggle, openItems: openItems, ...rest, children: validChildren.map((child, index) => {
926
+ return (jsxs(AccordionItem, { value: child.title, children: [jsx(AccordionHeader, { children: jsx(Subtitle2Stronger, { children: child.title }) }), jsx(AccordionPanel, { children: jsx("div", { className: classes.panelDiv, children: child.content }) }), index < validChildren.length - 1 && jsx(Divider, { inset: true, className: classes.divider })] }, child.content.key));
885
927
  }) }));
886
928
  };
887
929
 
@@ -889,7 +931,7 @@ function AsReadonlyArray(array) {
889
931
  return array;
890
932
  }
891
933
  // eslint-disable-next-line @typescript-eslint/naming-convention
892
- const useStyles$f = makeStyles({
934
+ const useStyles$h = makeStyles({
893
935
  rootDiv: {
894
936
  flex: 1,
895
937
  overflow: "hidden",
@@ -898,7 +940,7 @@ const useStyles$f = makeStyles({
898
940
  },
899
941
  });
900
942
  function ExtensibleAccordion(props) {
901
- const classes = useStyles$f();
943
+ const classes = useStyles$h();
902
944
  const { children, sections, sectionContent, context } = props;
903
945
  const defaultSections = useMemo(() => {
904
946
  const defaultSections = [];
@@ -986,7 +1028,7 @@ function ExtensibleAccordion(props) {
986
1028
  })] })) }));
987
1029
  }
988
1030
 
989
- const useStyles$e = makeStyles({
1031
+ const useStyles$g = makeStyles({
990
1032
  paneRootDiv: {
991
1033
  display: "flex",
992
1034
  flex: 1,
@@ -999,12 +1041,12 @@ const useStyles$e = makeStyles({
999
1041
  */
1000
1042
  const SidePaneContainer = forwardRef((props, ref) => {
1001
1043
  const { className, ...rest } = props;
1002
- const classes = useStyles$e();
1044
+ const classes = useStyles$g();
1003
1045
  return (jsx("div", { className: mergeClasses(classes.paneRootDiv, className), ref: ref, ...rest, children: props.children }));
1004
1046
  });
1005
1047
 
1006
1048
  // eslint-disable-next-line @typescript-eslint/naming-convention
1007
- const useStyles$d = makeStyles({
1049
+ const useStyles$f = makeStyles({
1008
1050
  extensionTeachingPopover: {
1009
1051
  maxWidth: "320px",
1010
1052
  },
@@ -1015,7 +1057,7 @@ const useStyles$d = makeStyles({
1015
1057
  * @returns The teaching moment popover.
1016
1058
  */
1017
1059
  const TeachingMoment = ({ shouldDisplay, positioningRef, onOpenChange, title, description }) => {
1018
- const classes = useStyles$d();
1060
+ const classes = useStyles$f();
1019
1061
  return (jsx(TeachingPopover, { appearance: "brand", open: shouldDisplay, positioning: { positioningRef }, onOpenChange: onOpenChange, children: jsxs(TeachingPopoverSurface, { className: classes.extensionTeachingPopover, children: [jsx(TeachingPopoverHeader, { children: title }), jsx(TeachingPopoverBody, { children: description })] }) }));
1020
1062
  };
1021
1063
 
@@ -1302,13 +1344,13 @@ function ConstructorFactory(constructor) {
1302
1344
  }
1303
1345
 
1304
1346
  // eslint-disable-next-line @typescript-eslint/naming-convention
1305
- const useStyles$c = makeStyles({
1347
+ const useStyles$e = makeStyles({
1306
1348
  placeholderDiv: {
1307
1349
  padding: `${tokens.spacingVerticalM} ${tokens.spacingHorizontalM}`,
1308
1350
  },
1309
1351
  });
1310
1352
  const PropertiesPane = (props) => {
1311
- const classes = useStyles$c();
1353
+ const classes = useStyles$e();
1312
1354
  const entity = props.context;
1313
1355
  return entity != null ? (jsx(ExtensibleAccordion, { ...props })) : (jsx("div", { className: classes.placeholderDiv, children: jsx(Body1Strong, { italic: true, children: "No entity selected." }) }));
1314
1356
  };
@@ -1409,7 +1451,7 @@ const Theme = (props) => {
1409
1451
  const RootComponentServiceIdentity = Symbol("RootComponent");
1410
1452
  const ShellServiceIdentity = Symbol("ShellService");
1411
1453
  // eslint-disable-next-line @typescript-eslint/naming-convention
1412
- const useStyles$b = makeStyles({
1454
+ const useStyles$d = makeStyles({
1413
1455
  mainView: {
1414
1456
  flex: 1,
1415
1457
  display: "flex",
@@ -1444,12 +1486,14 @@ const useStyles$b = makeStyles({
1444
1486
  barLeft: {
1445
1487
  marginRight: "auto",
1446
1488
  display: "flex",
1489
+ alignItems: "center",
1447
1490
  flexDirection: "row",
1448
1491
  columnGap: tokens.spacingHorizontalSNudge,
1449
1492
  },
1450
1493
  barRight: {
1451
1494
  marginLeft: "auto",
1452
1495
  display: "flex",
1496
+ alignItems: "center",
1453
1497
  flexDirection: "row-reverse",
1454
1498
  columnGap: tokens.spacingHorizontalSNudge,
1455
1499
  },
@@ -1554,7 +1598,7 @@ const useStyles$b = makeStyles({
1554
1598
  },
1555
1599
  });
1556
1600
  const PaneHeader = ({ title }) => {
1557
- const classes = useStyles$b();
1601
+ const classes = useStyles$d();
1558
1602
  if (!title) {
1559
1603
  return null;
1560
1604
  }
@@ -1562,7 +1606,7 @@ const PaneHeader = ({ title }) => {
1562
1606
  };
1563
1607
  // This is a wrapper for an item in a toolbar that simply adds a teaching moment, which is useful for dynamically added items, possibly from extensions.
1564
1608
  const ToolbarItem = ({ location, alignment, id, component: Component, displayName: displayName, suppressTeachingMoment }) => {
1565
- const classes = useStyles$b();
1609
+ const classes = useStyles$d();
1566
1610
  const useTeachingMoment = useMemo(() => MakePopoverTeachingMoment(`Bar/${location}/${alignment}/${displayName ?? id}`), [displayName, id]);
1567
1611
  const teachingMoment = useTeachingMoment(suppressTeachingMoment);
1568
1612
  return (jsxs(Fragment, { children: [jsx(TeachingMoment, { ...teachingMoment, shouldDisplay: teachingMoment.shouldDisplay && !suppressTeachingMoment, title: displayName ?? "Extension", description: `The "${displayName ?? id}" extension can be accessed here.` }), jsx("div", { className: classes.barItem, ref: teachingMoment.targetRef, children: jsx(Component, {}) })] }));
@@ -1570,7 +1614,7 @@ const ToolbarItem = ({ location, alignment, id, component: Component, displayNam
1570
1614
  // TODO: Handle overflow, possibly via https://react.fluentui.dev/?path=/docs/components-overflow--docs with priority.
1571
1615
  // This component just renders a toolbar with left aligned toolbar items on the left and right aligned toolbar items on the right.
1572
1616
  const Toolbar = ({ location, components }) => {
1573
- const classes = useStyles$b();
1617
+ const classes = useStyles$d();
1574
1618
  const leftComponents = useMemo(() => components.filter((entry) => entry.horizontalLocation === "left"), [components]);
1575
1619
  const rightComponents = useMemo(() => components.filter((entry) => entry.horizontalLocation === "right"), [components]);
1576
1620
  return (jsx(Fragment, { children: components.length > 0 && (jsxs("div", { className: `${classes.bar} ${location === "top" ? classes.barTop : null}`, children: [jsx("div", { className: classes.barLeft, children: leftComponents.map((entry) => (jsx(ToolbarItem, { location: location, alignment: entry.horizontalLocation, id: entry.key, component: entry.component, displayName: entry.displayName, suppressTeachingMoment: entry.suppressTeachingMoment }, entry.key))) }), jsx("div", { className: classes.barRight, children: rightComponents.map((entry) => (jsx(ToolbarItem, { location: location, alignment: entry.horizontalLocation, id: entry.key, component: entry.component, displayName: entry.displayName, suppressTeachingMoment: entry.suppressTeachingMoment }, entry.key))) })] })) }));
@@ -1580,7 +1624,7 @@ const SidePaneTab = (props) => {
1580
1624
  const { alignment, id, isSelected,
1581
1625
  // eslint-disable-next-line @typescript-eslint/naming-convention
1582
1626
  icon: Icon, title, suppressTeachingMoment, } = props;
1583
- const classes = useStyles$b();
1627
+ const classes = useStyles$d();
1584
1628
  const useTeachingMoment = useMemo(() => MakePopoverTeachingMoment(`Pane/${alignment}/${title ?? id}`), [title, id]);
1585
1629
  const teachingMoment = useTeachingMoment(suppressTeachingMoment);
1586
1630
  const tabClass = mergeClasses(classes.tab, isSelected ? undefined : classes.unselectedTab);
@@ -1593,7 +1637,7 @@ const SidePaneTab = (props) => {
1593
1637
  // In "compact" mode, the tab list is integrated into the pane itself.
1594
1638
  // In "full" mode, the returned tab list is later injected into the toolbar.
1595
1639
  function usePane(alignment, defaultWidth, minWidth, topPanes, bottomPanes, toolbarMode, topBarItems, bottomBarItems) {
1596
- const classes = useStyles$b();
1640
+ const classes = useStyles$d();
1597
1641
  const [topSelectedTab, setTopSelectedTab] = useState();
1598
1642
  const [bottomSelectedTab, setBottomSelectedTab] = useState();
1599
1643
  const [collapsed, setCollapsed] = useState(false);
@@ -1708,7 +1752,7 @@ function MakeShellServiceDefinition({ leftPaneDefaultWidth = 350, leftPaneMinWid
1708
1752
  const bottomRightPaneCollection = new ObservableCollection();
1709
1753
  const centralContentCollection = new ObservableCollection();
1710
1754
  const rootComponent = () => {
1711
- const classes = useStyles$b();
1755
+ const classes = useStyles$d();
1712
1756
  const topBarItems = useOrderedObservableCollection(topBarItemCollection);
1713
1757
  const bottomBarItems = useOrderedObservableCollection(bottomBarItemCollection);
1714
1758
  const topLeftPanes = useOrderedObservableCollection(topLeftPaneCollection);
@@ -1942,7 +1986,7 @@ function ExpandOrCollapseAll(treeItem, open, openItems) {
1942
1986
  TraverseGraph([treeItem], (treeItem) => treeItem.children, (treeItem) => addOrRemove(treeItem.type === "entity" ? treeItem.entity.uniqueId : treeItem.sectionName));
1943
1987
  }
1944
1988
  // eslint-disable-next-line @typescript-eslint/naming-convention
1945
- const useStyles$a = makeStyles({
1989
+ const useStyles$c = makeStyles({
1946
1990
  rootDiv: {
1947
1991
  flex: 1,
1948
1992
  overflow: "hidden",
@@ -1961,6 +2005,16 @@ const useStyles$a = makeStyles({
1961
2005
  sceneTreeItemLayout: {
1962
2006
  padding: 0,
1963
2007
  },
2008
+ treeItemLayoutAside: {
2009
+ gap: 0,
2010
+ paddingLeft: tokens.spacingHorizontalS,
2011
+ paddingRight: tokens.spacingHorizontalS,
2012
+ },
2013
+ treeItemLayoutMain: {
2014
+ flex: "1 1 0",
2015
+ overflow: "hidden",
2016
+ textOverflow: "ellipsis",
2017
+ },
1964
2018
  });
1965
2019
  const ActionCommand = (props) => {
1966
2020
  const { command } = props;
@@ -1996,7 +2050,7 @@ function MakeCommandElement(command, isPlaceholder) {
1996
2050
  }
1997
2051
  const SceneTreeItem = (props) => {
1998
2052
  const { isSelected, select } = props;
1999
- const classes = useStyles$a();
2053
+ const classes = useStyles$c();
2000
2054
  return (jsx(FlatTreeItem, { value: "scene", itemType: "leaf", parentValue: undefined, "aria-level": 1, "aria-setsize": 1, "aria-posinset": 1, onClick: select, children: jsx(TreeItemLayout, { iconBefore: jsx(GlobeRegular, {}), className: classes.sceneTreeItemLayout, style: isSelected ? { backgroundColor: tokens.colorNeutralBackground1Selected } : undefined, children: jsx(Body1Strong, { wrap: false, truncate: true, children: "Scene" }) }) }, "scene"));
2001
2055
  };
2002
2056
  const SectionTreeItem = (props) => {
@@ -2007,6 +2061,7 @@ const SectionTreeItem = (props) => {
2007
2061
  };
2008
2062
  const EntityTreeItem = (props) => {
2009
2063
  const { entityItem, isSelected, select, isFiltering, commandProviders, expandAll, collapseAll } = props;
2064
+ const classes = useStyles$c();
2010
2065
  const hasChildren = !!entityItem.children?.length;
2011
2066
  const displayInfo = useResource(useCallback(() => {
2012
2067
  const displayInfo = entityItem.getDisplayInfo();
@@ -2087,15 +2142,15 @@ const EntityTreeItem = (props) => {
2087
2142
  // Disable manual expand/collapse when a filter is active.
2088
2143
  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, children: jsx(TreeItemLayout, { iconBefore: entityItem.icon ? jsx(entityItem.icon, { entity: entityItem.entity }) : null, style: isSelected ? { backgroundColor: tokens.colorNeutralBackground1Selected } : undefined, actions: actions, aside: {
2089
2144
  // Match the gap and padding of the actions.
2090
- style: { gap: 0, paddingLeft: tokens.spacingHorizontalS, paddingRight: tokens.spacingHorizontalS },
2145
+ className: classes.treeItemLayoutAside,
2091
2146
  children: aside,
2092
2147
  }, main: {
2093
2148
  // Prevent the "main" content (the Body1 below) from growing too large and pushing the actions/aside out of view.
2094
- style: { flex: "1 1 0", overflow: "hidden", textOverflow: "ellipsis" },
2149
+ className: classes.treeItemLayoutMain,
2095
2150
  }, children: jsx(Body1, { wrap: false, truncate: true, children: name }) }) }, entityItem.entity.uniqueId) }), jsx(MenuPopover, { hidden: !hasChildren, children: jsxs(MenuList, { children: [jsx(MenuItem, { onClick: expandAll, children: jsx(Body1, { children: "Expand All" }) }), jsx(MenuItem, { onClick: collapseAll, children: jsx(Body1, { children: "Collapse All" }) })] }) })] }));
2096
2151
  };
2097
2152
  const SceneExplorer = (props) => {
2098
- const classes = useStyles$a();
2153
+ const classes = useStyles$c();
2099
2154
  const { sections, commandProviders, scene, selectedEntity } = props;
2100
2155
  const [openItems, setOpenItems] = useState(new Set());
2101
2156
  const [sceneVersion, setSceneVersion] = useState(0);
@@ -2840,14 +2895,14 @@ const TextPropertyLine = (props) => {
2840
2895
  return (jsx(PropertyLine, { ...props, children: jsx(Body1, { title: title, children: value }) }));
2841
2896
  };
2842
2897
 
2843
- const useStyles$9 = makeStyles({
2898
+ const useStyles$b = makeStyles({
2844
2899
  pinnedStatsPane: {
2845
2900
  flex: "0 1 auto",
2846
2901
  paddingBottom: tokens.spacingHorizontalM,
2847
2902
  },
2848
2903
  });
2849
2904
  const StatsPane = (props) => {
2850
- const classes = useStyles$9();
2905
+ const classes = useStyles$b();
2851
2906
  const scene = props.context;
2852
2907
  const engine = scene.getEngine();
2853
2908
  const fps = useObservableState(() => Math.round(engine.getFps()), engine.onBeginFrameObservable);
@@ -3018,19 +3073,19 @@ const DefaultInspectorExtensionFeed = new BuiltInsExtensionFeed("Inspector", [
3018
3073
  name: "Export Tools",
3019
3074
  description: "Adds new features to enable exporting Babylon assets such as .gltf, .glb, .babylon, and more.",
3020
3075
  keywords: ["export", "gltf", "glb", "babylon", "exporter", "tools"],
3021
- getExtensionModuleAsync: async () => await import('./exportService-Cu9CXW_A.js'),
3076
+ getExtensionModuleAsync: async () => await import('./exportService-CFGdt7eg.js'),
3022
3077
  },
3023
3078
  {
3024
3079
  name: "Capture Tools",
3025
3080
  description: "Adds new features to enable capturing screenshots, GIFs, videos, and more.",
3026
3081
  keywords: ["capture", "screenshot", "gif", "video", "tools"],
3027
- getExtensionModuleAsync: async () => await import('./captureService-CuhjVz1y.js'),
3082
+ getExtensionModuleAsync: async () => await import('./captureService-EFE-C_G5.js'),
3028
3083
  },
3029
3084
  {
3030
3085
  name: "Import Tools",
3031
3086
  description: "Adds new features related to importing Babylon assets.",
3032
3087
  keywords: ["import", "tools"],
3033
- getExtensionModuleAsync: async () => await import('./importService-BETG4FuQ.js'),
3088
+ getExtensionModuleAsync: async () => await import('./importService-DEy_vKyx.js'),
3034
3089
  },
3035
3090
  ]);
3036
3091
 
@@ -3451,7 +3506,7 @@ class ServiceContainer {
3451
3506
  }
3452
3507
 
3453
3508
  // eslint-disable-next-line @typescript-eslint/naming-convention
3454
- const useStyles$8 = makeStyles({
3509
+ const useStyles$a = makeStyles({
3455
3510
  extensionButton: {},
3456
3511
  extensionsDialogSurface: {
3457
3512
  height: "auto",
@@ -3496,7 +3551,7 @@ const useStyles$8 = makeStyles({
3496
3551
  // eslint-disable-next-line @typescript-eslint/naming-convention
3497
3552
  const useTeachingMoment = MakePopoverTeachingMoment("Extensions");
3498
3553
  const ExtensionDetails = memo((props) => {
3499
- const classes = useStyles$8();
3554
+ const classes = useStyles$a();
3500
3555
  const [canInstall, setCanInstall] = useState(false);
3501
3556
  const [canUninstall, setCanUninstall] = useState(false);
3502
3557
  const [isStateChanging, setIsStateChanging] = useState(false);
@@ -3540,7 +3595,7 @@ const ExtensionListServiceDefinition = {
3540
3595
  suppressTeachingMoment: true,
3541
3596
  order: -200,
3542
3597
  component: () => {
3543
- const classes = useStyles$8();
3598
+ const classes = useStyles$a();
3544
3599
  const [selectedTab, setSelectedTab] = useState("available");
3545
3600
  const extensionManager = useExtensionManager();
3546
3601
  const [extensions, setExtensions] = useState([]);
@@ -3569,7 +3624,7 @@ const ExtensionListServiceDefinition = {
3569
3624
  },
3570
3625
  };
3571
3626
 
3572
- const useStyles$7 = makeStyles({
3627
+ const useStyles$9 = makeStyles({
3573
3628
  themeButton: {
3574
3629
  margin: `${tokens.spacingVerticalXXS} 0`,
3575
3630
  },
@@ -3588,7 +3643,7 @@ const ThemeSelectorServiceDefinition = {
3588
3643
  suppressTeachingMoment: true,
3589
3644
  order: -300,
3590
3645
  component: () => {
3591
- const classes = useStyles$7();
3646
+ const classes = useStyles$9();
3592
3647
  const { isDarkMode, themeMode, setThemeMode } = useThemeMode();
3593
3648
  const onSelectedThemeChange = useCallback((e, data) => {
3594
3649
  setThemeMode(data.checkedItems.includes("System") ? "system" : data.checkedItems[0].toLocaleLowerCase());
@@ -3606,7 +3661,7 @@ const ThemeSelectorServiceDefinition = {
3606
3661
  };
3607
3662
 
3608
3663
  // eslint-disable-next-line @typescript-eslint/naming-convention
3609
- const useStyles$6 = makeStyles({
3664
+ const useStyles$8 = makeStyles({
3610
3665
  app: {
3611
3666
  colorScheme: "light dark",
3612
3667
  flexGrow: 1,
@@ -3642,7 +3697,7 @@ function MakeModularTool(options) {
3642
3697
  SetThemeMode(themeMode);
3643
3698
  }
3644
3699
  const modularToolRootComponent = () => {
3645
- const classes = useStyles$6();
3700
+ const classes = useStyles$8();
3646
3701
  const [extensionManagerContext, setExtensionManagerContext] = useState();
3647
3702
  const [requiredExtensions, setRequiredExtensions] = useState();
3648
3703
  const [requiredExtensionsDeferred, setRequiredExtensionsDeferred] = useState();
@@ -3845,7 +3900,7 @@ const MeshIcon = createFluentIcon("Mesh", "16", '<path d="M14.03,3.54l-5.11-2.07
3845
3900
  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" />');
3846
3901
  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"/>');
3847
3902
 
3848
- const useStyles$5 = makeStyles({
3903
+ const useStyles$7 = makeStyles({
3849
3904
  coordinatesModeButton: {
3850
3905
  margin: `0 ${tokens.spacingVerticalXS}`,
3851
3906
  },
@@ -3855,7 +3910,7 @@ const useStyles$5 = makeStyles({
3855
3910
  });
3856
3911
  const GizmoToolbar = (props) => {
3857
3912
  const { scene, entity, gizmoService } = props;
3858
- const classes = useStyles$5();
3913
+ const classes = useStyles$7();
3859
3914
  const gizmoManager = useResource(useCallback(() => {
3860
3915
  const utilityLayerRef = gizmoService.getUtilityLayer(scene);
3861
3916
  const keepDepthUtilityLayerRef = gizmoService.getUtilityLayer(scene, "keepDepth");
@@ -3970,6 +4025,32 @@ const GizmoToolbarServiceDefinition = {
3970
4025
  },
3971
4026
  };
3972
4027
 
4028
+ const useStyles$6 = makeStyles({
4029
+ badge: {
4030
+ margin: tokens.spacingHorizontalXXS,
4031
+ fontFamily: "monospace",
4032
+ },
4033
+ });
4034
+ const MiniStatsServiceDefinition = {
4035
+ friendlyName: "Mini Stats",
4036
+ consumes: [SceneContextIdentity, ShellServiceIdentity],
4037
+ factory: (sceneContext, shellService) => {
4038
+ shellService.addToolbarItem({
4039
+ key: "Mini Stats",
4040
+ verticalLocation: "bottom",
4041
+ horizontalLocation: "right",
4042
+ suppressTeachingMoment: true,
4043
+ component: () => {
4044
+ const classes = useStyles$6();
4045
+ const scene = useObservableState(() => sceneContext.currentScene, sceneContext.currentSceneObservable);
4046
+ const engine = scene?.getEngine();
4047
+ const fps = useObservableState(() => (engine ? Math.round(engine.getFps()) : null), engine?.onBeginFrameObservable);
4048
+ return fps != null ? jsx(Badge, { appearance: "outline", className: classes.badge, children: `${fps} fps` }) : null;
4049
+ },
4050
+ });
4051
+ },
4052
+ };
4053
+
3973
4054
  const TargetedAnimationGeneralProperties = (props) => {
3974
4055
  const { selectionService } = props;
3975
4056
  return (jsx(Fragment, { children: jsx(LinkToEntityPropertyLine, { label: "Target", description: "The entity animated by this animation.", entity: props.targetedAnimation.target, selectionService: selectionService }) }));
@@ -4030,6 +4111,7 @@ const SpinButton = (props) => {
4030
4111
  const lastCommittedValue = useRef(props.value);
4031
4112
  // step and forceInt are not mutually exclusive since there could be cases where you want to forceInt but have spinButton jump >1 int per spin
4032
4113
  const step = props.step != undefined ? props.step : props.forceInt ? 1 : undefined;
4114
+ const precision = Math.min(4, step !== undefined ? CalculatePrecision(step) : 2); // If no step, set precision to 2. Regardless, cap precision at 4 to avoid wild numbers
4033
4115
  useEffect(() => {
4034
4116
  if (props.value !== lastCommittedValue.current) {
4035
4117
  lastCommittedValue.current = props.value;
@@ -4067,51 +4149,8 @@ const SpinButton = (props) => {
4067
4149
  };
4068
4150
  const id = useId("spin-button");
4069
4151
  const mergedClassName = mergeClasses(classes.input, !validateValue(value) ? classes.invalid : "", props.className);
4070
- return (jsxs("div", { className: classes.container, children: [props.infoLabel && jsx(InfoLabel, { ...props.infoLabel, htmlFor: id }), jsx(SpinButton$1, { ...props, input: { className: classes.inputSlot }, step: step, id: id, size: "medium", precision: CalculatePrecision(step ?? 1.01), displayValue: props.unit ? `${PrecisionRound(value, CalculatePrecision(step ?? 1.01))} ${props.unit}` : undefined, value: value, onChange: handleChange, onKeyUp: handleKeyUp, onKeyDown: HandleKeyDown, onBlur: HandleOnBlur, className: mergedClassName })] }));
4152
+ return (jsxs("div", { className: classes.container, children: [props.infoLabel && jsx(InfoLabel, { ...props.infoLabel, htmlFor: id }), jsx(SpinButton$1, { ...props, input: { className: classes.inputSlot }, step: step, id: id, size: "medium", precision: precision, displayValue: `${value.toFixed(precision)}${props.unit ? " " + props.unit : ""}`, value: value, onChange: handleChange, onKeyUp: handleKeyUp, onKeyDown: HandleKeyDown, onBlur: HandleOnBlur, className: mergedClassName })] }));
4071
4153
  };
4072
- /**
4073
- * Fluent's CalculatePrecision function
4074
- * https://github.com/microsoft/fluentui/blob/dcbf775d37938eacffa37922fc0b43a3cdd5753f/packages/utilities/src/math.ts#L91C1
4075
- *
4076
- * Calculates a number's precision based on the number of trailing
4077
- * zeros if the number does not have a decimal indicated by a negative
4078
- * precision. Otherwise, it calculates the number of digits after
4079
- * the decimal point indicated by a positive precision.
4080
- *
4081
- * @param value - the value to determine the precision of
4082
- * @returns the calculated precision
4083
- */
4084
- function CalculatePrecision(value) {
4085
- /**
4086
- * Group 1:
4087
- * [1-9]([0]+$) matches trailing zeros
4088
- * Group 2:
4089
- * \.([0-9]*) matches all digits after a decimal point.
4090
- */ const groups = /[1-9]([0]+$)|\.([0-9]*)/.exec(String(value));
4091
- if (!groups) {
4092
- return 0;
4093
- }
4094
- if (groups[1]) {
4095
- return -groups[1].length;
4096
- }
4097
- if (groups[2]) {
4098
- return groups[2].length;
4099
- }
4100
- return 0;
4101
- }
4102
- /**
4103
- * Fluent's PrecisionRound function
4104
- * https://github.com/microsoft/fluentui/blob/dcbf775d37938eacffa37922fc0b43a3cdd5753f/packages/utilities/src/math.ts#L116
4105
- *
4106
- * Rounds a number to a certain level of precision. Accepts negative precision.
4107
- * @param value - The value that is being rounded.
4108
- * @param precision - The number of decimal places to round the number to
4109
- * @returns The rounded number.
4110
- */
4111
- function PrecisionRound(value, precision) {
4112
- const exp = Math.pow(10, precision);
4113
- return Math.round(value * exp) / exp;
4114
- }
4115
4154
 
4116
4155
  /**
4117
4156
  * Wraps a text input in a property line
@@ -4193,7 +4232,7 @@ const SyncedSliderInput = (props) => {
4193
4232
  setValue(value);
4194
4233
  props.onChange(value); // Input always updates immediately
4195
4234
  };
4196
- return (jsxs("div", { className: classes.container, children: [infoLabel && jsx(InfoLabel, { ...infoLabel, htmlFor: "syncedSlider" }), jsxs("div", { id: "syncedSlider", className: classes.syncedSlider, children: [props.min !== undefined && props.max !== undefined && (jsx(Slider, { ...passthroughProps, className: classes.slider, min: min / step, max: max / step, step: undefined, value: value / step, onChange: handleSliderChange, onPointerDown: handleSliderPointerDown, onPointerUp: handleSliderPointerUp })), jsx(SpinButton, { ...passthroughProps, value: Math.round(value / step) * step, onChange: handleInputChange, step: props.step })] })] }));
4235
+ return (jsxs("div", { className: classes.container, children: [infoLabel && jsx(InfoLabel, { ...infoLabel, htmlFor: "syncedSlider" }), jsxs("div", { id: "syncedSlider", className: classes.syncedSlider, children: [props.min !== undefined && props.max !== undefined && (jsx(Slider, { ...passthroughProps, className: classes.slider, min: min / step, max: max / step, step: undefined, value: value / step, onChange: handleSliderChange, onPointerDown: handleSliderPointerDown, onPointerUp: handleSliderPointerUp })), jsx(SpinButton, { ...passthroughProps, value: value, onChange: handleInputChange, step: props.step })] })] }));
4197
4236
  };
4198
4237
 
4199
4238
  /**
@@ -4402,6 +4441,9 @@ const TensorPropertyLine = (props) => {
4402
4441
  setVector(newVector);
4403
4442
  props.onChange(newVector);
4404
4443
  };
4444
+ useEffect(() => {
4445
+ setVector(props.value);
4446
+ }, [props.value, props.expandedContent]);
4405
4447
  return (jsx(PropertyLine, { ...props, onCopy: () => `new ${props.value.constructor.name}(${vector.x},${vector.y} ${HasZ(vector) ? `,${vector.z}` : ""}${HasW(vector) ? `,${vector.w}` : ""})`, expandedContent: jsxs(Fragment, { children: [jsx(SyncedSliderPropertyLine, { label: "X", value: converted(vector.x), min: min, max: max, onChange: (val) => onChange(val, "x"), unit: props.unit, step: props.step }), jsx(SyncedSliderPropertyLine, { label: "Y", value: converted(vector.y), min: min, max: max, onChange: (val) => onChange(val, "y"), unit: props.unit, step: props.step }), HasZ(vector) && (jsx(SyncedSliderPropertyLine, { label: "Z", value: converted(vector.z), min: min, max: max, onChange: (val) => onChange(val, "z"), unit: props.unit, step: props.step })), HasW(vector) && (jsx(SyncedSliderPropertyLine, { label: "W", value: converted(vector.w), min: min, max: max, onChange: (val) => onChange(val, "w"), unit: props.unit, step: props.step }))] }), children: jsx(Body1, { children: `[${formatted(props.value.x)}, ${formatted(props.value.y)}${HasZ(props.value) ? `, ${formatted(props.value.z)}` : ""}${HasW(props.value) ? `, ${formatted(props.value.w)}` : ""}]` }) }));
4406
4448
  };
4407
4449
  const ToDegreesConverter = { from: Tools.ToDegrees, to: Tools.ToRadians };
@@ -4435,14 +4477,17 @@ const Vector3PropertyLine = TensorPropertyLine;
4435
4477
  const Vector4PropertyLine = TensorPropertyLine;
4436
4478
 
4437
4479
  const useDropdownStyles = makeStyles({
4438
- dropdown: { ...UniformWidthStyling, minWidth: CustomTokens.inputWidth },
4480
+ dropdown: {
4481
+ minWidth: 0,
4482
+ width: "100%",
4483
+ },
4439
4484
  container: {
4440
4485
  display: "flex",
4441
4486
  flexDirection: "column",
4442
4487
  justifyContent: "center", // align items vertically
4443
4488
  gap: "4px",
4444
4489
  },
4445
- button: { textAlign: "end" },
4490
+ dropdownText: { textAlign: "end", textOverflow: "ellipsis", whiteSpace: "nowrap", overflowX: "hidden" },
4446
4491
  });
4447
4492
  /**
4448
4493
  * Renders a fluent UI dropdown component for the options passed in, and an additional 'Not Defined' option if null is set to true
@@ -4459,34 +4504,34 @@ const Dropdown = (props) => {
4459
4504
  setDefaultVal(value);
4460
4505
  }, [props.value]);
4461
4506
  const id = useId("dropdown");
4462
- const mergedClassName = mergeClasses(classes.dropdown, props.className);
4463
- return (jsxs("div", { className: classes.container, children: [props.infoLabel && jsx(InfoLabel, { ...props.infoLabel, htmlFor: id }), jsx(Dropdown$1, { id: id, disabled: props.disabled, size: "medium", className: mergedClassName, button: { className: classes.button }, onOptionSelect: (evt, data) => {
4507
+ const mergedClassName = mergeClasses(classes.container, props.className);
4508
+ const optionLabel = options.find((o) => o.value === defaultVal)?.label;
4509
+ return (jsxs("div", { className: mergedClassName, children: [props.infoLabel && jsx(InfoLabel, { ...props.infoLabel, htmlFor: id }), jsx(Dropdown$1, { id: id, disabled: props.disabled, size: "medium", className: classes.dropdown, button: jsx("span", { className: classes.dropdownText, children: optionLabel }), onOptionSelect: (evt, data) => {
4464
4510
  const value = typeof props.value === "number" ? Number(data.optionValue) : data.optionValue;
4465
4511
  if (value !== undefined) {
4466
4512
  setDefaultVal(value);
4467
4513
  props.onChange(value);
4468
4514
  }
4469
- }, selectedOptions: [defaultVal.toString()], value: options.find((o) => o.value === defaultVal)?.label, children: options.map((option) => (jsx(Option, { className: classes.dropdown, value: option.value.toString(), disabled: false, children: option.label }, option.label))) })] }));
4515
+ }, selectedOptions: [defaultVal.toString()], value: optionLabel, children: options.map((option) => (jsx(Option, { value: option.value.toString(), disabled: false, children: option.label }, option.label))) })] }));
4470
4516
  };
4471
4517
  const NumberDropdown = Dropdown;
4472
4518
  const StringDropdown = Dropdown;
4473
4519
 
4474
4520
  const useColorPickerStyles = makeStyles({
4475
4521
  container: {
4476
- width: "380px",
4522
+ width: "350px",
4477
4523
  display: "flex", // becomes a flexbox
4478
4524
  flexDirection: "column", // with children in a column
4479
4525
  alignItems: "center", // centers children horizontally
4480
4526
  justifyContent: "center", // centers children vertically (if height is set)
4481
- borderRadius: "4px",
4482
- gap: "15px",
4527
+ gap: tokens.spacingVerticalM,
4483
4528
  overflow: "visible",
4484
4529
  },
4485
4530
  row: {
4486
4531
  flex: 1, // is a row in the container's flex column
4487
4532
  display: "flex", // becomes its own flexbox
4488
4533
  flexDirection: "row", // with children in a row
4489
- gap: "20px",
4534
+ gap: tokens.spacingHorizontalXL,
4490
4535
  alignItems: "center", // align items vertically
4491
4536
  width: "100%",
4492
4537
  },
@@ -4544,7 +4589,7 @@ const ColorPickerPopup = (props) => {
4544
4589
  align: "start",
4545
4590
  overflowBoundary: document.body,
4546
4591
  autoSize: true,
4547
- }, open: popoverOpen, trapFocus: true, onOpenChange: (_, data) => setPopoverOpen(data.open), children: [jsx(PopoverTrigger, { disableButtonEnhancement: true, children: jsx(ColorSwatch, { borderColor: tokens.colorNeutralShadowKeyDarker, size: "small", color: color.toHexString(), value: color.toHexString().slice(1) }) }), jsx(PopoverSurface, { children: jsxs("div", { className: classes.container, children: [jsxs(ColorPicker, { className: classes.colorPicker, color: rgbaToHsv(color), onColorChange: handleColorPickerChange, children: [jsx(ColorArea, { inputX: { "aria-label": "Saturation" }, inputY: { "aria-label": "Brightness" } }), jsx(ColorSlider, { "aria-label": "Hue" }), color instanceof Color4 && jsx(AlphaSlider, { "aria-label": "Alpha" })] }), jsxs("div", { className: classes.row, children: [jsx("div", { className: classes.previewColor, style: { backgroundColor: color.toHexString() } }), jsx(NumberDropdown, { className: classes.inputField, infoLabel: {
4592
+ }, open: popoverOpen, trapFocus: true, onOpenChange: (_, data) => setPopoverOpen(data.open), children: [jsx(PopoverTrigger, { disableButtonEnhancement: true, children: jsx(ColorSwatch, { borderColor: tokens.colorNeutralShadowKeyDarker, size: "small", shape: "rounded", color: color.toHexString(), value: color.toHexString().slice(1) }) }), jsx(PopoverSurface, { children: jsxs("div", { className: classes.container, children: [jsxs(ColorPicker, { className: classes.colorPicker, color: rgbaToHsv(color), onColorChange: handleColorPickerChange, children: [jsx(ColorArea, { inputX: { "aria-label": "Saturation" }, inputY: { "aria-label": "Brightness" } }), jsx(ColorSlider, { "aria-label": "Hue" }), color instanceof Color4 && jsx(AlphaSlider, { "aria-label": "Alpha" })] }), jsxs("div", { className: classes.row, children: [jsx("div", { className: classes.previewColor, style: { backgroundColor: color.toHexString() } }), jsx(NumberDropdown, { className: classes.inputField, infoLabel: {
4548
4593
  label: "Color Space",
4549
4594
  info: jsx(Body1, { children: "Today this is not mutable as the color space is determined by the entity. Soon we will allow swapping" }),
4550
4595
  }, options: [
@@ -4558,7 +4603,6 @@ const ColorPickerPopup = (props) => {
4558
4603
  { label: "Float", value: 1 },
4559
4604
  ], disabled: true, value: isFloat ? 1 : 0, onChange: (val) => setFloat(val === 1) })] }), jsxs("div", { className: classes.inputRow, children: [jsx(InputRgbField, { title: "Red", value: color, rgbKey: "r", onChange: handleChange }), jsx(InputRgbField, { title: "Green", value: color, rgbKey: "g", onChange: handleChange }), jsx(InputRgbField, { title: "Blue", value: color, rgbKey: "b", onChange: handleChange }), jsx(InputAlphaField, { color: color, onChange: handleChange })] }), jsxs("div", { className: classes.inputRow, children: [jsx(InputHsvField, { title: "Hue", value: color, hsvKey: "h", max: 360, onChange: handleChange }), jsx(InputHsvField, { title: "Saturation", value: color, hsvKey: "s", max: 100, scale: 100, onChange: handleChange }), jsx(InputHsvField, { title: "Value", value: color, hsvKey: "v", max: 100, scale: 100, onChange: handleChange })] }), jsx("div", { className: classes.inputRow, children: jsx(InputHexField, { title: "Hexadecimal", linearHex: isLinear, isLinearMode: isLinear, value: color, onChange: handleChange }) })] }) })] }));
4560
4605
  };
4561
- const HEX_REGEX = RegExp(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}|[A-Fa-f0-9]{8})$/);
4562
4606
  /**
4563
4607
  * Component which displays the passed in color's HEX value, either in linearSpace (if linearHex is true) or in gamma space
4564
4608
  * When the hex color is changed by user, component calculates the new Color3/4 value and calls onChange
@@ -4570,7 +4614,7 @@ const HEX_REGEX = RegExp(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}|[A-Fa-f0-9]{8})$/);
4570
4614
  const InputHexField = (props) => {
4571
4615
  const classes = useColorPickerStyles();
4572
4616
  const { title, value, onChange, linearHex, isLinearMode } = props;
4573
- return (jsx(TextInput, { disabled: linearHex ? !isLinearMode : false, className: classes.inputField, value: linearHex ? value.toLinearSpace().toHexString() : value.toHexString(), validator: (val) => val != "" && HEX_REGEX.test(val), onChange: (val) => (linearHex ? onChange(Color3.FromHexString(val).toGammaSpace()) : onChange(Color3.FromHexString(val))), infoLabel: title
4617
+ return (jsx(TextInput, { disabled: linearHex ? !isLinearMode : false, className: classes.inputField, value: linearHex ? value.toLinearSpace().toHexString() : value.toHexString(), validator: ColorHexValidatorFn, onChange: (val) => (linearHex ? onChange(Color3.FromHexString(val).toGammaSpace()) : onChange(Color3.FromHexString(val))), infoLabel: title
4574
4618
  ? {
4575
4619
  label: title,
4576
4620
  // If not representing a linearHex, no info is needed.
@@ -4653,6 +4697,9 @@ const InputAlphaField = (props) => {
4653
4697
  const ColorPropertyLine = forwardRef((props, ref) => {
4654
4698
  ColorPropertyLine.displayName = "ColorPropertyLine";
4655
4699
  const [color, setColor] = useState(props.value);
4700
+ useEffect(() => {
4701
+ setColor(props.value);
4702
+ }, [props.value]);
4656
4703
  const onSliderChange = (value, key) => {
4657
4704
  let newColor;
4658
4705
  if (key === "a") {
@@ -4775,6 +4822,11 @@ const ArcRotateCameraBehaviorsProperties = (props) => {
4775
4822
  return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SwitchPropertyLine, label: "Auto Rotation", target: camera, propertyKey: "useAutoRotationBehavior" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Bouncing", target: camera, propertyKey: "useBouncingBehavior" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Framing", target: camera, propertyKey: "useFramingBehavior" })] }));
4776
4823
  };
4777
4824
 
4825
+ const useStyles$5 = makeStyles({
4826
+ dropdown: {
4827
+ ...UniformWidthStyling,
4828
+ },
4829
+ });
4778
4830
  /**
4779
4831
  * Wraps a dropdown in a property line
4780
4832
  * @param props - PropertyLineProps and DropdownProps
@@ -4782,7 +4834,8 @@ const ArcRotateCameraBehaviorsProperties = (props) => {
4782
4834
  */
4783
4835
  const DropdownPropertyLine = forwardRef((props, ref) => {
4784
4836
  DropdownPropertyLine.displayName = "DropdownPropertyLine";
4785
- return (jsx(PropertyLine, { ...props, ref: ref, children: jsx(Dropdown, { ...props }) }));
4837
+ const classes = useStyles$5();
4838
+ return (jsx(PropertyLine, { ...props, ref: ref, children: jsx(Dropdown, { ...props, className: classes.dropdown }) }));
4786
4839
  });
4787
4840
  /**
4788
4841
  * Dropdown component for number values.
@@ -4793,6 +4846,60 @@ const NumberDropdownPropertyLine = DropdownPropertyLine;
4793
4846
  */
4794
4847
  const StringDropdownPropertyLine = DropdownPropertyLine;
4795
4848
 
4849
+ // The below conversion functions are taken from old HexLineComponent and can likely be simplified
4850
+ const ConvertToHexString = (valueString) => {
4851
+ while (valueString.length < 10) {
4852
+ valueString += "0";
4853
+ }
4854
+ return valueString;
4855
+ };
4856
+ const MaskValidatorFn = (valueString) => {
4857
+ if (valueString.substring(0, 2) != "0x") {
4858
+ if (valueString.substring(0, 1) != "0") {
4859
+ valueString = "0x" + valueString;
4860
+ }
4861
+ else {
4862
+ valueString = "0x" + valueString.substr(1);
4863
+ }
4864
+ }
4865
+ const valueSubstr = valueString.substr(2);
4866
+ if (valueSubstr != "" && /^[0-9A-Fa-f]+$/g.test(valueSubstr) == false) {
4867
+ return false;
4868
+ }
4869
+ if (valueString.length > 10) {
4870
+ return false;
4871
+ }
4872
+ return true;
4873
+ };
4874
+ const GetHexValFromNumber = (val) => {
4875
+ let valueAsHex = val.toString(16);
4876
+ let hex0String = "";
4877
+ for (let i = 0; i < 8 - valueAsHex.length; i++) {
4878
+ // padding the '0's
4879
+ hex0String += "0";
4880
+ }
4881
+ valueAsHex = "0x" + hex0String + valueAsHex.toUpperCase();
4882
+ return valueAsHex;
4883
+ };
4884
+ /**
4885
+ * Takes a number representing a Hex value and converts it to a hex string then wraps the TextInput in a PropertyLine
4886
+ * @param props - PropertyLineProps
4887
+ * @returns property-line wrapped textbox that converts to/from hex number representation
4888
+ */
4889
+ const HexPropertyLine = (props) => {
4890
+ HexPropertyLine.displayName = "HexPropertyLine";
4891
+ const [hexVal, setHexVal] = useState(GetHexValFromNumber(props.value));
4892
+ const onStrValChange = (val) => {
4893
+ setHexVal(val);
4894
+ const valueStringAsHex = ConvertToHexString(val);
4895
+ props.onChange(parseInt(valueStringAsHex));
4896
+ };
4897
+ useEffect(() => {
4898
+ setHexVal(GetHexValFromNumber(props.value));
4899
+ }, [props.value]);
4900
+ return (jsx(PropertyLine, { ...props, children: jsx(TextInput, { ...props, validator: MaskValidatorFn, value: hexVal, onChange: onStrValChange }) }));
4901
+ };
4902
+
4796
4903
  const CameraModes = [
4797
4904
  { label: "Perspective", value: Camera.PERSPECTIVE_CAMERA },
4798
4905
  { label: "Orthographic", value: Camera.ORTHOGRAPHIC_CAMERA },
@@ -4806,7 +4913,7 @@ const CameraGeneralProperties = (props) => {
4806
4913
  const { camera, settings } = props;
4807
4914
  const [toDisplayAngle, fromDisplayAngle, useDegrees] = useAngleConverters(settings);
4808
4915
  const mode = useProperty(camera, "mode");
4809
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Near Plane", description: "Anything closer than this will not be drawn.", target: camera, propertyKey: "minZ" }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Far Plane", description: "Anything further than this will not be drawn.", target: camera, propertyKey: "maxZ" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Inertia", target: camera, propertyKey: "inertia", min: 0, max: 1, step: 0.01 }), jsx(PlaceholderPropertyLine, { label: "Layer Mask", value: camera.layerMask, onChange: () => { } }), jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Mode", options: CameraModes, target: camera, propertyKey: "mode" }), jsx(Collapse, { visible: mode === Camera.PERSPECTIVE_CAMERA, children: jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "FOV", description: `Field of view in ${useDegrees ? "degrees" : "radians"}`, target: camera, propertyKey: "fov", min: toDisplayAngle(0.1), max: toDisplayAngle(Math.PI), step: toDisplayAngle(0.01), convertTo: toDisplayAngle, convertFrom: fromDisplayAngle }) }), jsx(Collapse, { visible: mode === Camera.ORTHOGRAPHIC_CAMERA, children: jsxs("div", { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Left", target: camera, step: 0.1, propertyKey: "orthoLeft", nullable: true, defaultValue: 0 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Right", target: camera, step: 0.1, propertyKey: "orthoRight", nullable: true, defaultValue: 0 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Top", target: camera, step: 0.1, propertyKey: "orthoTop", nullable: true, defaultValue: 0 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Bottom", target: camera, step: 0.1, propertyKey: "orthoBottom", nullable: true, defaultValue: 0 })] }) })] }));
4916
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Near Plane", description: "Anything closer than this will not be drawn.", target: camera, propertyKey: "minZ" }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Far Plane", description: "Anything further than this will not be drawn.", target: camera, propertyKey: "maxZ" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Inertia", target: camera, propertyKey: "inertia", min: 0, max: 1, step: 0.01 }), jsx(BoundProperty, { component: HexPropertyLine, label: "Layer Mask", target: camera, propertyKey: "layerMask" }), jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Mode", options: CameraModes, target: camera, propertyKey: "mode" }), jsx(Collapse, { visible: mode === Camera.PERSPECTIVE_CAMERA, children: jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "FOV", description: `Field of view in ${useDegrees ? "degrees" : "radians"}`, target: camera, propertyKey: "fov", min: toDisplayAngle(0.1), max: toDisplayAngle(Math.PI), step: toDisplayAngle(0.01), convertTo: toDisplayAngle, convertFrom: fromDisplayAngle }) }), jsx(Collapse, { visible: mode === Camera.ORTHOGRAPHIC_CAMERA, children: jsxs("div", { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Left", target: camera, step: 0.1, propertyKey: "orthoLeft", nullable: true, defaultValue: 0 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Right", target: camera, step: 0.1, propertyKey: "orthoRight", nullable: true, defaultValue: 0 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Top", target: camera, step: 0.1, propertyKey: "orthoTop", nullable: true, defaultValue: 0 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Bottom", target: camera, step: 0.1, propertyKey: "orthoBottom", nullable: true, defaultValue: 0 })] }) })] }));
4810
4917
  };
4811
4918
 
4812
4919
  const FollowCameraTransformProperties = (props) => {
@@ -5558,11 +5665,8 @@ const MaterialPropertiesServiceDefinition = {
5558
5665
 
5559
5666
  const useInputStyles = makeStyles({
5560
5667
  textarea: {
5561
- // eslint-disable-next-line @typescript-eslint/naming-convention
5562
- "& textarea": {
5563
- minHeight: "100px",
5564
- maxHeight: "500px",
5565
- },
5668
+ minHeight: "100px",
5669
+ maxHeight: "500px",
5566
5670
  },
5567
5671
  });
5568
5672
  /**
@@ -5582,7 +5686,7 @@ const Textarea = (props) => {
5582
5686
  const handleKeyDown = (event) => {
5583
5687
  event.stopPropagation(); // Prevent event propagation
5584
5688
  };
5585
- return jsx(Textarea$1, { ...props, className: classes.textarea, onChange: handleChange, onKeyDown: handleKeyDown });
5689
+ return jsx(Textarea$1, { ...props, textarea: { className: classes.textarea }, onChange: handleChange, onKeyDown: handleKeyDown });
5586
5690
  };
5587
5691
 
5588
5692
  const PrettyJSONIndent = 2;
@@ -5723,14 +5827,14 @@ const MetadataProperties = (props) => {
5723
5827
  const [editedMetadata, setEditedMetadata] = useState(stringifiedMetadata);
5724
5828
  const isEditedMetadataJSON = useMemo(() => IsParsable(editedMetadata), [editedMetadata]);
5725
5829
  const unformattedEditedMetadata = useMemo(() => Restringify(editedMetadata, false), [editedMetadata]);
5726
- return (jsxs(Fragment, { children: [jsx(TextPropertyLine, { label: "Property Type", value: metadataType }), jsx(Collapse, { visible: canPreventObjectCorruption, children: jsx(SwitchPropertyLine, { label: "Prevent Object Corruption", value: isReadonly, onChange: setPreventObjectCorruption }) }), jsxs("div", { className: classes.mainDiv, children: [jsx(Textarea, { disabled: isReadonly, value: editedMetadata, onChange: setEditedMetadata }), jsx(ButtonLine, { label: "Populate glTF extras", disabled: !!editedMetadata && (!IsParsable(editedMetadata) || HasGltfExtras(editedMetadata)), onClick: () => {
5727
- const isFormatted = Restringify(editedMetadata, true) === editedMetadata;
5728
- let withGLTFExtras = PopulateGLTFExtras(editedMetadata);
5729
- if (isFormatted) {
5730
- withGLTFExtras = Restringify(withGLTFExtras, true);
5731
- }
5732
- setEditedMetadata(withGLTFExtras);
5733
- } }), jsx(LineContainer, { children: jsxs("div", { className: classes.buttonDiv, children: [jsx(Button$1, { icon: jsx(SaveRegular, {}), disabled: stringifiedMetadata === unformattedEditedMetadata, onClick: () => SaveMetadata(entity, editedMetadata), children: jsx(Body1, { children: "Save" }) }), jsx(Tooltip, { content: "Undo Changes", relationship: "label", children: jsx(Button$1, { icon: jsx(ArrowUndoRegular, {}), disabled: stringifiedMetadata === unformattedEditedMetadata, onClick: () => setEditedMetadata(stringifiedMetadata) }) }), jsx(Tooltip, { content: "Format (Pretty Print)", relationship: "label", children: jsx(Button$1, { icon: jsx(BracesRegular, {}), disabled: !isEditedMetadataJSON, onClick: () => setEditedMetadata(Restringify(editedMetadata, true)) }) }), jsx(Tooltip, { content: "Clear Formatting (Undo Pretty Print)", relationship: "label", children: jsx(Button$1, { icon: jsx(BracesDismiss16Regular, {}), disabled: !isEditedMetadataJSON, onClick: () => setEditedMetadata(Restringify(editedMetadata, false)) }) })] }) })] })] }));
5830
+ return (jsxs(Fragment, { children: [jsx(TextPropertyLine, { label: "Property Type", value: metadataType }), jsx(Collapse, { visible: canPreventObjectCorruption, children: jsx(SwitchPropertyLine, { label: "Prevent Object Corruption", value: isReadonly, onChange: setPreventObjectCorruption }) }), jsx(LineContainer, { children: jsx(Textarea, { disabled: isReadonly, value: editedMetadata, onChange: setEditedMetadata }) }), jsx(ButtonLine, { label: "Populate glTF extras", disabled: !!editedMetadata && (!IsParsable(editedMetadata) || HasGltfExtras(editedMetadata)), onClick: () => {
5831
+ const isFormatted = Restringify(editedMetadata, true) === editedMetadata;
5832
+ let withGLTFExtras = PopulateGLTFExtras(editedMetadata);
5833
+ if (isFormatted) {
5834
+ withGLTFExtras = Restringify(withGLTFExtras, true);
5835
+ }
5836
+ setEditedMetadata(withGLTFExtras);
5837
+ } }), jsx(LineContainer, { children: jsxs("div", { className: classes.buttonDiv, children: [jsx(Button$1, { icon: jsx(SaveRegular, {}), disabled: stringifiedMetadata === unformattedEditedMetadata, onClick: () => SaveMetadata(entity, editedMetadata), children: jsx(Body1, { children: "Save" }) }), jsx(Tooltip, { content: "Undo Changes", relationship: "label", children: jsx(Button$1, { icon: jsx(ArrowUndoRegular, {}), disabled: stringifiedMetadata === unformattedEditedMetadata, onClick: () => setEditedMetadata(stringifiedMetadata) }) }), jsx(Tooltip, { content: "Format (Pretty Print)", relationship: "label", children: jsx(Button$1, { icon: jsx(BracesRegular, {}), disabled: !isEditedMetadataJSON, onClick: () => setEditedMetadata(Restringify(editedMetadata, true)) }) }), jsx(Tooltip, { content: "Clear Formatting (Undo Pretty Print)", relationship: "label", children: jsx(Button$1, { icon: jsx(BracesDismiss16Regular, {}), disabled: !isEditedMetadataJSON, onClick: () => setEditedMetadata(Restringify(editedMetadata, false)) }) })] }) })] }));
5734
5838
  };
5735
5839
 
5736
5840
  function IsMetadataContainer(entity) {
@@ -5771,7 +5875,7 @@ const AbstractMeshGeneralProperties = (props) => {
5771
5875
  };
5772
5876
  const AbstractMeshDisplayProperties = (props) => {
5773
5877
  const { mesh } = props;
5774
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Alpha Index", target: mesh, propertyKey: "alphaIndex" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Receive Shadows", target: mesh, propertyKey: "receiveShadows" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Rendering Group Id", target: mesh, propertyKey: "renderingGroupId", min: RenderingManager.MIN_RENDERINGGROUPS, max: RenderingManager.MAX_RENDERINGGROUPS - 1, step: 1 }), jsx(BoundProperty, { component: PlaceholderPropertyLine, label: "TODO: Layer Mask", target: mesh, propertyKey: "layerMask" })] }));
5878
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Alpha Index", target: mesh, propertyKey: "alphaIndex" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Receive Shadows", target: mesh, propertyKey: "receiveShadows" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Rendering Group Id", target: mesh, propertyKey: "renderingGroupId", min: RenderingManager.MIN_RENDERINGGROUPS, max: RenderingManager.MAX_RENDERINGGROUPS - 1, step: 1 }), jsx(BoundProperty, { component: HexPropertyLine, label: "Layer Mask", target: mesh, propertyKey: "layerMask" })] }));
5775
5879
  };
5776
5880
  const AbstractMeshAdvancedProperties = (props) => {
5777
5881
  const { mesh } = props;
@@ -5781,7 +5885,7 @@ const AbstractMeshOutlineOverlayProperties = (props) => {
5781
5885
  const { mesh } = props;
5782
5886
  const renderOverlay = useProperty(mesh, "renderOverlay");
5783
5887
  const renderOutline = useProperty(mesh, "renderOutline");
5784
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SwitchPropertyLine, label: "Render Overlay", target: mesh, propertyKey: "renderOverlay" }), jsx(Collapse, { visible: renderOverlay, children: jsx(BoundProperty, { label: "Overlay Color", component: Color3PropertyLine, target: mesh, propertyKey: "overlayColor" }) }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Render Outline", target: mesh, propertyKey: "renderOutline" }), jsx(Collapse, { visible: renderOutline, children: jsx(BoundProperty, { label: "Outline Color", component: Color3PropertyLine, target: mesh, propertyKey: "outlineColor" }) })] }));
5888
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SwitchPropertyLine, label: "Render Overlay", target: mesh, propertyKey: "renderOverlay" }), jsx(Collapse, { visible: renderOverlay, children: jsx(BoundProperty, { label: "Overlay Color", component: Color3PropertyLine, target: mesh, propertyKey: "overlayColor" }) }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Render Outline", target: mesh, propertyKey: "renderOutline" }), jsxs(Collapse, { visible: renderOutline, children: [jsx(BoundProperty, { label: "Outline Color", component: Color3PropertyLine, target: mesh, propertyKey: "outlineColor" }), jsx(BoundProperty, { label: "Outline Width", component: NumberInputPropertyLine, target: mesh, propertyKey: "outlineWidth", step: 0.001 })] })] }));
5785
5889
  };
5786
5890
  const OcclusionTypes = [
5787
5891
  { label: "None", value: 0 },
@@ -6919,10 +7023,52 @@ const SkeletonPropertiesServiceDefinition = {
6919
7023
  },
6920
7024
  };
6921
7025
 
7026
+ const SpinButtonPropertyLine = (props) => {
7027
+ SpinButtonPropertyLine.displayName = "SpinButtonPropertyLine";
7028
+ return (jsx(PropertyLine, { ...props, children: jsx(SpinButton, { ...props }) }));
7029
+ };
7030
+
7031
+ const SpriteManagerGeneralProperties = (props) => {
7032
+ const { spriteManager, selectionService } = props;
7033
+ const texture = useProperty(spriteManager, "texture");
7034
+ return (jsxs(Fragment, { children: [jsx(TextPropertyLine, { label: "Capacity", value: spriteManager.capacity.toString() }), jsx(LinkToEntityPropertyLine, { label: "Texture", entity: texture, selectionService: selectionService }), jsx(ButtonLine, { label: "Dispose", onClick: () => spriteManager.dispose() })] }));
7035
+ };
7036
+ const SpriteManagerOtherProperties = (props) => {
7037
+ const { spriteManager } = props;
7038
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SwitchPropertyLine, label: "Pickable", target: spriteManager, propertyKey: "isPickable" }, "IsPickable"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Fog Enabled", target: spriteManager, propertyKey: "fogEnabled" }, "FogEnabled"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Depth Write", target: spriteManager, propertyKey: "disableDepthWrite", convertTo: (value) => !value, convertFrom: (value) => !value }, "DepthWrite"), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Rendering Group ID", target: spriteManager, propertyKey: "renderingGroupId", min: RenderingManager.MIN_RENDERINGGROUPS, max: RenderingManager.MAX_RENDERINGGROUPS, step: 1 }, "RenderingGroupId"), jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Blend Mode", target: spriteManager, propertyKey: "blendMode", options: AlphaModeOptions }, "BlendMode")] }));
7039
+ };
7040
+ const SpriteManagerCellProperties = (props) => {
7041
+ const { spriteManager } = props;
7042
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SpinButtonPropertyLine, label: "Cell Width", target: spriteManager, propertyKey: "cellWidth", min: 1, step: 1 }, "CellWidth"), jsx(BoundProperty, { component: SpinButtonPropertyLine, label: "Cell Height", target: spriteManager, propertyKey: "cellHeight", min: 1, step: 1 }, "CellHeight")] }));
7043
+ };
7044
+
7045
+ function useMaxCellCount(sprite) {
7046
+ const manager = sprite.manager;
7047
+ const texture = useProperty(manager, "texture");
7048
+ const textureSize = texture.getSize();
7049
+ let maxCellCount = 0;
7050
+ if (!textureSize.width || !textureSize.height) {
7051
+ maxCellCount = Math.max(sprite.fromIndex, sprite.toIndex);
7052
+ }
7053
+ else {
7054
+ maxCellCount = (textureSize.width / manager.cellWidth) * (textureSize.height / manager.cellHeight);
7055
+ }
7056
+ return maxCellCount;
7057
+ }
7058
+ const SpriteGeneralProperties = (props) => {
7059
+ const { sprite, selectionService } = props;
7060
+ return (jsxs(Fragment, { children: [jsx(LinkToEntityPropertyLine, { label: "Parent", description: `Sprite Manager that owns this sprite.`, entity: sprite.manager, selectionService: selectionService }, "Parent"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Is Visible", description: "Whether the sprite is visible or not.", target: sprite, propertyKey: "isVisible" }, "IsVisible")] }));
7061
+ };
7062
+ const SpriteTransformProperties = (props) => {
7063
+ const { sprite, settings } = props;
7064
+ const [toDisplayAngle, fromDisplayAngle, useDegrees] = useAngleConverters(settings);
7065
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: Vector3PropertyLine, label: "Position", target: sprite, propertyKey: "position" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Angle", description: `Rotation angle of the sprite in ${useDegrees ? "degrees" : "radians"}`, min: 0, max: toDisplayAngle(Math.PI * 2), step: toDisplayAngle(0.01), target: sprite, propertyKey: "angle", convertTo: toDisplayAngle, convertFrom: fromDisplayAngle }, "Angle"), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Width", description: "Width of the sprite (in world space units)", target: sprite, propertyKey: "width" }, "Width"), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Height", description: "Height of the sprite (in world space units)", target: sprite, propertyKey: "height" }, "Height")] }));
7066
+ };
6922
7067
  const SpriteAnimationProperties = (props) => {
6923
7068
  const { sprite } = props;
6924
7069
  const animationStarted = useObservableState(useCallback(() => sprite.animationStarted, [sprite]), useInterceptObservable("function", sprite, "playAnimation"), useInterceptObservable("function", sprite, "stopAnimation"), useInterceptObservable("function", sprite, "_animate"));
6925
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Start", description: "First frame of the animation.", min: 0, target: sprite, propertyKey: "fromIndex" }, "Start"), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "End", description: "Last frame of the animation.", min: 0, target: sprite, propertyKey: "toIndex" }, "End"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Loop", description: "Whether to loop the animation.", target: sprite, propertyKey: "loopAnimation" }, "Loop"), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Delay", description: "Delay between frames in milliseconds.", min: 0, target: sprite, propertyKey: "delay" }, "Delay"), jsx(ButtonLine, { label: animationStarted ? "Stop Animation" : "Start Animation", icon: animationStarted ? StopFilled : PlayFilled, onClick: () => {
7070
+ const maxCellCount = useMaxCellCount(sprite);
7071
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Start", description: "First frame of the animation.", min: 0, max: maxCellCount, target: sprite, propertyKey: "fromIndex" }, "Start"), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "End", description: "Last frame of the animation.", min: 0, max: maxCellCount, target: sprite, propertyKey: "toIndex" }, "End"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Loop", description: "Whether to loop the animation.", target: sprite, propertyKey: "loopAnimation" }, "Loop"), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Delay", description: "Delay between frames in milliseconds.", min: 0, target: sprite, propertyKey: "delay" }, "Delay"), jsx(ButtonLine, { label: animationStarted ? "Stop Animation" : "Start Animation", icon: animationStarted ? StopFilled : PlayFilled, onClick: () => {
6926
7072
  if (animationStarted) {
6927
7073
  sprite.stopAnimation();
6928
7074
  }
@@ -6931,62 +7077,59 @@ const SpriteAnimationProperties = (props) => {
6931
7077
  }
6932
7078
  } })] }));
6933
7079
  };
6934
-
6935
- const SpriteGeneralProperties = (props) => {
6936
- const { sprite, selectionService } = props;
6937
- return (jsxs(Fragment, { children: [jsx(LinkToEntityPropertyLine, { label: "Parent", description: `Sprite Manager that owns this sprite.`, entity: sprite.manager, selectionService: selectionService }, "Parent"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Is Visible", description: "Whether the sprite is visible or not.", target: sprite, propertyKey: "isVisible" }, "IsVisible")] }));
6938
- };
6939
-
6940
7080
  const SpriteOtherProperties = (props) => {
6941
7081
  const { sprite } = props;
6942
7082
  const color = useColor4Property(sprite, "color");
6943
- return (jsx(Fragment, { children: jsx(Color4PropertyLine, { label: "Color", description: "Color to tint the sprite.", value: color, onChange: (col) => (sprite.color = col) }, "Color") }));
7083
+ return (jsxs(Fragment, { children: [jsx(Color4PropertyLine, { label: "Color", description: "Color to tint the sprite.", value: color, onChange: (col) => (sprite.color = col) }, "Color"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Pickable", target: sprite, propertyKey: "isPickable" }, "IsPickable"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Use Alpha for Picking", target: sprite, propertyKey: "useAlphaForPicking" }, "UseAlphaForPicking")] }));
6944
7084
  };
6945
-
6946
- const SpriteTransformProperties = (props) => {
6947
- const { sprite, settings } = props;
6948
- const [toDisplayAngle, fromDisplayAngle, useDegrees] = useAngleConverters(settings);
6949
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: Vector3PropertyLine, label: "Position", target: sprite, propertyKey: "position" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Angle", description: `Rotation angle of the sprite in ${useDegrees ? "degrees" : "radians"}`, min: 0, max: toDisplayAngle(Math.PI * 2), step: toDisplayAngle(0.01), target: sprite, propertyKey: "angle", convertTo: toDisplayAngle, convertFrom: fromDisplayAngle }, "Angle"), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Width", description: "Width of the sprite (in world space units)", target: sprite, propertyKey: "width" }, "Width"), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Height", description: "Height of the sprite (in world space units)", target: sprite, propertyKey: "height" }, "Height")] }));
7085
+ const SpriteCellProperties = (props) => {
7086
+ const { sprite } = props;
7087
+ const maxCellCount = useMaxCellCount(sprite);
7088
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Cell Index", target: sprite, propertyKey: "cellIndex", min: 0, step: 1, max: maxCellCount }, "CellIndex"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Invert U", target: sprite, propertyKey: "invertU" }, "InvertU"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Invert V", target: sprite, propertyKey: "invertV" }, "InvertV")] }));
6950
7089
  };
6951
7090
 
6952
7091
  const SpritePropertiesServiceDefinition = {
6953
7092
  friendlyName: "Sprite Properties",
6954
7093
  consumes: [PropertiesServiceIdentity, SelectionServiceIdentity, SettingsContextIdentity],
6955
7094
  factory: (propertiesService, selectionService, settingsContent) => {
6956
- const generalSectionContentRegistration = propertiesService.addSectionContent({
6957
- key: "Sprite Properties",
6958
- predicate: (entity) => entity instanceof Sprite,
7095
+ const spriteManagerSectionContentRegistration = propertiesService.addSectionContent({
7096
+ key: "Sprite Manager Properties",
7097
+ predicate: (entity) => entity instanceof SpriteManager,
6959
7098
  content: [
6960
7099
  {
6961
7100
  section: "General",
6962
- component: ({ context }) => jsx(SpriteGeneralProperties, { sprite: context, selectionService: selectionService }),
7101
+ component: ({ context }) => jsx(SpriteManagerGeneralProperties, { spriteManager: context, selectionService: selectionService }),
7102
+ },
7103
+ {
7104
+ section: "Cells",
7105
+ component: ({ context }) => jsx(SpriteManagerCellProperties, { spriteManager: context }),
7106
+ },
7107
+ {
7108
+ section: "Other",
7109
+ component: ({ context }) => jsx(SpriteManagerOtherProperties, { spriteManager: context }),
6963
7110
  },
6964
7111
  ],
6965
7112
  });
6966
- const transformSectionContentRegistration = propertiesService.addSectionContent({
7113
+ const spriteSectionContentRegistration = propertiesService.addSectionContent({
6967
7114
  key: "Sprite Properties",
6968
7115
  predicate: (entity) => entity instanceof Sprite,
6969
7116
  content: [
7117
+ {
7118
+ section: "General",
7119
+ component: ({ context }) => jsx(SpriteGeneralProperties, { sprite: context, selectionService: selectionService }),
7120
+ },
6970
7121
  {
6971
7122
  section: "Transform",
6972
7123
  component: ({ context }) => jsx(SpriteTransformProperties, { sprite: context, settings: settingsContent }),
6973
7124
  },
6974
- ],
6975
- });
6976
- const animationSectionContentRegistration = propertiesService.addSectionContent({
6977
- key: "Sprite Properties",
6978
- predicate: (entity) => entity instanceof Sprite,
6979
- content: [
7125
+ {
7126
+ section: "Cell",
7127
+ component: ({ context }) => jsx(SpriteCellProperties, { sprite: context }),
7128
+ },
6980
7129
  {
6981
7130
  section: "Animation",
6982
7131
  component: ({ context }) => jsx(SpriteAnimationProperties, { sprite: context }),
6983
7132
  },
6984
- ],
6985
- });
6986
- const otherSectionContentRegistration = propertiesService.addSectionContent({
6987
- key: "Sprite Properties",
6988
- predicate: (entity) => entity instanceof Sprite,
6989
- content: [
6990
7133
  {
6991
7134
  section: "Other",
6992
7135
  component: ({ context }) => jsx(SpriteOtherProperties, { sprite: context }),
@@ -6995,10 +7138,8 @@ const SpritePropertiesServiceDefinition = {
6995
7138
  });
6996
7139
  return {
6997
7140
  dispose: () => {
6998
- generalSectionContentRegistration.dispose();
6999
- transformSectionContentRegistration.dispose();
7000
- otherSectionContentRegistration.dispose();
7001
- animationSectionContentRegistration.dispose();
7141
+ spriteManagerSectionContentRegistration.dispose();
7142
+ spriteSectionContentRegistration.dispose();
7002
7143
  },
7003
7144
  };
7004
7145
  },
@@ -7105,28 +7246,29 @@ function FindTextureType(type) {
7105
7246
  }
7106
7247
 
7107
7248
  const useStyles$3 = makeStyles({
7108
- root: { display: "flex", flexDirection: "column", gap: "8px" },
7249
+ root: {
7250
+ display: "flex",
7251
+ flexDirection: "column",
7252
+ },
7109
7253
  controls: {
7110
7254
  display: "flex",
7111
- gap: "2px",
7112
- padding: "2px",
7113
- width: "100%",
7114
- justifyContent: "center",
7255
+ gap: tokens.spacingHorizontalXS,
7115
7256
  },
7116
7257
  controlButton: {
7117
7258
  minWidth: "auto",
7118
7259
  flex: "1 1 0", // Equal flex grow/shrink with 0 basis
7119
- paddingVertical: "4px",
7120
- paddingHorizontal: "8px",
7260
+ paddingVertical: tokens.spacingVerticalXS,
7261
+ paddingHorizontal: tokens.spacingHorizontalS,
7121
7262
  overflow: "hidden",
7122
7263
  textOverflow: "ellipsis",
7123
7264
  },
7124
7265
  preview: {
7125
- border: "1px solid #ccc",
7126
- marginTop: "8px",
7266
+ border: `1px solid ${tokens.colorNeutralStroke1}`,
7267
+ marginTop: tokens.spacingVerticalXS,
7127
7268
  maxWidth: "100%",
7128
7269
  marginLeft: "auto",
7129
7270
  marginRight: "auto",
7271
+ marginBottom: tokens.spacingVerticalS,
7130
7272
  display: "block",
7131
7273
  },
7132
7274
  });
@@ -7328,20 +7470,6 @@ const CoordinatesMode = [
7328
7470
  { label: "Skybox", value: Texture.SKYBOX_MODE },
7329
7471
  { label: "Spherical", value: Texture.SPHERICAL_MODE },
7330
7472
  ];
7331
- const SamplingMode = [
7332
- { label: "Nearest", value: Texture.NEAREST_NEAREST }, // 1
7333
- { label: "Linear", value: Texture.LINEAR_LINEAR }, // 2
7334
- { label: "Linear & linear mip", value: Texture.LINEAR_LINEAR_MIPLINEAR }, // 3
7335
- { label: "Linear & nearest mip", value: Texture.LINEAR_LINEAR_MIPNEAREST }, // 11
7336
- { label: "Nearest & linear mip", value: Texture.NEAREST_NEAREST_MIPLINEAR }, // 8
7337
- { label: "Nearest & nearest mip", value: Texture.NEAREST_NEAREST_MIPNEAREST }, // 4
7338
- { label: "Nearest/Linear", value: Texture.NEAREST_LINEAR }, // 7
7339
- { label: "Nearest/Linear & linear mip", value: Texture.NEAREST_LINEAR_MIPLINEAR }, // 6
7340
- { label: "Nearest/Linear & nearest mip", value: Texture.NEAREST_LINEAR_MIPNEAREST }, // 5
7341
- { label: "Linear/Nearest", value: Texture.LINEAR_NEAREST }, // 12
7342
- { label: "Linear/Nearest & linear mip", value: Texture.LINEAR_NEAREST_MIPLINEAR }, // 10
7343
- { label: "Linear/Nearest & nearest mip", value: Texture.LINEAR_NEAREST_MIPNEAREST }, // 9
7344
- ];
7345
7473
  const BaseTextureCharacteristicProperties = (props) => {
7346
7474
  const { texture } = props;
7347
7475
  const internalTexture = useProperty(texture, "_texture");
@@ -7351,7 +7479,7 @@ const BaseTextureCharacteristicProperties = (props) => {
7351
7479
  const useSRGBBuffer = useProperty(internalTexture, "_useSRGBBuffer");
7352
7480
  const displayFormat = FindTextureFormat(format === -1 ? Constants.TEXTUREFORMAT_RGBA : format);
7353
7481
  const displayType = FindTextureType(type === -1 ? Constants.TEXTURETYPE_UNSIGNED_BYTE : type);
7354
- return (jsxs(Fragment, { children: [texture.is2DArray && jsx(TextPropertyLine, { label: "Layers", value: depth?.toString() ?? "?" }), texture.is3D && jsx(TextPropertyLine, { label: "Depth", value: depth?.toString() ?? "?" }), jsx(TextPropertyLine, { label: "Format", value: displayFormat?.label ?? "unknown" }), !displayFormat?.hideType && !displayFormat?.compressed && jsx(TextPropertyLine, { label: "Type", value: displayType?.label ?? "unknown" }), !!displayFormat?.normalizable && !displayFormat?.compressed && displayType?.normalizable != undefined && (jsx(BooleanBadgePropertyLine, { label: "Normalized", value: displayType.normalizable })), jsx(BooleanBadgePropertyLine, { label: "Compressed", value: displayFormat?.compressed ?? false }), jsx(BooleanBadgePropertyLine, { label: "sRGB Buffers", value: useSRGBBuffer ?? false }), jsx(BoundProperty, { component: BooleanBadgePropertyLine, label: "Gamma Space", target: texture, propertyKey: "gammaSpace" }), jsx(BoundProperty, { component: BooleanBadgePropertyLine, label: "Has Alpha", target: texture, propertyKey: "hasAlpha" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Alpha from RGB", target: texture, propertyKey: "getAlphaFromRGB" }), jsx(BooleanBadgePropertyLine, { label: "3D", value: texture.is3D }), jsx(BooleanBadgePropertyLine, { label: "2D Array", value: texture.is2DArray }), jsx(BooleanBadgePropertyLine, { label: "Cube", value: texture.isCube }), jsx(BooleanBadgePropertyLine, { label: "Render Target", value: texture.isRenderTarget }), jsx(BooleanBadgePropertyLine, { label: "Mipmaps", value: !texture.noMipmap }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "UV Set", target: texture, propertyKey: "coordinatesIndex", min: 0, max: 3, step: 1 }), jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Mode", target: texture, propertyKey: "coordinatesMode", options: CoordinatesMode }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Level", target: texture, propertyKey: "level", min: 0, max: 2, step: 0.01 }), jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Sampling", target: texture, propertyKey: "samplingMode", options: SamplingMode })] }));
7482
+ return (jsxs(Fragment, { children: [texture.is2DArray && jsx(TextPropertyLine, { label: "Layers", value: depth?.toString() ?? "?" }), texture.is3D && jsx(TextPropertyLine, { label: "Depth", value: depth?.toString() ?? "?" }), jsx(TextPropertyLine, { label: "Format", value: displayFormat?.label ?? "unknown" }), !displayFormat?.hideType && !displayFormat?.compressed && jsx(TextPropertyLine, { label: "Type", value: displayType?.label ?? "unknown" }), !!displayFormat?.normalizable && !displayFormat?.compressed && displayType?.normalizable != undefined && (jsx(BooleanBadgePropertyLine, { label: "Normalized", value: displayType.normalizable })), jsx(BooleanBadgePropertyLine, { label: "Compressed", value: displayFormat?.compressed ?? false }), jsx(BooleanBadgePropertyLine, { label: "sRGB Buffers", value: useSRGBBuffer ?? false }), jsx(BoundProperty, { component: BooleanBadgePropertyLine, label: "Gamma Space", target: texture, propertyKey: "gammaSpace" }), jsx(BoundProperty, { component: BooleanBadgePropertyLine, label: "Has Alpha", target: texture, propertyKey: "hasAlpha" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Alpha from RGB", target: texture, propertyKey: "getAlphaFromRGB" }), jsx(BooleanBadgePropertyLine, { label: "3D", value: texture.is3D }), jsx(BooleanBadgePropertyLine, { label: "2D Array", value: texture.is2DArray }), jsx(BooleanBadgePropertyLine, { label: "Cube", value: texture.isCube }), jsx(BooleanBadgePropertyLine, { label: "Render Target", value: texture.isRenderTarget }), jsx(BooleanBadgePropertyLine, { label: "Mipmaps", value: !texture.noMipmap }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "UV Set", target: texture, propertyKey: "coordinatesIndex", min: 0, max: 3, step: 1 }), jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Mode", target: texture, propertyKey: "coordinatesMode", options: CoordinatesMode }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Level", target: texture, propertyKey: "level", min: 0, max: 2, step: 0.01 })] }));
7355
7483
  };
7356
7484
  const BaseTextureTransformProperties = (props) => {
7357
7485
  const { texture } = props;
@@ -7400,10 +7528,29 @@ const TextureTransformProperties = (props) => {
7400
7528
  return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "U offset", target: texture, propertyKey: "uOffset", step: 0.01 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "V offset", target: texture, propertyKey: "vOffset", step: 0.01 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "U scale", target: texture, propertyKey: "uScale", step: 0.01 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "V scale", target: texture, propertyKey: "vScale", step: 0.01 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "U angle", target: texture, propertyKey: "uAng", min: 0, max: toDisplayAngle(Math.PI * 2), convertTo: (value) => toDisplayAngle(value, true), convertFrom: fromDisplayAngle }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "V angle", target: texture, propertyKey: "vAng", min: 0, max: toDisplayAngle(Math.PI * 2), convertTo: (value) => toDisplayAngle(value, true), convertFrom: fromDisplayAngle }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "W angle", target: texture, propertyKey: "wAng", min: 0, max: toDisplayAngle(Math.PI * 2), convertTo: (value) => toDisplayAngle(value, true), convertFrom: fromDisplayAngle }), jsx(SwitchPropertyLine, { label: "Clamp U", value: wrapU === Constants.TEXTURE_CLAMP_ADDRESSMODE, onChange: (value) => (texture.wrapU = value ? Constants.TEXTURE_CLAMP_ADDRESSMODE : Constants.TEXTURE_WRAP_ADDRESSMODE) }), jsx(SwitchPropertyLine, { label: "Clamp V", value: wrapV === Constants.TEXTURE_CLAMP_ADDRESSMODE, onChange: (value) => (texture.wrapV = value ? Constants.TEXTURE_CLAMP_ADDRESSMODE : Constants.TEXTURE_WRAP_ADDRESSMODE) })] }));
7401
7529
  };
7402
7530
 
7531
+ const SamplingMode = [
7532
+ { label: "Nearest", value: Texture.NEAREST_NEAREST }, // 1
7533
+ { label: "Linear", value: Texture.LINEAR_LINEAR }, // 2
7534
+ { label: "Linear & linear mip", value: Texture.LINEAR_LINEAR_MIPLINEAR }, // 3
7535
+ { label: "Linear & nearest mip", value: Texture.LINEAR_LINEAR_MIPNEAREST }, // 11
7536
+ { label: "Nearest & linear mip", value: Texture.NEAREST_NEAREST_MIPLINEAR }, // 8
7537
+ { label: "Nearest & nearest mip", value: Texture.NEAREST_NEAREST_MIPNEAREST }, // 4
7538
+ { label: "Nearest/Linear", value: Texture.NEAREST_LINEAR }, // 7
7539
+ { label: "Nearest/Linear & linear mip", value: Texture.NEAREST_LINEAR_MIPLINEAR }, // 6
7540
+ { label: "Nearest/Linear & nearest mip", value: Texture.NEAREST_LINEAR_MIPNEAREST }, // 5
7541
+ { label: "Linear/Nearest", value: Texture.LINEAR_NEAREST }, // 12
7542
+ { label: "Linear/Nearest & linear mip", value: Texture.LINEAR_NEAREST_MIPLINEAR }, // 10
7543
+ { label: "Linear/Nearest & nearest mip", value: Texture.LINEAR_NEAREST_MIPNEAREST }, // 9
7544
+ ];
7403
7545
  const ThinTextureGeneralProperties = (props) => {
7404
7546
  const { texture } = props;
7405
7547
  return (jsxs(Fragment, { children: [jsx(StringifiedPropertyLine, { label: "Width", value: texture.getSize().width, units: "px" }), jsx(StringifiedPropertyLine, { label: "Height", value: texture.getSize().height, units: "px" })] }));
7406
7548
  };
7549
+ const ThinTextureSamplingProperties = (props) => {
7550
+ const { texture } = props;
7551
+ const samplingMode = useObservableState(useCallback(() => texture.samplingMode, [texture]), useInterceptObservable("function", texture, "updateSamplingMode"));
7552
+ return jsx(NumberDropdownPropertyLine, { label: "Sampling", value: samplingMode, options: SamplingMode, onChange: (value) => texture.updateSamplingMode(value) });
7553
+ };
7407
7554
 
7408
7555
  // Don't use instanceof in this case as we don't want to bring in the gui package just to check if the entity is an AdvancedDynamicTexture.
7409
7556
  function IsAdvancedDynamicTexture$1(entity) {
@@ -7446,6 +7593,11 @@ const TexturePropertiesServiceDefinition = {
7446
7593
  order: 100,
7447
7594
  component: ({ context }) => jsx(ThinTextureGeneralProperties, { texture: context }),
7448
7595
  },
7596
+ {
7597
+ section: "General",
7598
+ order: 200,
7599
+ component: ({ context }) => jsx(ThinTextureSamplingProperties, { texture: context }),
7600
+ },
7449
7601
  ],
7450
7602
  });
7451
7603
  const textureContentRegistration = propertiesService.addSectionContent({
@@ -8482,6 +8634,22 @@ const PickingServiceDefinition = {
8482
8634
  },
8483
8635
  };
8484
8636
 
8637
+ const UserFeedbackServiceDefinition = {
8638
+ friendlyName: "User Feedback",
8639
+ consumes: [ShellServiceIdentity],
8640
+ factory: (shellService) => {
8641
+ shellService.addToolbarItem({
8642
+ key: "User Feedback",
8643
+ verticalLocation: "bottom",
8644
+ horizontalLocation: "right",
8645
+ suppressTeachingMoment: true,
8646
+ component: () => {
8647
+ return (jsx(Button, { appearance: "subtle", icon: PersonFeedbackRegular, title: "Give Feedback on Inspector v2", onClick: () => window.open("https://forum.babylonjs.com/", "_blank") }));
8648
+ },
8649
+ });
8650
+ },
8651
+ };
8652
+
8485
8653
  let CurrentInspectorToken = null;
8486
8654
  function IsInspectorVisible() {
8487
8655
  return CurrentInspectorToken != null;
@@ -8642,6 +8810,10 @@ function _ShowInspector(scene, options) {
8642
8810
  GizmoToolbarServiceDefinition,
8643
8811
  // Allows picking objects from the scene to select them.
8644
8812
  PickingServiceDefinition,
8813
+ // Adds entry points for user feedback on Inspector v2 (probably eventually will be removed).
8814
+ UserFeedbackServiceDefinition,
8815
+ // Adds always present "mini stats" (like fps) to the toolbar, etc.
8816
+ MiniStatsServiceDefinition,
8645
8817
  // Additional services passed in to the Inspector.
8646
8818
  ...(options.serviceDefinitions ?? []),
8647
8819
  ],
@@ -8931,21 +9103,6 @@ const Pane = (props) => {
8931
9103
  return (jsxs("div", { className: classes.rootDiv, children: [jsxs("div", { className: classes.header, children: [props.icon ? (jsx(props.icon, { className: classes.icon })) : (jsx("img", { className: classes.icon, id: "logo", src: "https://www.babylonjs.com/Assets/logo-babylonjs-social-twitter.png" })), jsx(Body1Strong, { id: "title", children: props.title })] }), props.children] }));
8932
9104
  };
8933
9105
 
8934
- /**
8935
- * Wraps a hex input in a property line
8936
- * @param props - PropertyLineProps and InputHexProps
8937
- * @returns property-line wrapped input hex component
8938
- */
8939
- const HexPropertyLine = (props) => {
8940
- HexPropertyLine.displayName = "HexPropertyLine";
8941
- return (jsx(PropertyLine, { ...props, children: jsx(InputHexField, { ...props }) }));
8942
- };
8943
-
8944
- const SpinButtonPropertyLine = (props) => {
8945
- SpinButtonPropertyLine.displayName = "SpinButtonPropertyLine";
8946
- return (jsx(PropertyLine, { ...props, children: jsx(SpinButton, { ...props }) }));
8947
- };
8948
-
8949
9106
  /**
8950
9107
  * Wraps textarea in a property line
8951
9108
  * @param props - PropertyLineProps and TextProps
@@ -8956,5 +9113,5 @@ const TextAreaPropertyLine = (props) => {
8956
9113
  return (jsx(PropertyLine, { ...props, children: jsx(Textarea, { ...props }) }));
8957
9114
  };
8958
9115
 
8959
- export { Accordion, AccordionSection, BooleanBadgePropertyLine, BoundProperty, BuiltInsExtensionFeed, Button, ButtonLine, CalculatePrecision, Checkbox, CheckboxPropertyLine, Collapse, Color3GradientComponent, Color3GradientList, Color3PropertyLine, Color4GradientComponent, Color4GradientList, Color4PropertyLine, ColorPickerPopup, ColorStepGradientComponent, ComboBox, ConstructorFactory, DebugServiceIdentity, DraggableLine, Dropdown, ExtensibleAccordion, FactorGradientComponent, FactorGradientList, FileUploadLine, GetPropertyDescriptor, HexPropertyLine, HideInspector, InfoLabel, InputHexField, InputHsvField, Inspector, InterceptFunction, InterceptProperty, IsInspectorVisible, IsPropertyReadonly, LineContainer, LinkPropertyLine, LinkToEntityPropertyLine, List, MakeDialogTeachingMoment, MakeLazyComponent, MakePopoverTeachingMoment, MakePropertyHook, MakeTeachingMoment, MessageBar, NumberDropdown, NumberDropdownPropertyLine, NumberInputPropertyLine, ObservableCollection, Pane, PlaceholderPropertyLine, PositionedPopover, PropertiesServiceIdentity, PropertyLine, QuaternionPropertyLine, RotationVectorPropertyLine, SceneContextIdentity, SceneExplorerServiceIdentity, SearchBar, SearchBox, SelectionServiceDefinition, SelectionServiceIdentity, SettingsContextIdentity, SettingsServiceIdentity, ShellServiceIdentity, ShowInspector, SidePaneContainer, SpinButton, SpinButtonPropertyLine, StatsServiceIdentity, StringDropdown, StringDropdownPropertyLine, StringifiedPropertyLine, Switch, SwitchPropertyLine, SyncedSliderInput, SyncedSliderPropertyLine, TeachingMoment, TextAreaPropertyLine, TextInput, TextInputPropertyLine, TextPropertyLine, Textarea, ToggleButton, ToolsServiceIdentity, Vector2PropertyLine, Vector3PropertyLine, Vector4PropertyLine, useAngleConverters, useAsyncResource, useColor3Property, useColor4Property, useEventfulState, useInterceptObservable, useObservableCollection, useObservableState, useOrderedObservableCollection, usePollingObservable, useProperty, useQuaternionProperty, useResource, useVector3Property };
9116
+ export { Accordion, AccordionSection, BooleanBadgePropertyLine, BoundProperty, BuiltInsExtensionFeed, Button, ButtonLine, Checkbox, CheckboxPropertyLine, Collapse, Color3GradientComponent, Color3GradientList, Color3PropertyLine, Color4GradientComponent, Color4GradientList, Color4PropertyLine, ColorPickerPopup, ColorStepGradientComponent, ComboBox, ConstructorFactory, DebugServiceIdentity, DraggableLine, Dropdown, ExtensibleAccordion, FactorGradientComponent, FactorGradientList, FileUploadLine, GetPropertyDescriptor, HexPropertyLine, HideInspector, InfoLabel, InputHexField, InputHsvField, Inspector, InterceptFunction, InterceptProperty, IsInspectorVisible, IsPropertyReadonly, LineContainer, LinkPropertyLine, LinkToEntityPropertyLine, List, MakeDialogTeachingMoment, MakeLazyComponent, MakePopoverTeachingMoment, MakePropertyHook, MakeTeachingMoment, MessageBar, NumberDropdown, NumberDropdownPropertyLine, NumberInputPropertyLine, ObservableCollection, Pane, PlaceholderPropertyLine, PositionedPopover, PropertiesServiceIdentity, PropertyLine, QuaternionPropertyLine, RotationVectorPropertyLine, SceneContextIdentity, SceneExplorerServiceIdentity, SearchBar, SearchBox, SelectionServiceDefinition, SelectionServiceIdentity, SettingsContextIdentity, SettingsServiceIdentity, ShellServiceIdentity, ShowInspector, SidePaneContainer, SpinButton, SpinButtonPropertyLine, StatsServiceIdentity, StringDropdown, StringDropdownPropertyLine, StringifiedPropertyLine, Switch, SwitchPropertyLine, SyncedSliderInput, SyncedSliderPropertyLine, TeachingMoment, TextAreaPropertyLine, TextInput, TextInputPropertyLine, TextPropertyLine, Textarea, ToggleButton, ToolsServiceIdentity, Vector2PropertyLine, Vector3PropertyLine, Vector4PropertyLine, useAngleConverters, useAsyncResource, useColor3Property, useColor4Property, useEventfulState, useInterceptObservable, useObservableCollection, useObservableState, useOrderedObservableCollection, usePollingObservable, useProperty, useQuaternionProperty, useResource, useVector3Property };
8960
9117
  //# sourceMappingURL=index.js.map