@babylonjs/inspector 9.6.2 → 9.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import { createContext, forwardRef, useContext, useState, useCallback, Component, useMemo, useEffect, useRef, useReducer, Children, isValidElement, useLayoutEffect, useImperativeHandle, cloneElement, createElement, Suspense, memo, Fragment as Fragment$1, lazy } from 'react';
3
- import { tokens, makeStyles, Tooltip as Tooltip$1, Button as Button$1, Spinner, Link as Link$1, Caption1, Body1, useFluent, Accordion as Accordion$1, AccordionHeader, Subtitle2Stronger, AccordionPanel, Divider, MessageBar as MessageBar$1, MessageBarBody, AccordionItem, SearchBox as SearchBox$1, Portal, ToggleButton as ToggleButton$1, InfoLabel as InfoLabel$1, Body1Strong, mergeClasses, useId, useToastController, Toast, ToastTitle, FluentProvider, Toaster, Checkbox as Checkbox$1, createLightTheme, createDarkTheme, TeachingPopover, TeachingPopoverSurface, TeachingPopoverHeader, TeachingPopoverBody, createDOMRenderer, RendererProvider, Menu, MenuTrigger, SplitButton, MenuPopover, MenuList, MenuItem, Toolbar as Toolbar$1, ToolbarRadioButton, MenuGroup, MenuGroupHeader, Switch as Switch$1, treeItemLevelToken, FlatTree, FlatTreeItem, TreeItemLayout, MenuDivider, MenuItemCheckbox, useMergedRefs, Input, Dropdown as Dropdown$1, Option, Popover as Popover$1, PopoverTrigger, PopoverSurface, ColorPicker, ColorArea, ColorSlider, AlphaSlider, ColorSwatch, PresenceBadge, Slider as Slider$1, MenuItemRadio, Dialog, DialogSurface, DialogBody, DialogTitle, DialogContent, DialogActions, List as List$1, ListItem, Badge, Label, MessageBarTitle, useComboboxFilter, Combobox, Subtitle2, Textarea as Textarea$1, ToolbarButton, ToolbarDivider, Field } from '@fluentui/react-components';
4
- import { ErrorCircleRegular, EyeFilled, EyeOffRegular, CheckmarkFilled, EditRegular, FilterRegular, PinFilled, PinRegular, ArrowCircleUpRegular, ChevronCircleRight16Regular, ChevronCircleRight20Regular, ChevronCircleDown16Regular, ChevronCircleDown20Regular, Copy16Regular, CopyRegular, PanelLeftExpandRegular, PanelRightExpandRegular, PanelLeftContractRegular, PanelRightContractRegular, PictureInPictureEnterRegular, MoreHorizontalRegular, LayoutColumnTwoFocusLeftFilled, LayoutColumnTwoSplitLeftFocusTopLeftFilled, LayoutColumnTwoSplitLeftFocusBottomLeftFilled, LayoutColumnTwoFocusRightFilled, LayoutColumnTwoSplitRightFocusTopRightFilled, LayoutColumnTwoSplitRightFocusBottomRightFilled, SettingsRegular, DocumentTextRegular, createFluentIcon, TextSortAscendingRegular, GlobeRegular, WarningRegular, ArrowExpandAllRegular, ArrowCollapseAllRegular, CubeTreeRegular, BugRegular, ArrowUploadRegular, ArrowBidirectionalUpDownFilled, ArrowDownloadRegular, StopRegular, RecordRegular, DataBarHorizontalRegular, WrenchRegular, ArrowClockwiseRegular, WeatherSunnyRegular, WeatherMoonRegular, PlugDisconnectedRegular, PlugConnectedRegular, PlugConnectedCheckmarkRegular, ArrowRotateClockwiseRegular, ArrowExpandRegular, SelectObjectRegular, CubeRegular, CameraRegular, AddRegular, DeleteRegular, FullScreenMaximizeRegular, ChevronDownRegular, ChevronRightRegular, CircleSmallFilled, SaveRegular, PreviousRegular, ArrowPreviousRegular, TriangleLeftRegular, RecordStopRegular, PlayRegular, ArrowNextRegular, NextRegular, PauseRegular, LinkDismissRegular, LinkEditRegular, ArrowUndoRegular, BracesRegular, BracesDismiss16Regular, EyeRegular, CloudArrowUpRegular, CloudArrowDownRegular, EyeOffFilled, ArrowMoveFilled, StopFilled, PlayFilled, LockOpenRegular, LockClosedRegular, ResizeRegular, ChevronUpRegular, ArrowResetRegular, CircleHalfFillRegular, EyedropperRegular, PaintBucketRegular, InkStrokeRegular, StackRegular, FilmstripRegular, PauseFilled, WeatherSunnyLowFilled, LayerRegular, FrameRegular, AppGenericRegular, RectangleLandscapeRegular, BorderOutsideRegular, BorderNoneRegular, MyLocationRegular, BubbleMultipleRegular, LightbulbRegular, VideoFilled, VideoRegular, FlashlightRegular, FlashlightOffRegular, DropRegular, BlurRegular, PipelineRegular, PersonWalkingRegular, DataLineRegular, SoundWaveCircleRegular, PersonSquareRegular, LayerDiagonalPersonRegular, ImageEditRegular, ImageRegular, TargetRegular, PersonFeedbackRegular, BranchRegular, DeleteFilled } from '@fluentui/react-icons';
3
+ import { tokens, makeStyles, Tooltip as Tooltip$1, Button as Button$1, Spinner, Link as Link$1, Caption1, Body1, useFluent, Accordion as Accordion$1, AccordionHeader, Subtitle2Stronger, AccordionPanel, Divider, MessageBar as MessageBar$1, MessageBarBody, AccordionItem, SearchBox as SearchBox$1, Portal, ToggleButton as ToggleButton$1, InfoLabel as InfoLabel$1, Body1Strong, mergeClasses, useId, useToastController, Toast, ToastTitle, FluentProvider, Toaster, Checkbox as Checkbox$1, createLightTheme, createDarkTheme, TeachingPopover, TeachingPopoverSurface, TeachingPopoverHeader, TeachingPopoverBody, createDOMRenderer, RendererProvider, Menu, MenuTrigger, SplitButton, MenuPopover, MenuList, MenuItem, Toolbar as Toolbar$1, ToolbarRadioButton, MenuGroup, MenuGroupHeader, Switch as Switch$1, treeItemLevelToken, FlatTree, FlatTreeItem, TreeItemLayout, MenuDivider, MenuItemCheckbox, useMergedRefs, Input, Dropdown as Dropdown$1, Option, Popover as Popover$1, PopoverTrigger, PopoverSurface, ColorPicker, ColorArea, ColorSlider, AlphaSlider, ColorSwatch, PresenceBadge, Slider as Slider$1, MenuItemRadio, Dialog as Dialog$1, DialogSurface, DialogBody, DialogTitle, DialogContent, DialogActions, List as List$1, ListItem, Badge, Label, MessageBarTitle, useComboboxFilter, Combobox, Subtitle2, Textarea as Textarea$1, ToolbarButton, ToolbarDivider, DialogTrigger, Field } from '@fluentui/react-components';
4
+ import { ErrorCircleRegular, EyeFilled, EyeOffRegular, CheckmarkFilled, EditRegular, FilterRegular, PinFilled, PinRegular, ArrowCircleUpRegular, ChevronCircleRight16Regular, ChevronCircleRight20Regular, ChevronCircleDown16Regular, ChevronCircleDown20Regular, Copy16Regular, CopyRegular, PanelLeftExpandRegular, PanelRightExpandRegular, PanelLeftContractRegular, PanelRightContractRegular, PictureInPictureEnterRegular, MoreHorizontalRegular, LayoutColumnTwoFocusLeftFilled, LayoutColumnTwoSplitLeftFocusTopLeftFilled, LayoutColumnTwoSplitLeftFocusBottomLeftFilled, LayoutColumnTwoFocusRightFilled, LayoutColumnTwoSplitRightFocusTopRightFilled, LayoutColumnTwoSplitRightFocusBottomRightFilled, SettingsRegular, DocumentTextRegular, createFluentIcon, TextSortAscendingRegular, GlobeRegular, WarningRegular, ArrowExpandAllRegular, ArrowCollapseAllRegular, CubeTreeRegular, BugRegular, ArrowUploadRegular, ArrowBidirectionalUpDownFilled, ArrowDownloadRegular, StopRegular, RecordRegular, DataBarHorizontalRegular, WrenchRegular, ArrowClockwiseRegular, WeatherSunnyRegular, WeatherMoonRegular, PlugDisconnectedRegular, PlugConnectedRegular, PlugConnectedCheckmarkRegular, ArrowRotateClockwiseRegular, ArrowExpandRegular, SelectObjectRegular, CubeRegular, CameraRegular, AddRegular, DeleteRegular, FullScreenMaximizeRegular, ChevronDownRegular, ChevronRightRegular, CircleSmallFilled, SaveRegular, PreviousRegular, ArrowPreviousRegular, TriangleLeftRegular, RecordStopRegular, PlayRegular, ArrowNextRegular, NextRegular, PauseRegular, LinkDismissRegular, LinkEditRegular, ArrowUndoRegular, BracesRegular, BracesDismiss16Regular, EyeRegular, CloudArrowUpRegular, CloudArrowDownRegular, EyeOffFilled, ArrowMoveFilled, StopFilled, PlayFilled, LockOpenRegular, LockClosedRegular, ResizeRegular, ChevronUpRegular, ArrowResetRegular, CircleHalfFillRegular, EyedropperRegular, PaintBucketRegular, InkStrokeRegular, StackRegular, FilmstripRegular, PauseFilled, WeatherSunnyLowFilled, LayerRegular, FrameRegular, AppGenericRegular, RectangleLandscapeRegular, BorderOutsideRegular, BorderNoneRegular, MyLocationRegular, BubbleMultipleRegular, LightbulbRegular, VideoFilled, VideoRegular, FlashlightRegular, FlashlightOffRegular, DropRegular, BlurRegular, PipelineRegular, PersonWalkingRegular, DataLineRegular, SoundWaveCircleRegular, PersonSquareRegular, LayerDiagonalPersonRegular, ImageEditRegular, ImageRegular, LinkRegular, ArrowSyncRegular, TargetRegular, PersonFeedbackRegular, DismissRegular, BranchRegular, DeleteFilled } from '@fluentui/react-icons';
5
5
  import { Color3, Color4 } from '@babylonjs/core/Maths/math.color.js';
6
6
  import { Vector3, Quaternion, Matrix, Vector2, Vector4, TmpVectors } from '@babylonjs/core/Maths/math.vector.js';
7
7
  import { Observable } from '@babylonjs/core/Misc/observable.js';
@@ -144,6 +144,7 @@ import { SceneRecorder } from '@babylonjs/core/Misc/sceneRecorder.js';
144
144
  import { VideoRecorder } from '@babylonjs/core/Misc/videoRecorder.js';
145
145
  import { SceneSerializer } from '@babylonjs/core/Misc/sceneSerializer.js';
146
146
  import { EnvironmentTextureTools } from '@babylonjs/core/Misc/environmentTextureTools.js';
147
+ import { SerializeSmartAssetManagerMap, GetAllSmartAssets, RemoveSmartAssetAsync, LoadSmartAssetMapAsync, GetSmartAssetTextureExtensions, GetSmartAssetManager, LoadSmartAssetTextureAsync, LoadSmartAssetAsync, ReloadSmartAssetAsync, FindSmartAssetKeyForObject, UnloadSmartAssetAsync } from '@babylonjs/core/SmartAssets/smartAssetManager.js';
147
148
  import { ImportAnimationsAsync, SceneLoader } from '@babylonjs/core/Loading/sceneLoader.js';
148
149
  import { FilesInput } from '@babylonjs/core/Misc/filesInput.js';
149
150
  import { registeredGLTFExtensions } from '@babylonjs/loaders/glTF/2.0/glTFLoaderExtensionRegistry.js';
@@ -284,7 +285,7 @@ const Button = forwardRef((props, ref) => {
284
285
  });
285
286
  Button.displayName = "Button";
286
287
 
287
- const useStyles$_ = makeStyles({
288
+ const useStyles$11 = makeStyles({
288
289
  root: {
289
290
  display: "flex",
290
291
  flexDirection: "column",
@@ -365,7 +366,7 @@ class ErrorBoundary extends Component {
365
366
  }
366
367
  }
367
368
  function ErrorFallback({ error, onRetry }) {
368
- const styles = useStyles$_();
369
+ const styles = useStyles$11();
369
370
  return (jsxs("div", { className: styles.root, children: [jsx(ErrorCircleRegular, { className: styles.icon }), jsx("div", { className: styles.title, children: "Something went wrong" }), jsx("div", { className: styles.message, children: "An error occurred in this component. You can try again or continue using other parts of the tool." }), jsx(Button, { label: "Try Again", appearance: "primary", onClick: onRetry }), error && jsx("div", { className: styles.details, children: error.message })] }));
370
371
  }
371
372
 
@@ -1366,7 +1367,7 @@ function useIsSectionEmpty(sectionId) {
1366
1367
  return hasItems;
1367
1368
  }
1368
1369
 
1369
- const useStyles$Z = makeStyles({
1370
+ const useStyles$10 = makeStyles({
1370
1371
  accordion: {
1371
1372
  display: "flex",
1372
1373
  flexDirection: "column",
@@ -1458,7 +1459,7 @@ const useStyles$Z = makeStyles({
1458
1459
  */
1459
1460
  const AccordionMenuBar = () => {
1460
1461
  AccordionMenuBar.displayName = "AccordionMenuBar";
1461
- const classes = useStyles$Z();
1462
+ const classes = useStyles$10();
1462
1463
  const accordionCtx = useContext(AccordionContext);
1463
1464
  if (!accordionCtx) {
1464
1465
  return null;
@@ -1482,7 +1483,7 @@ const AccordionMenuBar = () => {
1482
1483
  const AccordionSectionBlock = (props) => {
1483
1484
  AccordionSectionBlock.displayName = "AccordionSectionBlock";
1484
1485
  const { children, sectionId } = props;
1485
- const classes = useStyles$Z();
1486
+ const classes = useStyles$10();
1486
1487
  const accordionCtx = useContext(AccordionContext);
1487
1488
  const { context: sectionContext, isEmpty } = useAccordionSectionBlockContext(props);
1488
1489
  if (accordionCtx) {
@@ -1502,7 +1503,7 @@ const AccordionSectionBlock = (props) => {
1502
1503
  const AccordionSectionItem = (props) => {
1503
1504
  AccordionSectionItem.displayName = "AccordionSectionItem";
1504
1505
  const { children, staticItem } = props;
1505
- const classes = useStyles$Z();
1506
+ const classes = useStyles$10();
1506
1507
  const accordionCtx = useContext(AccordionContext);
1507
1508
  const itemState = useAccordionSectionItemState(props);
1508
1509
  const [ctrlMode, setCtrlMode] = useState(false);
@@ -1542,7 +1543,7 @@ const AccordionSectionItem = (props) => {
1542
1543
  */
1543
1544
  const AccordionPinnedContainer = () => {
1544
1545
  AccordionPinnedContainer.displayName = "AccordionPinnedContainer";
1545
- const classes = useStyles$Z();
1546
+ const classes = useStyles$10();
1546
1547
  const accordionCtx = useContext(AccordionContext);
1547
1548
  return (jsx("div", { ref: accordionCtx?.pinnedContainerRef, className: classes.pinnedContainer, children: jsx(MessageBar$1, { className: classes.pinnedContainerEmpty, children: jsx(MessageBarBody, { children: "No pinned items" }) }) }));
1548
1549
  };
@@ -1553,7 +1554,7 @@ const AccordionPinnedContainer = () => {
1553
1554
  */
1554
1555
  const AccordionSearchBox = () => {
1555
1556
  AccordionSearchBox.displayName = "AccordionSearchBox";
1556
- const classes = useStyles$Z();
1557
+ const classes = useStyles$10();
1557
1558
  const accordionCtx = useContext(AccordionContext);
1558
1559
  if (!accordionCtx?.features.search) {
1559
1560
  return null;
@@ -1569,7 +1570,7 @@ const AccordionSearchBox = () => {
1569
1570
  */
1570
1571
  const AccordionSection = (props) => {
1571
1572
  AccordionSection.displayName = "AccordionSection";
1572
- const classes = useStyles$Z();
1573
+ const classes = useStyles$10();
1573
1574
  return jsx("div", { className: classes.panelDiv, children: props.children });
1574
1575
  };
1575
1576
  const StringAccordion = Accordion$1;
@@ -1577,7 +1578,7 @@ const Accordion = forwardRef((props, ref) => {
1577
1578
  Accordion.displayName = "Accordion";
1578
1579
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
1579
1580
  const { children, highlightSections, uniqueId, enablePinnedItems, enableHiddenItems, enableSearchItems, ...rest } = props;
1580
- const classes = useStyles$Z();
1581
+ const classes = useStyles$10();
1581
1582
  const { size } = useContext(ToolContext);
1582
1583
  const accordionCtx = useAccordionContext(props);
1583
1584
  const hasPinning = accordionCtx?.features.pinning ?? false;
@@ -1674,7 +1675,7 @@ const Collapse = (props) => {
1674
1675
  return (jsx(Collapse$1, { visible: props.visible, orientation: props.orientation, unmountOnExit: true, children: jsx("div", { className: `${classes.collapseContent} ${props.orientation === "horizontal" ? classes.horizontal : classes.vertical}`, children: props.children }) }));
1675
1676
  };
1676
1677
 
1677
- const useStyles$Y = makeStyles({
1678
+ const useStyles$$ = makeStyles({
1678
1679
  button: {
1679
1680
  display: "flex",
1680
1681
  alignItems: "center",
@@ -1692,7 +1693,7 @@ const ToggleButton = (props) => {
1692
1693
  ToggleButton.displayName = "ToggleButton";
1693
1694
  const { value, onChange, title, appearance = "subtle" } = props;
1694
1695
  const { size } = useContext(ToolContext);
1695
- const classes = useStyles$Y();
1696
+ const classes = useStyles$$();
1696
1697
  const [checked, setChecked] = useState(value);
1697
1698
  const toggle = useCallback(() => {
1698
1699
  setChecked((prevChecked) => {
@@ -2001,7 +2002,7 @@ const UXContextProvider = (props) => {
2001
2002
  function AsReadonlyArray(array) {
2002
2003
  return array;
2003
2004
  }
2004
- const useStyles$X = makeStyles({
2005
+ const useStyles$_ = makeStyles({
2005
2006
  rootDiv: {
2006
2007
  flex: 1,
2007
2008
  overflow: "hidden",
@@ -2016,7 +2017,7 @@ const useStyles$X = makeStyles({
2016
2017
  * @returns The extensible accordion component.
2017
2018
  */
2018
2019
  function ExtensibleAccordion(props) {
2019
- const classes = useStyles$X();
2020
+ const classes = useStyles$_();
2020
2021
  const { children, sections, sectionContent, context, sectionsRef, ...rest } = props;
2021
2022
  const defaultSections = useMemo(() => {
2022
2023
  const defaultSections = [];
@@ -2141,7 +2142,7 @@ function ExtensibleAccordion(props) {
2141
2142
  })] }) })) }));
2142
2143
  }
2143
2144
 
2144
- const useStyles$W = makeStyles({
2145
+ const useStyles$Z = makeStyles({
2145
2146
  paneRootDiv: {
2146
2147
  display: "flex",
2147
2148
  flex: 1,
@@ -2154,7 +2155,7 @@ const useStyles$W = makeStyles({
2154
2155
  */
2155
2156
  const SidePaneContainer = forwardRef((props, ref) => {
2156
2157
  const { className, ...rest } = props;
2157
- const classes = useStyles$W();
2158
+ const classes = useStyles$Z();
2158
2159
  return (jsx("div", { className: mergeClasses(classes.paneRootDiv, className), ref: ref, ...rest, children: props.children }));
2159
2160
  });
2160
2161
 
@@ -2425,7 +2426,7 @@ function useTheme(invert = false) {
2425
2426
  }
2426
2427
 
2427
2428
  // Fluent doesn't apply styling to scrollbars by default, so provide our own reasonable default.
2428
- const useStyles$V = makeStyles({
2429
+ const useStyles$Y = makeStyles({
2429
2430
  root: {
2430
2431
  scrollbarColor: `${tokens.colorNeutralForeground3} ${tokens.colorTransparentBackground}`,
2431
2432
  },
@@ -2441,11 +2442,11 @@ const Theme = (props) => {
2441
2442
  // break any UI within the portal. Therefore, default to false.
2442
2443
  const { invert = false, applyStylesToPortals = false, className, ...rest } = props;
2443
2444
  const theme = useTheme(invert);
2444
- const classes = useStyles$V();
2445
+ const classes = useStyles$Y();
2445
2446
  return (jsx(FluentProvider, { theme: theme, className: mergeClasses(classes.root, className), applyStylesToPortals: applyStylesToPortals, ...rest, children: props.children }));
2446
2447
  };
2447
2448
 
2448
- const useStyles$U = makeStyles({
2449
+ const useStyles$X = makeStyles({
2449
2450
  extensionTeachingPopover: {
2450
2451
  maxWidth: "320px",
2451
2452
  },
@@ -2456,7 +2457,7 @@ const useStyles$U = makeStyles({
2456
2457
  * @returns The teaching moment popover.
2457
2458
  */
2458
2459
  const TeachingMoment = ({ shouldDisplay, positioningRef, onOpenChange, title, description }) => {
2459
- const classes = useStyles$U();
2460
+ const classes = useStyles$X();
2460
2461
  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 })] }) }));
2461
2462
  };
2462
2463
 
@@ -2889,7 +2890,7 @@ const RootComponentServiceIdentity = Symbol("RootComponent");
2889
2890
  * The unique identity symbol for the shell service.
2890
2891
  */
2891
2892
  const ShellServiceIdentity = Symbol("ShellService");
2892
- const useStyles$T = makeStyles({
2893
+ const useStyles$W = makeStyles({
2893
2894
  mainView: {
2894
2895
  flex: 1,
2895
2896
  display: "flex",
@@ -3102,14 +3103,14 @@ const DockMenu = (props) => {
3102
3103
  };
3103
3104
  const PaneHeader = (props) => {
3104
3105
  const { id, title, dockOptions } = props;
3105
- const classes = useStyles$T();
3106
+ const classes = useStyles$W();
3106
3107
  return (jsxs("div", { className: classes.paneHeaderDiv, children: [props.icon && (jsx("div", { className: classes.paneHeaderIcon, children: jsx(props.icon, {}) })), jsx(Subtitle2Stronger, { className: mergeClasses(classes.paneHeaderText, !props.icon && classes.paneHeaderTextNoIcon), children: title }), jsx(DockMenu, { sidePaneId: id, dockOptions: dockOptions, children: jsx(Button$1, { className: classes.paneHeaderButton, appearance: "transparent", icon: jsx(MoreHorizontalRegular, {}) }) })] }));
3107
3108
  };
3108
3109
  // 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.
3109
3110
  const ToolbarItem = (props) => {
3110
3111
  // eslint-disable-next-line @typescript-eslint/naming-convention
3111
3112
  const { verticalLocation, horizontalLocation, id, component: Component, displayName } = props;
3112
- const classes = useStyles$T();
3113
+ const classes = useStyles$W();
3113
3114
  const useTeachingMoment = useMemo(() => MakePopoverTeachingMoment(`Bar/${verticalLocation}/${horizontalLocation}/${displayName ?? id}`), [displayName, id]);
3114
3115
  const teachingMoment = useTeachingMoment(props.teachingMoment === false);
3115
3116
  const title = typeof props.teachingMoment === "object" ? props.teachingMoment.title : (displayName ?? id);
@@ -3119,7 +3120,7 @@ const ToolbarItem = (props) => {
3119
3120
  // TODO: Handle overflow, possibly via https://react.fluentui.dev/?path=/docs/components-overflow--docs with priority.
3120
3121
  // This component just renders a toolbar with left aligned toolbar items on the left and right aligned toolbar items on the right.
3121
3122
  const Toolbar = ({ location, components }) => {
3122
- const classes = useStyles$T();
3123
+ const classes = useStyles$W();
3123
3124
  const leftComponents = useMemo(() => components.filter((entry) => entry.horizontalLocation === "left"), [components]);
3124
3125
  const rightComponents = useMemo(() => components.filter((entry) => entry.horizontalLocation === "right"), [components]);
3125
3126
  return (jsx(Fragment, { children: components.length > 0 && (jsxs("div", { className: `${classes.bar} ${location === "top" ? classes.barTop : classes.barBottom}`, children: [jsx("div", { className: classes.barLeft, children: leftComponents.map((entry) => (jsx(ToolbarItem, { verticalLocation: location, horizontalLocation: entry.horizontalLocation, id: entry.key, component: entry.component, displayName: entry.displayName, teachingMoment: entry.teachingMoment }, entry.key))) }), jsx("div", { className: classes.barRight, children: rightComponents.map((entry) => (jsx(ToolbarItem, { verticalLocation: location, horizontalLocation: entry.horizontalLocation, id: entry.key, component: entry.component, displayName: entry.displayName, teachingMoment: entry.teachingMoment }, entry.key))) })] })) }));
@@ -3129,7 +3130,7 @@ const SidePaneTab = (props) => {
3129
3130
  const { location, id, isSelected, isFirst, isLast, dockOptions,
3130
3131
  // eslint-disable-next-line @typescript-eslint/naming-convention
3131
3132
  icon: Icon, title, } = props;
3132
- const classes = useStyles$T();
3133
+ const classes = useStyles$W();
3133
3134
  const useTeachingMoment = useMemo(() => MakePopoverTeachingMoment(`Pane/${location}/${title ?? id}`), [title, id]);
3134
3135
  const teachingMoment = useTeachingMoment(props.teachingMoment === false);
3135
3136
  const tabClass = mergeClasses(classes.tab, isSelected ? classes.selectedTab : classes.unselectedTab, isFirst ? classes.firstTab : undefined, isLast ? classes.lastTab : undefined);
@@ -3141,7 +3142,7 @@ const SidePaneTab = (props) => {
3141
3142
  // In "compact" mode, the tab list is integrated into the pane itself.
3142
3143
  // In "full" mode, the returned tab list is later injected into the toolbar.
3143
3144
  function usePane(location, defaultWidth, minWidth, sidePanes, onSelectSidePane, dockOperations, toolbarMode, topBarItems, bottomBarItems, initialCollapsed) {
3144
- const classes = useStyles$T();
3145
+ const classes = useStyles$W();
3145
3146
  const [topSelectedTab, setTopSelectedTab] = useState();
3146
3147
  const [bottomSelectedTab, setBottomSelectedTab] = useState();
3147
3148
  const [collapsed, setCollapsed] = useState(initialCollapsed);
@@ -3370,7 +3371,7 @@ function MakeShellServiceDefinition({ leftPaneDefaultWidth = 350, leftPaneMinWid
3370
3371
  expand: () => onCollapseChanged.notifyObservers({ location: "right", collapsed: false }),
3371
3372
  };
3372
3373
  const rootComponent = () => {
3373
- const classes = useStyles$T();
3374
+ const classes = useStyles$W();
3374
3375
  const [sidePaneDockOverrides, setSidePaneDockOverrides] = useSetting(SidePaneDockOverridesSettingDescriptor);
3375
3376
  // This function returns a promise that resolves after the dock change takes effect so that
3376
3377
  // we can then select the re-docked pane.
@@ -3757,13 +3758,13 @@ function useImpulse() {
3757
3758
  return [value, pulse];
3758
3759
  }
3759
3760
 
3760
- const useStyles$S = makeStyles({
3761
+ const useStyles$V = makeStyles({
3761
3762
  placeholderDiv: {
3762
3763
  padding: `${tokens.spacingVerticalM} ${tokens.spacingHorizontalM}`,
3763
3764
  },
3764
3765
  });
3765
3766
  const PropertiesPane = (props) => {
3766
- const classes = useStyles$S();
3767
+ const classes = useStyles$V();
3767
3768
  const entity = props.context;
3768
3769
  return entity != null ? (jsx(ExtensibleAccordion, { ...props })) : (jsx("div", { className: classes.placeholderDiv, children: jsx(Body1Strong, { italic: true, children: "No entity selected." }) }));
3769
3770
  };
@@ -4203,7 +4204,7 @@ function CoerceEntityArray(entities, sort) {
4203
4204
  }
4204
4205
  return entities;
4205
4206
  }
4206
- const useStyles$R = makeStyles({
4207
+ const useStyles$U = makeStyles({
4207
4208
  rootDiv: {
4208
4209
  flex: 1,
4209
4210
  overflow: "hidden",
@@ -4312,14 +4313,14 @@ function MakeInlineCommandElement(command, isPlaceholder) {
4312
4313
  }
4313
4314
  const SceneTreeItem = (props) => {
4314
4315
  const { isSelected, select } = props;
4315
- const classes = useStyles$R();
4316
+ const classes = useStyles$U();
4316
4317
  const [compactMode] = useSetting(CompactModeSettingDescriptor);
4317
4318
  const treeItemLayoutClass = mergeClasses(classes.sceneTreeItemLayout, compactMode ? classes.treeItemLayoutCompact : undefined);
4318
4319
  return (jsx(FlatTreeItem, { className: classes.treeItem, value: "scene", itemType: "leaf", parentValue: undefined, "aria-level": 1, "aria-setsize": 1, "aria-posinset": 1, onClick: select, children: jsx(TreeItemLayout, { iconBefore: jsx(GlobeRegular, {}), className: treeItemLayoutClass, style: isSelected ? { backgroundColor: tokens.colorNeutralBackground1Selected } : undefined, children: jsx(Body1Strong, { wrap: false, truncate: true, children: "Scene" }) }) }, "scene"));
4319
4320
  };
4320
4321
  const SectionTreeItem = (props) => {
4321
4322
  const { section, isFiltering, commandProviders, expandAll, collapseAll, isDropTarget, ...dropProps } = props;
4322
- const classes = useStyles$R();
4323
+ const classes = useStyles$U();
4323
4324
  const [compactMode] = useSetting(CompactModeSettingDescriptor);
4324
4325
  // Get the commands that apply to this section.
4325
4326
  const commands = useResource(useCallback(() => {
@@ -4336,7 +4337,7 @@ const SectionTreeItem = (props) => {
4336
4337
  };
4337
4338
  const EntityTreeItem = (props) => {
4338
4339
  const { entityItem, isSelected, select, isFiltering, commandProviders, expandAll, collapseAll, isDragging, isDropTarget, ...dragProps } = props;
4339
- const classes = useStyles$R();
4340
+ const classes = useStyles$U();
4340
4341
  const [compactMode] = useSetting(CompactModeSettingDescriptor);
4341
4342
  const hasChildren = !!entityItem.children?.length;
4342
4343
  const displayInfo = useResource(useCallback(() => {
@@ -4452,7 +4453,7 @@ const EntityTreeItem = (props) => {
4452
4453
  }, children: jsx(Tooltip$1, { content: name, relationship: "description", children: jsx(Body1, { wrap: false, truncate: true, children: name }) }) }) }, GetEntityId$1(entityItem.entity)) }), jsx(MenuPopover, { hidden: !hasChildren && contextMenuCommands.length === 0, children: jsxs(MenuList, { children: [hasChildren && (jsxs(Fragment, { children: [jsx(MenuItem, { icon: jsx(ArrowExpandAllRegular, {}), onClick: expandAll, children: jsx(Body1, { children: "Expand All" }) }), jsx(MenuItem, { icon: jsx(ArrowCollapseAllRegular, {}), onClick: collapseAll, children: jsx(Body1, { children: "Collapse All" }) })] })), hasChildren && contextMenuCommands.length > 0 && jsx(MenuDivider, {}), contextMenuItems] }) })] }));
4453
4454
  };
4454
4455
  const SceneExplorer = (props) => {
4455
- const classes = useStyles$R();
4456
+ const classes = useStyles$U();
4456
4457
  const { sections, entityCommandProviders, sectionCommandProviders, scene, selectedEntity = null } = props;
4457
4458
  const [openItems, setOpenItems] = useState(new Set());
4458
4459
  const [sceneVersion, setSceneVersion] = useState(0);
@@ -6078,7 +6079,7 @@ class CanvasGraphService {
6078
6079
  }
6079
6080
  }
6080
6081
 
6081
- const useStyles$Q = makeStyles({
6082
+ const useStyles$T = makeStyles({
6082
6083
  canvas: {
6083
6084
  flexGrow: 1,
6084
6085
  width: "100%",
@@ -6087,7 +6088,7 @@ const useStyles$Q = makeStyles({
6087
6088
  });
6088
6089
  const CanvasGraph = (props) => {
6089
6090
  const { collector, scene, layoutObservable, returnToPlayheadObservable, onVisibleRangeChangedObservable, initialGraphSize } = props;
6090
- const classes = useStyles$Q();
6091
+ const classes = useStyles$T();
6091
6092
  const canvasRef = useRef(null);
6092
6093
  useEffect(() => {
6093
6094
  if (!canvasRef.current) {
@@ -6164,7 +6165,7 @@ function EvaluateExpression(rawValue) {
6164
6165
  return NaN;
6165
6166
  }
6166
6167
  }
6167
- const useStyles$P = makeStyles({
6168
+ const useStyles$S = makeStyles({
6168
6169
  icon: {
6169
6170
  "&:hover": {
6170
6171
  color: tokens.colorBrandForeground1,
@@ -6178,7 +6179,7 @@ const useStyles$P = makeStyles({
6178
6179
  const SpinButton = forwardRef((props, ref) => {
6179
6180
  SpinButton.displayName = "SpinButton2";
6180
6181
  const inputClasses = useInputStyles$1();
6181
- const classes = useStyles$P();
6182
+ const classes = useStyles$S();
6182
6183
  const { size } = useContext(ToolContext);
6183
6184
  const { min, max } = props;
6184
6185
  const baseStep = props.step ?? 1;
@@ -6445,7 +6446,7 @@ const Dropdown = (props) => {
6445
6446
  const NumberDropdown = Dropdown;
6446
6447
  const StringDropdown = Dropdown;
6447
6448
 
6448
- const useStyles$O = makeStyles({
6449
+ const useStyles$R = makeStyles({
6449
6450
  surface: {
6450
6451
  maxWidth: "400px",
6451
6452
  },
@@ -6460,7 +6461,7 @@ const useStyles$O = makeStyles({
6460
6461
  const Popover = forwardRef((props, ref) => {
6461
6462
  const { children, open: controlledOpen, onOpenChange, positioning, surfaceClassName } = props;
6462
6463
  const [internalOpen, setInternalOpen] = useState(false);
6463
- const classes = useStyles$O();
6464
+ const classes = useStyles$R();
6464
6465
  const isControlled = controlledOpen !== undefined;
6465
6466
  const popoverOpen = isControlled ? controlledOpen : internalOpen;
6466
6467
  const handleOpenChange = (_, data) => {
@@ -6704,7 +6705,7 @@ const InputAlphaField = (props) => {
6704
6705
  } }));
6705
6706
  };
6706
6707
 
6707
- const useStyles$N = makeStyles({
6708
+ const useStyles$Q = makeStyles({
6708
6709
  sidebar: {
6709
6710
  display: "flex",
6710
6711
  flexDirection: "column",
@@ -6768,7 +6769,7 @@ const useStyles$N = makeStyles({
6768
6769
  });
6769
6770
  const PerformanceSidebar = (props) => {
6770
6771
  const { collector, onVisibleRangeChangedObservable } = props;
6771
- const classes = useStyles$N();
6772
+ const classes = useStyles$Q();
6772
6773
  // Map from id to IPerfMetadata information
6773
6774
  const [metadataMap, setMetadataMap] = useState();
6774
6775
  // Map from category to all the ids belonging to that category
@@ -6841,7 +6842,7 @@ const PerformanceSidebar = (props) => {
6841
6842
  })] }, `category-${category || "version"}`))) }));
6842
6843
  };
6843
6844
 
6844
- const useStyles$M = makeStyles({
6845
+ const useStyles$P = makeStyles({
6845
6846
  container: {
6846
6847
  display: "flex",
6847
6848
  flexDirection: "row",
@@ -6870,7 +6871,7 @@ const useStyles$M = makeStyles({
6870
6871
  });
6871
6872
  const PerformanceViewer = (props) => {
6872
6873
  const { scene, layoutObservable, returnToLiveObservable, performanceCollector, initialGraphSize } = props;
6873
- const classes = useStyles$M();
6874
+ const classes = useStyles$P();
6874
6875
  const [onVisibleRangeChangedObservable] = useState(() => new Observable());
6875
6876
  const onReturnToPlayheadClick = () => {
6876
6877
  returnToLiveObservable.notifyObservers();
@@ -7037,14 +7038,14 @@ const TextPropertyLine = (props) => {
7037
7038
  return (jsx(PropertyLine, { ...props, children: jsx(Body1, { title: title, children: value ?? "" }) }));
7038
7039
  };
7039
7040
 
7040
- const useStyles$L = makeStyles({
7041
+ const useStyles$O = makeStyles({
7041
7042
  pinnedStatsPane: {
7042
7043
  flex: "0 1 auto",
7043
7044
  paddingBottom: tokens.spacingHorizontalM,
7044
7045
  },
7045
7046
  });
7046
7047
  const StatsPane = (props) => {
7047
- const classes = useStyles$L();
7048
+ const classes = useStyles$O();
7048
7049
  const scene = props.context;
7049
7050
  const engine = scene.getEngine();
7050
7051
  const pollingObservable = usePollingObservable(250);
@@ -7207,7 +7208,7 @@ const ToolsServiceDefinition = {
7207
7208
  */
7208
7209
  const ReactContextServiceIdentity = Symbol("ReactContextService");
7209
7210
 
7210
- const useStyles$K = makeStyles({
7211
+ const useStyles$N = makeStyles({
7211
7212
  dropdown: {
7212
7213
  ...UniformWidthStyling,
7213
7214
  },
@@ -7219,7 +7220,7 @@ const useStyles$K = makeStyles({
7219
7220
  */
7220
7221
  const DropdownPropertyLine = forwardRef((props, ref) => {
7221
7222
  DropdownPropertyLine.displayName = "DropdownPropertyLine";
7222
- const classes = useStyles$K();
7223
+ const classes = useStyles$N();
7223
7224
  return (jsx(PropertyLine, { ...props, ref: ref, children: jsx(Dropdown, { ...props, className: classes.dropdown }) }));
7224
7225
  });
7225
7226
  /**
@@ -7377,7 +7378,7 @@ const SyncedSliderInput = (props) => {
7377
7378
  return (jsxs("div", { className: mergeClasses(classes.container, props.className), children: [infoLabel && jsx(InfoLabel, { ...infoLabel, htmlFor: "syncedSlider" }), jsxs("div", { id: "syncedSlider", className: classes.syncedSlider, children: [hasSlider && (jsx(Slider, { className: getSliderClassName(), value: value, onChange: handleSliderChange, min: props.min, max: props.max, step: props.step, disabled: props.disabled, onPointerDown: handleSliderPointerDown, onPointerUp: handleSliderPointerUp })), jsx(SpinButton, { ...passthroughProps, className: useCompactSizing ? classes.compactSpinButton : classes.spinButton, inputClassName: useCompactSizing ? classes.compactSpinButtonInput : classes.spinButtonInput, value: value, onChange: handleInputChange, step: props.step, disabled: props.disabled, disableDragButton: true })] })] }));
7378
7379
  };
7379
7380
 
7380
- const useStyles$J = makeStyles({
7381
+ const useStyles$M = makeStyles({
7381
7382
  uniformWidth: {
7382
7383
  ...UniformWidthStyling,
7383
7384
  },
@@ -7389,7 +7390,7 @@ const useStyles$J = makeStyles({
7389
7390
  */
7390
7391
  const SyncedSliderPropertyLine = forwardRef((props, ref) => {
7391
7392
  SyncedSliderPropertyLine.displayName = "SyncedSliderPropertyLine";
7392
- const classes = useStyles$J();
7393
+ const classes = useStyles$M();
7393
7394
  const { label, description, ...sliderProps } = props;
7394
7395
  return (jsx(PropertyLine, { ref: ref, ...props, children: jsx(SyncedSliderInput, { ...sliderProps, className: mergeClasses(classes.uniformWidth, props.className) }) }));
7395
7396
  });
@@ -7821,6 +7822,78 @@ const GizmoServiceDefinition = {
7821
7822
  },
7822
7823
  };
7823
7824
 
7825
+ const OnInspectorAssetNotFoundPromptRequestedObservable = new Observable(undefined, true);
7826
+ let InspectorAssetNotFoundPromptHandlerObserver = null;
7827
+ let MissingAssetPromptQueue = Promise.resolve(undefined);
7828
+ /**
7829
+ * Sets the Inspector-owned prompt handler used by Smart Assets when an asset is missing.
7830
+ * @param handler - The handler installed by the Inspector UI, or null to clear it.
7831
+ */
7832
+ function SetInspectorAssetNotFoundPromptHandler(handler) {
7833
+ InspectorAssetNotFoundPromptHandlerObserver?.remove();
7834
+ InspectorAssetNotFoundPromptHandlerObserver = null;
7835
+ if (!handler) {
7836
+ OnInspectorAssetNotFoundPromptRequestedObservable.notifyIfTriggered = true;
7837
+ return;
7838
+ }
7839
+ InspectorAssetNotFoundPromptHandlerObserver = OnInspectorAssetNotFoundPromptRequestedObservable.add((request) => {
7840
+ OnInspectorAssetNotFoundPromptRequestedObservable.notifyIfTriggered = false;
7841
+ OnInspectorAssetNotFoundPromptRequestedObservable.cleanLastNotifiedState();
7842
+ void (async () => {
7843
+ try {
7844
+ request.resolve(await handler(request.key, request.expectedUrl));
7845
+ }
7846
+ catch (error) {
7847
+ request.reject(error);
7848
+ }
7849
+ })();
7850
+ });
7851
+ OnInspectorAssetNotFoundPromptRequestedObservable.notifyIfTriggered = false;
7852
+ OnInspectorAssetNotFoundPromptRequestedObservable.cleanLastNotifiedState();
7853
+ }
7854
+ async function RunQueuedMissingAssetPromptAsync(key, expectedUrl) {
7855
+ const previousPrompt = MissingAssetPromptQueue;
7856
+ let releasePrompt = () => { };
7857
+ MissingAssetPromptQueue = new Promise((resolve) => {
7858
+ releasePrompt = resolve;
7859
+ });
7860
+ await previousPrompt;
7861
+ try {
7862
+ return await new Promise((resolve, reject) => {
7863
+ OnInspectorAssetNotFoundPromptRequestedObservable.notifyObservers({ key, expectedUrl, resolve, reject });
7864
+ });
7865
+ }
7866
+ finally {
7867
+ releasePrompt();
7868
+ }
7869
+ }
7870
+ /**
7871
+ * Default handler for missing assets. Delegates to the Inspector UI when it is open.
7872
+ * Pane focus is handled by the prompt host itself once the user picks a file.
7873
+ * @param key - The smart asset key that was not found.
7874
+ * @param expectedUrl - The URL that failed to load.
7875
+ * @returns A promise resolving to a new URL, File, or null to skip.
7876
+ */
7877
+ // eslint-disable-next-line @typescript-eslint/naming-convention
7878
+ async function inspectorAssetNotFoundHandler(key, expectedUrl) {
7879
+ return await RunQueuedMissingAssetPromptAsync(key, expectedUrl);
7880
+ }
7881
+ /**
7882
+ * Installs the Inspector `onAssetNotFound` handler while preserving any existing handler for restoration.
7883
+ * @param sam - The SmartAssetManager to install the handler on.
7884
+ * @returns A function that restores the previous handler if the Inspector handler is still installed.
7885
+ */
7886
+ // eslint-disable-next-line @typescript-eslint/naming-convention
7887
+ function installInspectorAssetNotFoundHandler(sam) {
7888
+ const previousHandler = sam.onAssetNotFound;
7889
+ sam.onAssetNotFound = inspectorAssetNotFoundHandler;
7890
+ return () => {
7891
+ if (sam.onAssetNotFound === inspectorAssetNotFoundHandler) {
7892
+ sam.onAssetNotFound = previousHandler;
7893
+ }
7894
+ };
7895
+ }
7896
+
7824
7897
  const InstalledExtensionsKey = "Extensions/InstalledExtensions";
7825
7898
  const ExtensionInstalledKeyPrefix = "Extensions/IsExtensionInstalled";
7826
7899
  function GetExtensionInstalledKey(name) {
@@ -8277,7 +8350,7 @@ function useExtensionManager() {
8277
8350
  return useContext(ExtensionManagerContext)?.extensionManager;
8278
8351
  }
8279
8352
 
8280
- const useStyles$I = makeStyles({
8353
+ const useStyles$L = makeStyles({
8281
8354
  themeButton: {
8282
8355
  margin: 0,
8283
8356
  },
@@ -8296,7 +8369,7 @@ const ThemeSelectorServiceDefinition = {
8296
8369
  teachingMoment: false,
8297
8370
  order: -300,
8298
8371
  component: () => {
8299
- const classes = useStyles$I();
8372
+ const classes = useStyles$L();
8300
8373
  const { isDarkMode, themeMode, setThemeMode } = useThemeMode();
8301
8374
  const onSelectedThemeChange = useCallback((e, data) => {
8302
8375
  setThemeMode(data.checkedItems.includes("System") ? "system" : data.checkedItems[0].toLocaleLowerCase());
@@ -8313,7 +8386,7 @@ const ThemeSelectorServiceDefinition = {
8313
8386
  },
8314
8387
  };
8315
8388
 
8316
- const useStyles$H = makeStyles({
8389
+ const useStyles$K = makeStyles({
8317
8390
  app: {
8318
8391
  colorScheme: "light dark",
8319
8392
  flexGrow: 1,
@@ -8358,7 +8431,7 @@ function MakeModularTool(options) {
8358
8431
  // This deferred resolves once the React effect cleanup (which disposes the ServiceContainer) is complete.
8359
8432
  const disposeDeferred = new Deferred();
8360
8433
  const modularToolRootComponent = () => {
8361
- const classes = useStyles$H();
8434
+ const classes = useStyles$K();
8362
8435
  const [extensionManagerContext, setExtensionManagerContext] = useState();
8363
8436
  const [requiredExtensions, setRequiredExtensions] = useState();
8364
8437
  const [requiredExtensionsDeferred, setRequiredExtensionsDeferred] = useState();
@@ -8445,7 +8518,7 @@ function MakeModularTool(options) {
8445
8518
  }
8446
8519
  // Register the extension list service (for browsing/installing extensions) if extension feeds are provided.
8447
8520
  if (extensionFeeds.length > 0) {
8448
- const { ExtensionListServiceDefinition } = await import('./extensionsListService-C33SU1_a.js');
8521
+ const { ExtensionListServiceDefinition } = await import('./extensionsListService-DTrjNf_v.js');
8449
8522
  serviceContainer.addService(ExtensionListServiceDefinition);
8450
8523
  }
8451
8524
  // Register all external services (that make up a unique tool).
@@ -8519,7 +8592,7 @@ function MakeModularTool(options) {
8519
8592
  else {
8520
8593
  // eslint-disable-next-line @typescript-eslint/naming-convention
8521
8594
  const Content = rootComponentService.rootComponent;
8522
- return (jsx(ReactContextsWrapper, { contexts: contexts, children: jsx(SettingsStoreContext.Provider, { value: settingsStore, children: jsx(ExtensionManagerContext.Provider, { value: extensionManagerContext, children: jsx(Theme, { className: classes.app, children: jsxs(ToastProvider, { imperativeRef: setToastHandle, children: [jsx(Dialog, { open: !!requiredExtensions, modalType: "alert", children: jsx(DialogSurface, { children: jsxs(DialogBody, { children: [jsx(DialogTitle, { children: "Required Extensions" }), jsxs(DialogContent, { children: ["Opening this URL requires the following extensions to be installed and enabled:", jsx("ul", { children: requiredExtensions?.map((name) => (jsx("li", { children: name }, name))) })] }), jsxs(DialogActions, { children: [jsx(Button$1, { appearance: "primary", onClick: onAcceptRequiredExtensions, children: "Install & Enable" }), jsx(Button$1, { appearance: "secondary", onClick: onRejectRequiredExtensions, children: "No Thanks" })] })] }) }) }), jsx(Dialog, { open: !!extensionInstallError, modalType: "alert", children: jsx(DialogSurface, { children: jsxs(DialogBody, { children: [jsx(DialogTitle, { children: jsxs("div", { className: classes.extensionErrorTitleDiv, children: ["Extension Install Error", jsx(ErrorCircleRegular, { className: classes.extensionErrorIcon })] }) }), jsx(DialogContent, { children: jsxs(List$1, { children: [jsx(ListItem, { children: jsx(Body1, { children: `Extension "${extensionInstallError?.extension.name}" failed to install and was removed.` }) }), jsx(ListItem, { children: jsx(Body1, { children: `${extensionInstallError?.error}` }) })] }) }), jsx(DialogActions, { children: jsx(Button$1, { appearance: "primary", onClick: onAcknowledgedExtensionInstallError, children: "Close" }) })] }) }) }), jsx(Suspense, { fallback: jsx(Spinner, { className: classes.spinner }), children: jsx(Content, {}) })] }) }) }) }) }));
8595
+ return (jsx(ReactContextsWrapper, { contexts: contexts, children: jsx(SettingsStoreContext.Provider, { value: settingsStore, children: jsx(ExtensionManagerContext.Provider, { value: extensionManagerContext, children: jsx(Theme, { className: classes.app, children: jsxs(ToastProvider, { imperativeRef: setToastHandle, children: [jsx(Dialog$1, { open: !!requiredExtensions, modalType: "alert", children: jsx(DialogSurface, { children: jsxs(DialogBody, { children: [jsx(DialogTitle, { children: "Required Extensions" }), jsxs(DialogContent, { children: ["Opening this URL requires the following extensions to be installed and enabled:", jsx("ul", { children: requiredExtensions?.map((name) => (jsx("li", { children: name }, name))) })] }), jsxs(DialogActions, { children: [jsx(Button$1, { appearance: "primary", onClick: onAcceptRequiredExtensions, children: "Install & Enable" }), jsx(Button$1, { appearance: "secondary", onClick: onRejectRequiredExtensions, children: "No Thanks" })] })] }) }) }), jsx(Dialog$1, { open: !!extensionInstallError, modalType: "alert", children: jsx(DialogSurface, { children: jsxs(DialogBody, { children: [jsx(DialogTitle, { children: jsxs("div", { className: classes.extensionErrorTitleDiv, children: ["Extension Install Error", jsx(ErrorCircleRegular, { className: classes.extensionErrorIcon })] }) }), jsx(DialogContent, { children: jsxs(List$1, { children: [jsx(ListItem, { children: jsx(Body1, { children: `Extension "${extensionInstallError?.extension.name}" failed to install and was removed.` }) }), jsx(ListItem, { children: jsx(Body1, { children: `${extensionInstallError?.error}` }) })] }) }), jsx(DialogActions, { children: jsx(Button$1, { appearance: "primary", onClick: onAcknowledgedExtensionInstallError, children: "Close" }) })] }) }) }), jsx(Suspense, { fallback: jsx(Spinner, { className: classes.spinner }), children: jsx(Content, {}) })] }) }) }) }) }));
8523
8596
  }
8524
8597
  };
8525
8598
  // Set the container element to be a flex container so that the tool can be displayed properly.
@@ -8566,14 +8639,14 @@ const DefaultInspectorExtensionFeed = new BuiltInsExtensionFeed("Inspector", [
8566
8639
  description: "Adds a new panel for easy creation of various Babylon assets. This is a WIP extension...expect changes!",
8567
8640
  keywords: ["creation", "tools"],
8568
8641
  ...BabylonWebResources,
8569
- getExtensionModuleAsync: async () => await import('./quickCreateToolsService-pBNUavZe.js'),
8642
+ getExtensionModuleAsync: async () => await import('./quickCreateToolsService-8d6rBO-A.js'),
8570
8643
  },
8571
8644
  {
8572
8645
  name: "Reflector",
8573
8646
  description: "Connects to the Reflector Bridge for real-time scene synchronization with the Babylon.js Sandbox.",
8574
8647
  keywords: ["reflector", "bridge", "sync", "sandbox", "tools"],
8575
8648
  ...BabylonWebResources,
8576
- getExtensionModuleAsync: async () => await import('./reflectorService-CAi2NPRy.js'),
8649
+ getExtensionModuleAsync: async () => await import('./reflectorService-B7LcD1Sn.js'),
8577
8650
  },
8578
8651
  ]);
8579
8652
 
@@ -9614,7 +9687,7 @@ const ColorSliders = ({ color, onSliderChange }) => (jsxs(Fragment, { children:
9614
9687
  const Color3PropertyLine = ColorPropertyLine;
9615
9688
  const Color4PropertyLine = ColorPropertyLine;
9616
9689
 
9617
- const useStyles$G = makeStyles({
9690
+ const useStyles$J = makeStyles({
9618
9691
  uniformWidth: {
9619
9692
  ...UniformWidthStyling,
9620
9693
  },
@@ -9626,7 +9699,7 @@ const useStyles$G = makeStyles({
9626
9699
  */
9627
9700
  const TextInputPropertyLine = (props) => {
9628
9701
  TextInputPropertyLine.displayName = "TextInputPropertyLine";
9629
- const classes = useStyles$G();
9702
+ const classes = useStyles$J();
9630
9703
  return (jsx(PropertyLine, { ...props, children: jsx(TextInput, { ...props, className: mergeClasses(classes.uniformWidth, props.className) }) }));
9631
9704
  };
9632
9705
  /**
@@ -9637,7 +9710,7 @@ const TextInputPropertyLine = (props) => {
9637
9710
  */
9638
9711
  const NumberInputPropertyLine = (props) => {
9639
9712
  NumberInputPropertyLine.displayName = "NumberInputPropertyLine";
9640
- const classes = useStyles$G();
9713
+ const classes = useStyles$J();
9641
9714
  return (jsx(PropertyLine, { ...props, children: jsx(SpinButton, { ...props, className: mergeClasses(classes.uniformWidth, props.className) }) }));
9642
9715
  };
9643
9716
 
@@ -9769,7 +9842,7 @@ const LegacyInspectableObjectPropertiesServiceDefinition = {
9769
9842
  };
9770
9843
 
9771
9844
  const DocUrl = "https://www.npmjs.com/package/@babylonjs/inspector#inspector-cli";
9772
- const useStyles$F = makeStyles({
9845
+ const useStyles$I = makeStyles({
9773
9846
  tooltipContent: {
9774
9847
  display: "flex",
9775
9848
  flexDirection: "column",
@@ -9787,7 +9860,7 @@ const CliConnectionStatusServiceDefinition = {
9787
9860
  teachingMoment: false,
9788
9861
  order: 0 /* DefaultToolbarItemOrder.CliStatus */,
9789
9862
  component: () => {
9790
- const classes = useStyles$F();
9863
+ const classes = useStyles$I();
9791
9864
  const isEnabled = useObservableState(() => cliConnectionStatus.isEnabled, cliConnectionStatus.onConnectionStatusChanged);
9792
9865
  const isConnected = useObservableState(() => cliConnectionStatus.isConnected, cliConnectionStatus.onConnectionStatusChanged);
9793
9866
  const { showToast } = useToast();
@@ -9848,7 +9921,7 @@ const BreakTangentIcon = createFluentIcon("BreakTangent", "20", '<g transform="s
9848
9921
  const UnifyTangentIcon = createFluentIcon("UnifyTangent", "20", '<g transform="scale(0.5)"><path d="M27.94,18.28a1.49,1.49,0,0,0-1.41,1h-5l-1.62-1.63-1.62,1.63h-5a1.5,1.5,0,1,0,0,1h5l1.62,1.62,1.62-1.62h5a1.5,1.5,0,1,0,1.41-2Z"/></g>');
9849
9922
  const StepTangentIcon = createFluentIcon("StepTangent", "20", '<g transform="scale(0.5)"><path d="M29,16.71a1.5,1.5,0,1,0-2,1.41v5.67H11v1H28V18.12A1.51,1.51,0,0,0,29,16.71Z"/></g>');
9850
9923
 
9851
- const useStyles$E = makeStyles({
9924
+ const useStyles$H = makeStyles({
9852
9925
  coordinatesModeButton: {
9853
9926
  margin: `0 0 0 ${tokens.spacingHorizontalXS}`,
9854
9927
  },
@@ -9864,7 +9937,7 @@ const useStyles$E = makeStyles({
9864
9937
  });
9865
9938
  const GizmoToolbar = (props) => {
9866
9939
  const { gizmoService, sceneContext } = props;
9867
- const classes = useStyles$E();
9940
+ const classes = useStyles$H();
9868
9941
  const gizmoMode = useObservableState(() => gizmoService.gizmoMode, gizmoService.onGizmoModeChanged);
9869
9942
  const coordinatesMode = useObservableState(() => gizmoService.coordinatesMode, gizmoService.onCoordinatesModeChanged);
9870
9943
  const cameraGizmo = useObservableState(() => gizmoService.gizmoCamera, gizmoService.onCameraGizmoChanged);
@@ -9993,7 +10066,7 @@ const HighlightServiceDefinition = {
9993
10066
  },
9994
10067
  };
9995
10068
 
9996
- const useStyles$D = makeStyles({
10069
+ const useStyles$G = makeStyles({
9997
10070
  badge: {
9998
10071
  margin: tokens.spacingHorizontalXXS,
9999
10072
  fontFamily: "monospace",
@@ -10010,7 +10083,7 @@ const MiniStatsServiceDefinition = {
10010
10083
  order: 300 /* DefaultToolbarItemOrder.FrameRate */,
10011
10084
  teachingMoment: false,
10012
10085
  component: () => {
10013
- const classes = useStyles$D();
10086
+ const classes = useStyles$G();
10014
10087
  const scene = useObservableState(useCallback(() => sceneContext.currentScene, [sceneContext.currentScene]), sceneContext.currentSceneObservable);
10015
10088
  const engine = scene?.getEngine();
10016
10089
  const pollingObservable = usePollingObservable(250);
@@ -10339,7 +10412,7 @@ function useCurveEditor() {
10339
10412
  return context;
10340
10413
  }
10341
10414
 
10342
- const useStyles$C = makeStyles({
10415
+ const useStyles$F = makeStyles({
10343
10416
  root: {
10344
10417
  display: "flex",
10345
10418
  flexDirection: "row",
@@ -10383,7 +10456,7 @@ const useStyles$C = makeStyles({
10383
10456
  * @returns The top bar component
10384
10457
  */
10385
10458
  const TopBar = () => {
10386
- const styles = useStyles$C();
10459
+ const styles = useStyles$F();
10387
10460
  const { state, observables } = useCurveEditor();
10388
10461
  const [keyFrameValue, setKeyFrameValue] = useState(null);
10389
10462
  const [keyValue, setKeyValue] = useState(null);
@@ -10476,7 +10549,7 @@ const ColorChannelColors = {
10476
10549
  */
10477
10550
  const DefaultCurveColor = "#ffffff";
10478
10551
 
10479
- const useStyles$B = makeStyles({
10552
+ const useStyles$E = makeStyles({
10480
10553
  root: {
10481
10554
  display: "flex",
10482
10555
  flexDirection: "column",
@@ -10518,7 +10591,7 @@ const LOOP_MODES$1 = [
10518
10591
  * @returns The edit animation panel component
10519
10592
  */
10520
10593
  const EditAnimationPanel = ({ animation, onClose }) => {
10521
- const styles = useStyles$B();
10594
+ const styles = useStyles$E();
10522
10595
  const { observables } = useCurveEditor();
10523
10596
  const [name, setName] = useState(animation.name);
10524
10597
  const [property, setProperty] = useState(animation.targetProperty);
@@ -10549,7 +10622,7 @@ const EditAnimationPanel = ({ animation, onClose }) => {
10549
10622
  return (jsxs("div", { className: styles.root, children: [jsxs("div", { className: styles.form, children: [jsxs("div", { className: styles.row, children: [jsx(Label, { children: "Display Name" }), jsx(Input, { value: name, onChange: (_, data) => setName(data.value), placeholder: "Animation name" })] }), jsxs("div", { className: styles.row, children: [jsx(Label, { children: "Property" }), jsx(Input, { value: property, onChange: (_, data) => setProperty(data.value), placeholder: "e.g., position, rotation, scaling" })] }), jsxs("div", { className: styles.row, children: [jsx(Label, { children: "Loop Mode" }), jsx(Dropdown$1, { value: getLoopModeLabel(loopMode), selectedOptions: [loopMode.toString()], onOptionSelect: (_, data) => setLoopMode(Number(data.optionValue)), positioning: "below", inlinePopup: true, children: LOOP_MODES$1.map((mode) => (jsx(Option, { value: mode.value.toString(), children: mode.label }, mode.value))) })] })] }), jsxs("div", { className: styles.buttons, children: [jsx(Button, { appearance: "primary", onClick: saveChanges, label: "Save", disabled: !isValid }), jsx(Button, { appearance: "subtle", onClick: onClose, label: "Cancel" })] })] }));
10550
10623
  };
10551
10624
 
10552
- const useStyles$A = makeStyles({
10625
+ const useStyles$D = makeStyles({
10553
10626
  root: {
10554
10627
  display: "flex",
10555
10628
  flexDirection: "column",
@@ -10615,7 +10688,7 @@ const useStyles$A = makeStyles({
10615
10688
  * @returns Animation entry component
10616
10689
  */
10617
10690
  const AnimationEntry = ({ animation }) => {
10618
- const styles = useStyles$A();
10691
+ const styles = useStyles$D();
10619
10692
  const { state, actions, observables } = useCurveEditor();
10620
10693
  const [isExpanded, setIsExpanded] = useState(false);
10621
10694
  const [isHovered, setIsHovered] = useState(false);
@@ -10694,7 +10767,7 @@ const AnimationEntry = ({ animation }) => {
10694
10767
  * @returns Animation sub-entry component
10695
10768
  */
10696
10769
  const AnimationSubEntry = ({ animation, subName, color }) => {
10697
- const styles = useStyles$A();
10770
+ const styles = useStyles$D();
10698
10771
  const { actions, observables } = useCurveEditor();
10699
10772
  const activeChannel = actions.getActiveChannel(animation);
10700
10773
  const isThisChannelActive = activeChannel === color;
@@ -10717,7 +10790,7 @@ const AnimationSubEntry = ({ animation, subName, color }) => {
10717
10790
  * @returns Animation list component
10718
10791
  */
10719
10792
  const AnimationList = () => {
10720
- const styles = useStyles$A();
10793
+ const styles = useStyles$D();
10721
10794
  const { state, observables } = useCurveEditor();
10722
10795
  // Re-render when animations are loaded or changed (e.g. animation deleted)
10723
10796
  // useCallback stabilizes the accessor to prevent infinite re-render loops
@@ -10730,7 +10803,7 @@ const AnimationList = () => {
10730
10803
  }) }));
10731
10804
  };
10732
10805
 
10733
- const useStyles$z = makeStyles({
10806
+ const useStyles$C = makeStyles({
10734
10807
  root: {
10735
10808
  display: "flex",
10736
10809
  flexDirection: "column",
@@ -10779,7 +10852,7 @@ const LoopModeOptions = LOOP_MODES.map((lm) => ({ label: lm, value: lm }));
10779
10852
  * @returns The add animation panel component
10780
10853
  */
10781
10854
  const AddAnimationPanel = ({ onClose }) => {
10782
- const styles = useStyles$z();
10855
+ const styles = useStyles$C();
10783
10856
  const { state, actions, observables } = useCurveEditor();
10784
10857
  const [name, setName] = useState("");
10785
10858
  const [mode, setMode] = useState("List");
@@ -10996,7 +11069,7 @@ const AddAnimationPanel = ({ onClose }) => {
10996
11069
  return (jsxs("div", { className: styles.root, children: [jsx("div", { className: styles.header, children: jsx("div", { className: styles.title, children: "Add Animation" }) }), jsxs("div", { className: styles.form, children: [jsxs("div", { className: styles.row, children: [jsx(Label, { children: "Display Name" }), jsx(TextInput, { value: name, onChange: setName })] }), jsx("div", { className: styles.row, children: jsx(StringDropdown, { value: mode, onChange: (val) => setMode(val), options: ModeOptions, disabled: properties.length === 0, infoLabel: { label: "Mode" } }) }), jsxs("div", { className: styles.row, children: [jsx(Label, { children: "Property" }), isCustomMode ? (jsx(TextInput, { value: customProperty, onChange: setCustomProperty })) : (jsx(StringDropdown, { value: selectedProperty, onChange: (val) => setSelectedProperty(val), options: properties.map((p) => ({ label: p, value: p })) }))] }), jsxs("div", { className: styles.row, children: [jsx(Label, { children: "Type" }), isCustomMode ? (jsx(StringDropdown, { value: animationType, onChange: (val) => setAnimationType(val), options: AnimationTypeOptions })) : (jsx("div", { className: styles.typeDisplay, children: inferredType }))] }), jsx("div", { className: styles.row, children: jsx(StringDropdown, { value: loopMode, onChange: (val) => setLoopMode(val), options: LoopModeOptions, infoLabel: { label: "Loop Mode" } }) })] }), jsxs("div", { className: styles.buttons, children: [jsx(Button, { appearance: "primary", onClick: createAnimation, disabled: !isValid, label: "Create" }), jsx(Button, { appearance: "subtle", onClick: onClose, label: "Cancel" })] })] }));
10997
11070
  };
10998
11071
 
10999
- const useStyles$y = makeStyles({
11072
+ const useStyles$B = makeStyles({
11000
11073
  root: {
11001
11074
  display: "flex",
11002
11075
  flexDirection: "column",
@@ -11039,7 +11112,7 @@ const useStyles$y = makeStyles({
11039
11112
  * @returns The load animation panel component
11040
11113
  */
11041
11114
  const LoadAnimationPanel = ({ onClose }) => {
11042
- const styles = useStyles$y();
11115
+ const styles = useStyles$B();
11043
11116
  const { state, actions, observables } = useCurveEditor();
11044
11117
  const [snippetIdInput, setSnippetIdInput] = useState("");
11045
11118
  const [loadedSnippetId, setLoadedSnippetId] = useState(null);
@@ -11178,7 +11251,7 @@ class StringTools {
11178
11251
  }
11179
11252
  }
11180
11253
 
11181
- const useStyles$x = makeStyles({
11254
+ const useStyles$A = makeStyles({
11182
11255
  root: {
11183
11256
  display: "flex",
11184
11257
  flexDirection: "column",
@@ -11223,7 +11296,7 @@ const useStyles$x = makeStyles({
11223
11296
  * @returns The save animation panel component
11224
11297
  */
11225
11298
  const SaveAnimationPanel = ({ onClose: _onClose }) => {
11226
- const styles = useStyles$x();
11299
+ const styles = useStyles$A();
11227
11300
  const { state } = useCurveEditor();
11228
11301
  const [selectedAnimations, setSelectedAnimations] = useState(() => {
11229
11302
  if (!state.animations) {
@@ -11296,7 +11369,7 @@ const SaveAnimationPanel = ({ onClose: _onClose }) => {
11296
11369
  }) }), jsxs("div", { className: styles.buttons, children: [jsx(Button, { appearance: "primary", onClick: saveToSnippetServer, disabled: selectedAnimations.length === 0 || isSaving, label: isSaving ? "Saving..." : "Save to Snippet Server" }), jsx(Button, { appearance: "secondary", onClick: saveToFile, disabled: selectedAnimations.length === 0, label: "Save to File" })] }), saveError && jsx("div", { className: styles.errorText, children: saveError }), snippetId && jsxs("div", { className: styles.snippetId, children: ["Saved! Snippet ID: ", snippetId] })] }));
11297
11370
  };
11298
11371
 
11299
- const useStyles$w = makeStyles({
11372
+ const useStyles$z = makeStyles({
11300
11373
  root: {
11301
11374
  display: "flex",
11302
11375
  flexDirection: "column",
@@ -11350,7 +11423,7 @@ const useStyles$w = makeStyles({
11350
11423
  * @returns The sidebar component
11351
11424
  */
11352
11425
  const SideBar = () => {
11353
- const styles = useStyles$w();
11426
+ const styles = useStyles$z();
11354
11427
  const { state, actions, observables } = useCurveEditor();
11355
11428
  const [openPopover, setOpenPopover] = useState(null);
11356
11429
  const [fps, setFps] = useState(60);
@@ -12338,7 +12411,7 @@ const KeyPointComponent = (props) => {
12338
12411
  } })] }))] }))] }));
12339
12412
  };
12340
12413
 
12341
- const useStyles$v = makeStyles({
12414
+ const useStyles$y = makeStyles({
12342
12415
  root: {
12343
12416
  position: "absolute",
12344
12417
  top: 0,
@@ -12522,7 +12595,7 @@ function ExtractValuesFromKeys(keys, curves) {
12522
12595
  * @returns The graph component
12523
12596
  */
12524
12597
  const Graph = ({ width, height }) => {
12525
- const styles = useStyles$v();
12598
+ const styles = useStyles$y();
12526
12599
  const { state, actions, observables } = useCurveEditor();
12527
12600
  const svgRef = useRef(null);
12528
12601
  const [scale, setScale] = useState(1);
@@ -12885,7 +12958,7 @@ const Graph = ({ width, height }) => {
12885
12958
  }), renderValueAxis()] })] }));
12886
12959
  };
12887
12960
 
12888
- const useStyles$u = makeStyles({
12961
+ const useStyles$x = makeStyles({
12889
12962
  root: {
12890
12963
  position: "absolute",
12891
12964
  top: 0,
@@ -12928,7 +13001,7 @@ const useStyles$u = makeStyles({
12928
13001
  * @returns The playhead component
12929
13002
  */
12930
13003
  const PlayHead = ({ width, height: _height }) => {
12931
- const styles = useStyles$u();
13004
+ const styles = useStyles$x();
12932
13005
  const { state, actions, observables } = useCurveEditor();
12933
13006
  const [isDragging, setIsDragging] = useState(false);
12934
13007
  // Use refs for all mutable values to avoid render cycles
@@ -13079,7 +13152,7 @@ const PlayHead = ({ width, height: _height }) => {
13079
13152
  return (jsxs("div", { className: styles.root, children: [jsx("div", { ref: lineRef, className: styles.line, onPointerDown: handlePointerDown, onPointerMove: handlePointerMove, onPointerUp: handlePointerUp }), jsx("div", { ref: handleRef, className: styles.handle, onPointerDown: handlePointerDown, onPointerMove: handlePointerMove, onPointerUp: handlePointerUp })] }));
13080
13153
  };
13081
13154
 
13082
- const useStyles$t = makeStyles({
13155
+ const useStyles$w = makeStyles({
13083
13156
  root: {
13084
13157
  display: "flex",
13085
13158
  flexDirection: "row",
@@ -13111,7 +13184,7 @@ const useStyles$t = makeStyles({
13111
13184
  * @returns The frame bar component
13112
13185
  */
13113
13186
  const FrameBar = ({ width }) => {
13114
- const styles = useStyles$t();
13187
+ const styles = useStyles$w();
13115
13188
  const { state, observables } = useCurveEditor();
13116
13189
  const containerRef = useRef(null);
13117
13190
  const [scale, setScale] = useState(1);
@@ -13169,7 +13242,7 @@ const FrameBar = ({ width }) => {
13169
13242
  return (jsx("div", { className: styles.root, ref: containerRef, children: renderTicks() }));
13170
13243
  };
13171
13244
 
13172
- const useStyles$s = makeStyles({
13245
+ const useStyles$v = makeStyles({
13173
13246
  root: {
13174
13247
  display: "flex",
13175
13248
  flexDirection: "column",
@@ -13210,7 +13283,7 @@ const OFFSET_X = 10;
13210
13283
  * @returns The range frame bar component
13211
13284
  */
13212
13285
  const RangeFrameBar = ({ width }) => {
13213
- const styles = useStyles$s();
13286
+ const styles = useStyles$v();
13214
13287
  const { state, actions, observables } = useCurveEditor();
13215
13288
  const svgRef = useRef(null);
13216
13289
  const [viewWidth, setViewWidth] = useState(width);
@@ -13321,7 +13394,7 @@ const RangeFrameBar = ({ width }) => {
13321
13394
  }), renderKeyframes, renderActiveFrame] }) }));
13322
13395
  };
13323
13396
 
13324
- const useStyles$r = makeStyles({
13397
+ const useStyles$u = makeStyles({
13325
13398
  root: {
13326
13399
  display: "flex",
13327
13400
  flexDirection: "column",
@@ -13353,7 +13426,7 @@ const useStyles$r = makeStyles({
13353
13426
  * @returns The canvas component
13354
13427
  */
13355
13428
  const Canvas = () => {
13356
- const styles = useStyles$r();
13429
+ const styles = useStyles$u();
13357
13430
  const { observables } = useCurveEditor();
13358
13431
  const containerRef = useRef(null);
13359
13432
  const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
@@ -13383,7 +13456,7 @@ const Canvas = () => {
13383
13456
  return (jsxs("div", { className: styles.root, ref: containerRef, children: [jsx("div", { className: styles.frameBar, children: jsx(FrameBar, { width: dimensions.width }) }), jsxs("div", { className: styles.canvasArea, children: [jsx(Graph, { width: dimensions.width, height: dimensions.height - 70 }), jsx(PlayHead, { width: dimensions.width, height: dimensions.height - 70 })] }), jsx("div", { className: styles.rangeFrameBar, children: jsx(RangeFrameBar, { width: dimensions.width }) })] }));
13384
13457
  };
13385
13458
 
13386
- const useStyles$q = makeStyles({
13459
+ const useStyles$t = makeStyles({
13387
13460
  root: {
13388
13461
  flex: 1,
13389
13462
  height: "25px",
@@ -13445,7 +13518,7 @@ const useStyles$q = makeStyles({
13445
13518
  * @returns The range selector component
13446
13519
  */
13447
13520
  const RangeSelector = () => {
13448
- const styles = useStyles$q();
13521
+ const styles = useStyles$t();
13449
13522
  const { state, actions, observables } = useCurveEditor();
13450
13523
  const containerRef = useRef(null);
13451
13524
  const scrollbarRef = useRef(null);
@@ -13590,7 +13663,7 @@ function GetKeyAtAnyFrameIndex(animations, frame) {
13590
13663
  }
13591
13664
  return false;
13592
13665
  }
13593
- const useStyles$p = makeStyles({
13666
+ const useStyles$s = makeStyles({
13594
13667
  root: {
13595
13668
  display: "flex",
13596
13669
  flexDirection: "row",
@@ -13647,7 +13720,7 @@ MediaControls.displayName = "MediaControls";
13647
13720
  * @returns The BottomBar component.
13648
13721
  */
13649
13722
  const BottomBar = () => {
13650
- const styles = useStyles$p();
13723
+ const styles = useStyles$s();
13651
13724
  const { state, actions, observables } = useCurveEditor();
13652
13725
  // Track display frame separately for smooth updates during playback
13653
13726
  const [displayFrame, setDisplayFrame] = useState(state.activeFrame);
@@ -13762,7 +13835,7 @@ const BottomBar = () => {
13762
13835
  return (jsxs("div", { className: styles.root, children: [jsx("div", { className: styles.mediaControls, children: jsx(MediaControls, { hasActiveAnimations: hasActiveAnimations, isPlaying: state.isPlaying, forwardAnimation: state.forwardAnimation, onPlayForward: handlePlayForward, onPlayBackward: handlePlayBackward, onStop: handleStop, onPrevKey: handlePrevKey, onNextKey: handleNextKey, onFirstFrame: handleFirstFrame, onLastFrame: handleLastFrame }) }), jsxs("div", { className: styles.frameDisplay, children: [jsx("div", { className: styles.frameLabel, children: "Frame:" }), jsx(SpinButton, { className: styles.spinButton, value: displayFrame, onChange: handleFrameChange, min: state.fromKey, max: state.toKey, disabled: !hasActiveAnimations })] }), jsx(RangeSelector, {}), jsxs("div", { className: styles.clipLengthSection, children: [jsx("div", { className: styles.frameLabel, children: "Clip Length:" }), jsx(SpinButton, { className: styles.spinButton, value: clipLength, onChange: handleClipLengthChange, min: 1, disabled: !hasActiveAnimations })] })] }));
13763
13836
  };
13764
13837
 
13765
- const useStyles$o = makeStyles({
13838
+ const useStyles$r = makeStyles({
13766
13839
  root: {
13767
13840
  display: "flex",
13768
13841
  flexDirection: "column",
@@ -13811,7 +13884,7 @@ const useStyles$o = makeStyles({
13811
13884
  * @returns The curve editor content
13812
13885
  */
13813
13886
  const CurveEditorContent = () => {
13814
- const styles = useStyles$o();
13887
+ const styles = useStyles$r();
13815
13888
  const { state, actions, observables } = useCurveEditor();
13816
13889
  const rootRef = useRef(null);
13817
13890
  const prepareRef = useRef(() => actions.prepare());
@@ -14653,7 +14726,7 @@ function useObservableArray(target, getItems, addFn, removeFn, changeFn) {
14653
14726
  }, [getItems]), useInterceptObservable("function", target, addFn), useInterceptObservable("function", target, removeFn), changeFn ? useInterceptObservable("function", target, changeFn) : undefined);
14654
14727
  }
14655
14728
 
14656
- const useStyles$n = makeStyles({
14729
+ const useStyles$q = makeStyles({
14657
14730
  lightsListDiv: {
14658
14731
  display: "flex",
14659
14732
  flexDirection: "column",
@@ -14663,7 +14736,7 @@ const ClusteredLightContainerSetupProperties = ({ context: container }) => {
14663
14736
  return (jsxs(Fragment, { children: [jsx(BooleanBadgePropertyLine, { label: "Is Supported", value: container.isSupported }), jsx(BoundProperty, { label: "Horizontal Tiles", component: NumberInputPropertyLine, target: container, propertyKey: "horizontalTiles", step: 1, min: 1, forceInt: true }), jsx(BoundProperty, { label: "Vertical Tiles", component: NumberInputPropertyLine, target: container, propertyKey: "verticalTiles", step: 1, min: 1, forceInt: true }), jsx(BoundProperty, { label: "Depth Slices", component: NumberInputPropertyLine, target: container, propertyKey: "depthSlices", step: 1, min: 1, forceInt: true }), jsx(BoundProperty, { label: "Max Range", component: NumberInputPropertyLine, target: container, propertyKey: "maxRange", min: 1 })] }));
14664
14737
  };
14665
14738
  const ClusteredLightContainerLightsProperties = ({ container, selectionService, }) => {
14666
- const classes = useStyles$n();
14739
+ const classes = useStyles$q();
14667
14740
  const lights = useObservableArray(container, useCallback(() => container.lights, [container]), "addLight", "removeLight");
14668
14741
  return (jsx(PropertyLine, { label: "Lights", expandedContent: jsx("div", { className: classes.lightsListDiv, children: lights.map((light) => (jsx(LinkToEntityPropertyLine, { label: light.getClassName(), entity: light, selectionService: selectionService }, light.uniqueId))) }), children: jsx(Badge, { appearance: "filled", children: lights.length }) }));
14669
14742
  };
@@ -14713,7 +14786,7 @@ const HemisphericLightSetupProperties = ({ context: hemisphericLight }) => {
14713
14786
  return (jsxs(Fragment, { children: [jsx(BoundProperty, { label: "Direction", component: Vector3PropertyLine, target: hemisphericLight, propertyKey: "direction" }), jsx(BoundProperty, { label: "Diffuse", component: Color3PropertyLine, target: hemisphericLight, propertyKey: "diffuse" }), jsx(BoundProperty, { label: "Ground", component: Color3PropertyLine, target: hemisphericLight, propertyKey: "groundColor" }), jsx(BoundProperty, { label: "Intensity", component: NumberInputPropertyLine, target: hemisphericLight, propertyKey: "intensity" })] }));
14714
14787
  };
14715
14788
 
14716
- const useStyles$m = makeStyles({
14789
+ const useStyles$p = makeStyles({
14717
14790
  root: {
14718
14791
  display: "grid",
14719
14792
  gridTemplateRows: "repeat(1fr)",
@@ -14743,7 +14816,7 @@ const useStyles$m = makeStyles({
14743
14816
  const ComboBox = forwardRef((props, ref) => {
14744
14817
  ComboBox.displayName = "ComboBox";
14745
14818
  const comboId = useId();
14746
- const styles = useStyles$m();
14819
+ const styles = useStyles$p();
14747
14820
  const { size } = useContext(ToolContext);
14748
14821
  // Find the label for the current value
14749
14822
  const getLabel = (value) => props.options.find((opt) => opt.value === value)?.label ?? "";
@@ -14766,7 +14839,7 @@ const ComboBox = forwardRef((props, ref) => {
14766
14839
  return (jsxs("div", { className: styles.root, children: [jsx("label", { id: comboId, children: props.label }), jsx(Combobox, { ref: ref, defaultOpen: props.defaultOpen, size: size, root: { className: styles.comboBox }, input: { className: styles.input }, listbox: { className: styles.listbox }, onOptionSelect: onOptionSelect, "aria-labelledby": comboId, placeholder: "Search..", onChange: (ev) => setQuery(ev.target.value), value: query, children: children })] }));
14767
14840
  });
14768
14841
 
14769
- const useStyles$l = makeStyles({
14842
+ const useStyles$o = makeStyles({
14770
14843
  linkDiv: {
14771
14844
  display: "flex",
14772
14845
  flexDirection: "row",
@@ -14791,7 +14864,7 @@ const useStyles$l = makeStyles({
14791
14864
  function EntitySelector(props) {
14792
14865
  const { value, onLink, getEntities, getName, filter, defaultValue } = props;
14793
14866
  const onChange = props.onChange;
14794
- const classes = useStyles$l();
14867
+ const classes = useStyles$o();
14795
14868
  const comboBoxRef = useRef(null);
14796
14869
  // Build options with uniqueId as key
14797
14870
  const options = useMemo(() => {
@@ -14945,7 +15018,7 @@ const TextureUpload = (props) => {
14945
15018
  return jsx(UploadButton, { onUpload: handleUpload, accept: accept, title: "Upload Texture", label: label });
14946
15019
  };
14947
15020
 
14948
- const useStyles$k = makeStyles({
15021
+ const useStyles$n = makeStyles({
14949
15022
  container: {
14950
15023
  display: "flex",
14951
15024
  flexDirection: "row",
@@ -14962,7 +15035,7 @@ const useStyles$k = makeStyles({
14962
15035
  const TextureSelector = (props) => {
14963
15036
  TextureSelector.displayName = "TextureSelector";
14964
15037
  const { scene, cubeOnly, value, onChange, onLink, defaultValue } = props;
14965
- const classes = useStyles$k();
15038
+ const classes = useStyles$n();
14966
15039
  const getTextures = useCallback(() => scene.textures, [scene.textures]);
14967
15040
  const getName = useCallback((texture) => texture.displayName || texture.name || `${texture.getClassName() || "Unnamed Texture"} (${texture.uniqueId})`, []);
14968
15041
  const filter = useCallback((texture) => !cubeOnly || texture.isCube, [cubeOnly]);
@@ -15330,6 +15403,22 @@ const MaterialTransparencyProperties = (props) => {
15330
15403
  const useAlphaTest = useProperty(material, "transparencyMode") === Material.MATERIAL_ALPHATEST;
15331
15404
  return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Alpha", target: material, propertyKey: "alpha", min: 0, max: 1, step: 0.01 }), jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Transparency Mode", docLink: "https://doc.babylonjs.com/features/featuresDeepDive/materials/advanced/transparent_rendering/#the-transparencymode-property", target: material, propertyKey: "transparencyMode", options: TransparencyModeOptions, nullable: true, defaultValue: Material.MATERIAL_OPAQUE }), jsx(Collapse, { visible: hasAlphaCutOff && useAlphaTest, children: jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "alphaCutOff", target: material, propertyKey: "alphaCutOff", min: 0, max: 1, step: 0.01 }) }), jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Alpha Mode", docLink: "https://doc.babylonjs.com/features/featuresDeepDive/materials/using/blendModes/#available-blend-modes", target: material, propertyKey: "alphaMode", options: AlphaModeOptions }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Separate Culling Pass", target: material, propertyKey: "separateCullingPass" })] }));
15332
15405
  };
15406
+ const TextureRepetitionModeOptions = [
15407
+ { label: "None", value: Constants.TEXTURE_REPETITION_NONE },
15408
+ { label: "Noise Blend (3 fetches)", value: Constants.TEXTURE_REPETITION_NOISE_BLEND },
15409
+ { label: "Hex Tiling (3 fetches)", value: Constants.TEXTURE_REPETITION_HEX_TILING },
15410
+ { label: "Tile Randomization (4 fetches)", value: Constants.TEXTURE_REPETITION_TILE_RANDOMIZATION },
15411
+ { label: "Voronoi Bombing (9 fetches)", value: Constants.TEXTURE_REPETITION_VORONOI_BOMBING },
15412
+ ];
15413
+ const MaterialTextureRepetitionProperties = (props) => {
15414
+ const { material } = props;
15415
+ const isHexTiling = useProperty(material, "textureRepetitionMode") === Constants.TEXTURE_REPETITION_HEX_TILING;
15416
+ const hexParams = material.textureRepetitionHexTilingParams;
15417
+ const setHexParam = useCallback((index, value) => {
15418
+ hexParams[index] = value;
15419
+ }, [hexParams]);
15420
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Mode", description: "Texture repetition breaking mode. Higher fetch counts produce better quality at higher cost.", target: material, propertyKey: "textureRepetitionMode", options: TextureRepetitionModeOptions }), jsxs(Collapse, { visible: isHexTiling, children: [jsx(SyncedSliderPropertyLine, { label: "Rotation Strength", description: "How much each hex tile is rotated (0 = no rotation, 1 = full rotation)", value: hexParams[0], onChange: (v) => setHexParam(0, v), min: 0, max: 1, step: 0.01 }), jsx(SyncedSliderPropertyLine, { label: "Fall-off Contrast", description: "How much luminance affects blending weight at tile borders", value: hexParams[1], onChange: (v) => setHexParam(1, v), min: 0, max: 1, step: 0.01 }), jsx(SyncedSliderPropertyLine, { label: "Exponent", description: "Controls the sharpness of weight falloff between tiles", value: hexParams[2], onChange: (v) => setHexParam(2, v), min: 1, max: 20, step: 0.5 }), jsx(SyncedSliderPropertyLine, { label: "Contrast", description: "Boost blending contrast (0.5 = neutral, higher = more contrast)", value: hexParams[3], onChange: (v) => setHexParam(3, v), min: 0.01, max: 0.99, step: 0.01 })] })] }));
15421
+ };
15333
15422
  const MaterialStencilProperties = (props) => {
15334
15423
  const { material } = props;
15335
15424
  const stencilEnabled = useProperty(material.stencil, "enabled");
@@ -15551,7 +15640,7 @@ async function EditNodeMaterial(material) {
15551
15640
  await material.edit({ nodeEditorConfig: { backgroundColor: material.getScene().clearColor } });
15552
15641
  }
15553
15642
 
15554
- const useStyles$j = makeStyles({
15643
+ const useStyles$m = makeStyles({
15555
15644
  subsection: {
15556
15645
  marginTop: tokens.spacingVerticalM,
15557
15646
  },
@@ -15625,7 +15714,7 @@ const GradientBlockPropertyLine = (props) => {
15625
15714
  };
15626
15715
  const NodeMaterialInputProperties = (props) => {
15627
15716
  const { material } = props;
15628
- const classes = useStyles$j();
15717
+ const classes = useStyles$m();
15629
15718
  const inputBlocks = useObservableState(useCallback(() => {
15630
15719
  const inspectorVisibleInputBlocks = material
15631
15720
  .getInputBlocks()
@@ -15877,7 +15966,7 @@ const OpenPBRMaterialBaseProperties = (props) => {
15877
15966
  */
15878
15967
  const OpenPBRMaterialSpecularProperties = (props) => {
15879
15968
  const { material } = props;
15880
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular Weight", target: material, propertyKey: "specularWeight", min: 0, max: 1, step: 0.01, description: "Controls how strong the reflections appear.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Specular Weight", target: material, propertyKey: "specularWeightTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Specular Color", target: material, propertyKey: "specularColor", isLinearMode: true, description: "Tints the color of reflections.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Specular Color", target: material, propertyKey: "specularColorTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular Roughness", target: material, propertyKey: "specularRoughness", min: 0, max: 1, step: 0.01, description: "Controls how sharp or blurry reflections are.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Specular Roughness", target: material, propertyKey: "specularRoughnessTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular Roughness Anisotropy", target: material, propertyKey: "specularRoughnessAnisotropy", min: 0, max: 1, step: 0.01, description: "Stretches reflections in one direction for brushed or streaked looks. Requires specular_roughness > 0.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/microfacetmodel" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Specular Roughness Anisotropy", target: material, propertyKey: "specularRoughnessAnisotropyTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular IOR", target: material, propertyKey: "specularIor", min: 1, max: 3, step: 0.01, description: "Index of refraction is a physical value controlling the reflective intensity and refraction. The parameter has no effect on metals.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate" })] }));
15969
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular Weight", target: material, propertyKey: "specularWeight", min: 0, max: 1, step: 0.01, description: "Controls how strong the reflections appear.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Specular Weight", target: material, propertyKey: "specularWeightTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Specular Color", target: material, propertyKey: "specularColor", isLinearMode: true, description: "Tints the color of reflections.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Specular Color", target: material, propertyKey: "specularColorTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular Roughness", target: material, propertyKey: "specularRoughness", min: 0, max: 1, step: 0.01, description: "Controls how sharp or blurry reflections are.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Specular Roughness", target: material, propertyKey: "specularRoughnessTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular Roughness Anisotropy", target: material, propertyKey: "specularRoughnessAnisotropy", min: 0, max: 1, step: 0.01, description: "Stretches reflections in one direction for brushed or streaked looks. Requires specular_roughness > 0.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/microfacetmodel" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Specular Roughness Anisotropy", target: material, propertyKey: "specularRoughnessAnisotropyTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular IOR", target: material, propertyKey: "specularIor", min: 1, max: 30, step: 0.01, description: "Index of refraction is a physical value controlling the reflective intensity and refraction. The parameter has no effect on metals.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate" })] }));
15881
15970
  };
15882
15971
  const OpenPBRMaterialTransmissionProperties = (props) => {
15883
15972
  const { material } = props;
@@ -16007,6 +16096,10 @@ const MaterialPropertiesServiceDefinition = {
16007
16096
  section: "Stencil",
16008
16097
  component: ({ context }) => jsx(MaterialStencilProperties, { material: context }),
16009
16098
  },
16099
+ {
16100
+ section: "Texture Repetition",
16101
+ component: ({ context }) => jsx(MaterialTextureRepetitionProperties, { material: context }),
16102
+ },
16010
16103
  ],
16011
16104
  });
16012
16105
  const standardMaterialContentRegistration = propertiesService.addSectionContent({
@@ -16347,7 +16440,7 @@ function SaveMetadata(entity, metadata) {
16347
16440
  entity.metadata = metadata;
16348
16441
  }
16349
16442
  }
16350
- const useStyles$i = makeStyles({
16443
+ const useStyles$l = makeStyles({
16351
16444
  mainDiv: {
16352
16445
  display: "flex",
16353
16446
  flexDirection: "column",
@@ -16367,7 +16460,7 @@ const useStyles$i = makeStyles({
16367
16460
  */
16368
16461
  const MetadataProperties = (props) => {
16369
16462
  const { entity } = props;
16370
- const classes = useStyles$i();
16463
+ const classes = useStyles$l();
16371
16464
  const { size } = useContext(ToolContext);
16372
16465
  const metadata = useProperty(entity, "metadata");
16373
16466
  const stringifiedMetadata = useMemo(() => StringifyMetadata(metadata, false) ?? "", [metadata]);
@@ -17302,7 +17395,7 @@ const ParticleSystemEmitterProperties = (props) => {
17302
17395
  } })) : (jsx(Property, { component: TextPropertyLine, propertyPath: "source", label: "Source", value: "No meshes in scene." })), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Use normals for direction", target: particleEmitterType, propertyKey: "useMeshNormalsForDirection" }), !useMeshNormalsForDirection && (jsxs(Fragment, { children: [jsx(BoundProperty, { component: Vector3PropertyLine, label: "Direction1", target: particleEmitterType, propertyKey: "direction1" }), jsx(BoundProperty, { component: Vector3PropertyLine, label: "Direction2", target: particleEmitterType, propertyKey: "direction2" })] }))] })), particleEmitterType instanceof BoxParticleEmitter && (jsxs(Fragment, { children: [jsx(BoundProperty, { component: Vector3PropertyLine, label: "Direction1", target: particleEmitterType, propertyKey: "direction1" }), jsx(BoundProperty, { component: Vector3PropertyLine, label: "Direction2", target: particleEmitterType, propertyKey: "direction2" }), jsx(BoundProperty, { component: Vector3PropertyLine, label: "Min emit box", target: particleEmitterType, propertyKey: "minEmitBox" }), jsx(BoundProperty, { component: Vector3PropertyLine, label: "Max emit box", target: particleEmitterType, propertyKey: "maxEmitBox" })] })), particleEmitterType instanceof ConeParticleEmitter && (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Radius range", target: particleEmitterType, propertyKey: "radiusRange", min: 0, max: 1, step: 0.01 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Height range", target: particleEmitterType, propertyKey: "heightRange", min: 0, max: 1, step: 0.01 }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Emit from spawn point only", target: particleEmitterType, propertyKey: "emitFromSpawnPointOnly" }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Direction randomizer", target: particleEmitterType, propertyKey: "directionRandomizer", min: 0, max: 1, step: 0.01 })] })), particleEmitterType instanceof SphereParticleEmitter && (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Radius", target: particleEmitterType, propertyKey: "radius", min: 0, step: 0.1 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Radius range", target: particleEmitterType, propertyKey: "radiusRange", min: 0, max: 1, step: 0.01 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Direction randomizer", target: particleEmitterType, propertyKey: "directionRandomizer", min: 0, max: 1, step: 0.01 })] })), particleEmitterType instanceof CylinderParticleEmitter && (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Radius", target: particleEmitterType, propertyKey: "radius", min: 0, step: 0.1 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Height", target: particleEmitterType, propertyKey: "height", min: 0, step: 0.1 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Radius range", target: particleEmitterType, propertyKey: "radiusRange", min: 0, max: 1, step: 0.01 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Direction randomizer", target: particleEmitterType, propertyKey: "directionRandomizer", min: 0, max: 1, step: 0.01 })] })), particleEmitterType instanceof HemisphericParticleEmitter && (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Radius", target: particleEmitterType, propertyKey: "radius", min: 0, step: 0.1 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Radius range", target: particleEmitterType, propertyKey: "radiusRange", min: 0, max: 1, step: 0.01 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Direction randomizer", target: particleEmitterType, propertyKey: "directionRandomizer", min: 0, max: 1, step: 0.01 })] })), particleEmitterType instanceof PointParticleEmitter && (jsxs(Fragment, { children: [jsx(BoundProperty, { component: Vector3PropertyLine, label: "Direction1", target: particleEmitterType, propertyKey: "direction1" }), jsx(BoundProperty, { component: Vector3PropertyLine, label: "Direction2", target: particleEmitterType, propertyKey: "direction2" })] }))] })), !scene && jsx(TextPropertyLine, { label: "Emitter", value: "No scene available." })] }));
17303
17396
  };
17304
17397
 
17305
- const useStyles$h = makeStyles({
17398
+ const useStyles$k = makeStyles({
17306
17399
  subsection: {
17307
17400
  marginTop: tokens.spacingVerticalM,
17308
17401
  },
@@ -17322,7 +17415,7 @@ const ParticleSystemSizeProperties = (props) => {
17322
17415
  const sizeGradientGetter = useCallback(() => system.getSizeGradients(), [system]);
17323
17416
  const sizeGradient = useObservableArray(system, sizeGradientGetter, "addSizeGradient", "removeSizeGradient", "forceRefreshGradients");
17324
17417
  const useSizeGradients = (sizeGradient?.length ?? 0) > 0;
17325
- const classes = useStyles$h();
17418
+ const classes = useStyles$k();
17326
17419
  return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Min size", target: system, propertyKey: "minSize", min: 0, step: 0.1 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Max size", target: system, propertyKey: "maxSize", min: 0, step: 0.1 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Min scale x", target: system, propertyKey: "minScaleX", min: 0, step: 0.1 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Max scale x", target: system, propertyKey: "maxScaleX", min: 0, step: 0.1 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Min scale y", target: system, propertyKey: "minScaleY", min: 0, step: 0.1 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Max scale y", target: system, propertyKey: "maxScaleY", min: 0, step: 0.1 }), isCpuParticleSystem && !useStartSizeGradients && (jsx(ButtonLine, { label: "Use Start Size gradients", onClick: () => {
17327
17420
  system.addStartSizeGradient(0, system.minSize, system.maxSize);
17328
17421
  system.forceRefreshGradients();
@@ -17358,7 +17451,7 @@ const ParticleSystemSizeProperties = (props) => {
17358
17451
  } })] }))] }));
17359
17452
  };
17360
17453
 
17361
- const useStyles$g = makeStyles({
17454
+ const useStyles$j = makeStyles({
17362
17455
  subsection: {
17363
17456
  marginTop: tokens.spacingVerticalM,
17364
17457
  },
@@ -17384,7 +17477,7 @@ const ParticleSystemEmissionProperties = (props) => {
17384
17477
  const useVelocityGradients = (velocityGradients?.length ?? 0) > 0;
17385
17478
  const useLimitVelocityGradients = (limitVelocityGradients?.length ?? 0) > 0;
17386
17479
  const useDragGradients = (dragGradients?.length ?? 0) > 0;
17387
- const classes = useStyles$g();
17480
+ const classes = useStyles$j();
17388
17481
  return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Emit rate", target: system, propertyKey: "emitRate", min: 0, step: 1 }), isCpuParticleSystem && !useEmitRateGradients && (jsx(ButtonLine, { label: "Use Emit rate gradients", onClick: () => {
17389
17482
  system.addEmitRateGradient(0, system.emitRate, system.emitRate);
17390
17483
  system.forceRefreshGradients();
@@ -17452,7 +17545,7 @@ const ParticleSystemEmissionProperties = (props) => {
17452
17545
  } })] }))] }));
17453
17546
  };
17454
17547
 
17455
- const useStyles$f = makeStyles({
17548
+ const useStyles$i = makeStyles({
17456
17549
  subsection: {
17457
17550
  marginTop: tokens.spacingVerticalM,
17458
17551
  },
@@ -17469,7 +17562,7 @@ const ParticleSystemLifetimeProperties = (props) => {
17469
17562
  const lifeTimeGradientsGetter = useCallback(() => system.getLifeTimeGradients(), [system]);
17470
17563
  const lifeTimeGradients = useObservableArray(system, lifeTimeGradientsGetter, "addLifeTimeGradient", "removeLifeTimeGradient", "forceRefreshGradients");
17471
17564
  const useLifeTimeGradients = (lifeTimeGradients?.length ?? 0) > 0;
17472
- const classes = useStyles$f();
17565
+ const classes = useStyles$i();
17473
17566
  return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Min lifetime", target: system, propertyKey: "minLifeTime", min: 0, step: 0.1 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Max lifetime", target: system, propertyKey: "maxLifeTime", min: 0, step: 0.1 }), isCpuParticleSystem && !useLifeTimeGradients && (jsx(ButtonLine, { label: "Use Lifetime gradients", onClick: () => {
17474
17567
  system.addLifeTimeGradient(0, system.minLifeTime, system.maxLifeTime);
17475
17568
  system.forceRefreshGradients();
@@ -17489,7 +17582,7 @@ const ParticleSystemLifetimeProperties = (props) => {
17489
17582
  } })] })), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Target stop duration", target: system, propertyKey: "targetStopDuration", min: 0, step: 0.1 })] }));
17490
17583
  };
17491
17584
 
17492
- const useStyles$e = makeStyles({
17585
+ const useStyles$h = makeStyles({
17493
17586
  subsection: {
17494
17587
  marginTop: tokens.spacingVerticalM,
17495
17588
  },
@@ -17516,7 +17609,7 @@ const ParticleSystemColorProperties = (props) => {
17516
17609
  const hasRampGradients = (rampGradients?.length ?? 0) > 0;
17517
17610
  const hasColorRemapGradients = (colorRemapGradients?.length ?? 0) > 0;
17518
17611
  const hasAlphaRemapGradients = (alphaRemapGradients?.length ?? 0) > 0;
17519
- const classes = useStyles$e();
17612
+ const classes = useStyles$h();
17520
17613
  return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: Color4PropertyLine, label: "Color 1", target: system, propertyKey: "color1" }), jsx(BoundProperty, { component: Color4PropertyLine, label: "Color 2", target: system, propertyKey: "color2" }), jsx(BoundProperty, { component: Color4PropertyLine, label: "Color dead", target: system, propertyKey: "colorDead" }), !hasColorGradients && (jsx(ButtonLine, { label: "Use Color gradients", onClick: () => {
17521
17614
  system.addColorGradient(0, system.color1, system.color1);
17522
17615
  system.addColorGradient(1, system.color2, system.color2);
@@ -17588,7 +17681,7 @@ const ParticleSystemColorProperties = (props) => {
17588
17681
  } })] }))] }))] }));
17589
17682
  };
17590
17683
 
17591
- const useStyles$d = makeStyles({
17684
+ const useStyles$g = makeStyles({
17592
17685
  subsection: {
17593
17686
  marginTop: tokens.spacingVerticalM,
17594
17687
  },
@@ -17603,7 +17696,7 @@ const ParticleSystemRotationProperties = (props) => {
17603
17696
  const angularSpeedGradientsGetter = useCallback(() => system.getAngularSpeedGradients(), [system]);
17604
17697
  const angularSpeedGradients = useObservableArray(system, angularSpeedGradientsGetter, "addAngularSpeedGradient", "removeAngularSpeedGradient", "forceRefreshGradients");
17605
17698
  const useAngularSpeedGradients = (angularSpeedGradients?.length ?? 0) > 0;
17606
- const classes = useStyles$d();
17699
+ const classes = useStyles$g();
17607
17700
  return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Min Angular speed", target: system, propertyKey: "minAngularSpeed", step: 0.01 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Max Angular speed", target: system, propertyKey: "maxAngularSpeed", step: 0.01 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Min initial rotation", target: system, propertyKey: "minInitialRotation", step: 0.01 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Max initial rotation", target: system, propertyKey: "maxInitialRotation", step: 0.01 }), !useAngularSpeedGradients && (jsx(ButtonLine, { label: "Use Angular speed gradients", onClick: () => {
17608
17701
  system.addAngularSpeedGradient(0, system.minAngularSpeed, system.maxAngularSpeed);
17609
17702
  system.forceRefreshGradients();
@@ -17709,7 +17802,7 @@ const AttractorComponent = (props) => {
17709
17802
  } }), !attractorData.isReadOnly && (jsx(ToggleButton, { title: "Add / remove position gizmo from particle attractor", checkedIcon: ArrowMoveFilled, value: isControlled(impostor), onChange: (control) => onControl(control ? impostor : undefined) }))] }));
17710
17803
  };
17711
17804
 
17712
- const useStyles$c = makeStyles({
17805
+ const useStyles$f = makeStyles({
17713
17806
  subsection: {
17714
17807
  marginTop: tokens.spacingVerticalM,
17715
17808
  },
@@ -17766,7 +17859,7 @@ const AttractorList = (props) => {
17766
17859
  gizmoManager.attachToMesh(attached);
17767
17860
  setControlledImpostor(attached);
17768
17861
  };
17769
- const classes = useStyles$c();
17862
+ const classes = useStyles$f();
17770
17863
  return (jsxs(Fragment, { children: [items.length > 0 && (jsxs(Fragment, { children: [jsx(Color3PropertyLine, { label: "Attractor Debug Color", value: impostorColor, onChange: setImpostorColor }), jsx(SyncedSliderPropertyLine, { label: "Attractor Debug Size", value: impostorScale, onChange: setImpostorScale, min: 0, max: 10, step: 0.1 }), jsx(Subtitle2, { className: classes.subsection, children: "Attractors list" })] })), jsx(List, { addButtonLabel: `Add New Attractor`, items: items, onDelete: attractorSource.removeAttractor
17771
17864
  ? (item, _index) => {
17772
17865
  // Only CPU attractors (Attractor instances) can be removed
@@ -17876,7 +17969,7 @@ const ParticleSystemAttractorProperties = (props) => {
17876
17969
  return (jsx(Fragment, { children: scene ? (jsx(AttractorList, { attractorSource: attractorSource, scene: scene })) : (jsx(MessageBar, { intent: "info", title: "No Scene Available", message: "Cannot display attractors without a scene" })) }));
17877
17970
  };
17878
17971
 
17879
- const useStyles$b = makeStyles({
17972
+ const useStyles$e = makeStyles({
17880
17973
  subsection: {
17881
17974
  marginTop: tokens.spacingVerticalM,
17882
17975
  },
@@ -17930,7 +18023,7 @@ const InputBlockPropertyLine = (props) => {
17930
18023
  */
17931
18024
  const ParticleSystemNodeEditorProperties = (props) => {
17932
18025
  const { particleSystem: system } = props;
17933
- const classes = useStyles$b();
18026
+ const classes = useStyles$e();
17934
18027
  const source = system.source;
17935
18028
  const inputBlocks = useObservableState(useCallback(() => {
17936
18029
  if (!source) {
@@ -18791,14 +18884,14 @@ const SkeletonPropertiesServiceDefinition = {
18791
18884
  },
18792
18885
  };
18793
18886
 
18794
- const useStyles$a = makeStyles({
18887
+ const useStyles$d = makeStyles({
18795
18888
  uniformWidth: {
18796
18889
  ...UniformWidthStyling,
18797
18890
  },
18798
18891
  });
18799
18892
  const SpinButtonPropertyLine = (props) => {
18800
18893
  SpinButtonPropertyLine.displayName = "SpinButtonPropertyLine";
18801
- const classes = useStyles$a();
18894
+ const classes = useStyles$d();
18802
18895
  return (jsx(PropertyLine, { ...props, children: jsx(SpinButton, { ...props, className: mergeClasses(classes.uniformWidth, props.className) }) }));
18803
18896
  };
18804
18897
 
@@ -19046,7 +19139,7 @@ function _TextureFormatHasNoAlpha(format) {
19046
19139
  }
19047
19140
  }
19048
19141
 
19049
- const useStyles$9 = makeStyles({
19142
+ const useStyles$c = makeStyles({
19050
19143
  root: {
19051
19144
  display: "flex",
19052
19145
  flexDirection: "column",
@@ -19098,7 +19191,7 @@ const TextureChannelStates = {
19098
19191
  */
19099
19192
  const TexturePreview = (props) => {
19100
19193
  const { texture, disableToolbar = false, maxWidth = "100%", maxHeight = "384px", offsetX = 0, offsetY = 0, width, height, imperativeRef } = props;
19101
- const classes = useStyles$9();
19194
+ const classes = useStyles$c();
19102
19195
  const canvasRef = useRef(null);
19103
19196
  const [channels, setChannels] = useState(TextureChannelStates.ALL);
19104
19197
  const [face, setFace] = useState(0);
@@ -20008,7 +20101,7 @@ class TextureCanvasManager {
20008
20101
  }
20009
20102
  }
20010
20103
 
20011
- const useStyles$8 = makeStyles({
20104
+ const useStyles$b = makeStyles({
20012
20105
  channelsBar: {
20013
20106
  display: "flex",
20014
20107
  flexDirection: "column",
@@ -20053,7 +20146,7 @@ const useStyles$8 = makeStyles({
20053
20146
  */
20054
20147
  const ChannelsBar = (props) => {
20055
20148
  const { channels, setChannels } = props;
20056
- const classes = useStyles$8();
20149
+ const classes = useStyles$b();
20057
20150
  const toggleVisibility = useCallback((index) => {
20058
20151
  const newChannels = [...channels];
20059
20152
  newChannels[index] = { ...newChannels[index], visible: !newChannels[index].visible };
@@ -20083,7 +20176,7 @@ const ChannelsBar = (props) => {
20083
20176
  }) }));
20084
20177
  };
20085
20178
 
20086
- const useStyles$7 = makeStyles({
20179
+ const useStyles$a = makeStyles({
20087
20180
  propertiesBar: {
20088
20181
  display: "flex",
20089
20182
  backgroundColor: tokens.colorNeutralBackground1,
@@ -20133,7 +20226,7 @@ const useStyles$7 = makeStyles({
20133
20226
  },
20134
20227
  });
20135
20228
  const PixelDataDisplay = ({ label, value }) => {
20136
- const classes = useStyles$7();
20229
+ const classes = useStyles$a();
20137
20230
  return (jsxs("span", { className: classes.pixelData, children: [jsxs(Label, { className: classes.pixelDataLabel, children: [label, ":"] }), jsx(Label, { className: classes.pixelDataValue, children: value !== undefined ? value : "-" })] }));
20138
20231
  };
20139
20232
  const CubeFaces = ["+X", "-X", "+Y", "-Y", "+Z", "-Z"];
@@ -20144,7 +20237,7 @@ const CubeFaces = ["+X", "-X", "+Y", "-Y", "+Z", "-Z"];
20144
20237
  */
20145
20238
  const PropertiesBar = (props) => {
20146
20239
  const { texture, size, saveTexture, pixelData, face, setFace, resetTexture, resizeTexture, uploadTexture, mipLevel, setMipLevel } = props;
20147
- const classes = useStyles$7();
20240
+ const classes = useStyles$a();
20148
20241
  const uploadInputRef = useRef(null);
20149
20242
  const [width, setWidth] = useState(size.width);
20150
20243
  const [height, setHeight] = useState(size.height);
@@ -20179,7 +20272,7 @@ const PropertiesBar = (props) => {
20179
20272
  return (jsxs("div", { className: classes.propertiesBar, children: [jsxs("div", { className: classes.section, children: [jsx(Label, { children: "W:" }), jsx(Input, { className: classes.dimensionInput, size: "small", type: "text", value: width.toString(), readOnly: texture.isCube, onChange: (_, data) => setWidth(getNewDimension(width, data.value)) }), jsx(Label, { children: "H:" }), jsx(Input, { className: classes.dimensionInput, size: "small", type: "text", value: height.toString(), readOnly: texture.isCube, onChange: (_, data) => setHeight(getNewDimension(height, data.value)) }), !texture.isCube && (jsx(Tooltip$1, { content: "Resize", relationship: "label", children: jsx(ToolbarButton, { icon: jsx(ResizeRegular, {}), onClick: handleResize }) }))] }), jsx(ToolbarDivider, {}), jsxs("div", { className: classes.section, children: [jsx(PixelDataDisplay, { label: "X", value: pixelData.x }), jsx(PixelDataDisplay, { label: "Y", value: pixelData.y })] }), jsx(ToolbarDivider, {}), jsxs("div", { className: classes.section, children: [jsx(PixelDataDisplay, { label: "R", value: pixelData.r }), jsx(PixelDataDisplay, { label: "G", value: pixelData.g }), jsx(PixelDataDisplay, { label: "B", value: pixelData.b }), jsx(PixelDataDisplay, { label: "A", value: pixelData.a })] }), texture.isCube && (jsxs(Fragment, { children: [jsx(ToolbarDivider, {}), jsx(Toolbar$1, { size: "small", children: CubeFaces.map((label, index) => (jsx(ToolbarButton, { className: classes.faceButton, appearance: face === index ? "primary" : "subtle", onClick: () => setFace(index), children: label }, label))) })] })), mipsEnabled && (jsxs(Fragment, { children: [jsx(ToolbarDivider, {}), jsxs("div", { className: classes.section, children: [jsx(Label, { children: "MIP:" }), jsx(Tooltip$1, { content: "Mip Preview Up", relationship: "label", children: jsx(ToolbarButton, { icon: jsx(ChevronUpRegular, {}), disabled: mipLevel <= 0, onClick: () => setMipLevel(mipLevel - 1) }) }), jsx(Label, { children: mipLevel }), jsx(Tooltip$1, { content: "Mip Preview Down", relationship: "label", children: jsx(ToolbarButton, { icon: jsx(ChevronDownRegular, {}), disabled: mipLevel >= maxLevels, onClick: () => setMipLevel(mipLevel + 1) }) })] })] })), jsx("div", { className: classes.spacer }), jsxs(Toolbar$1, { size: "small", children: [jsx(Tooltip$1, { content: "Reset", relationship: "label", children: jsx(ToolbarButton, { icon: jsx(ArrowResetRegular, {}), onClick: resetTexture }) }), jsx(Tooltip$1, { content: "Upload", relationship: "label", children: jsx(ToolbarButton, { icon: jsx(ArrowUploadRegular, {}), onClick: handleUploadClick }) }), jsx("input", { ref: uploadInputRef, className: classes.uploadInput, type: "file", accept: ".jpg, .png, .tga, .dds, .env, .exr", onChange: handleFileChange }), jsx(Tooltip$1, { content: "Save", relationship: "label", children: jsx(ToolbarButton, { icon: jsx(SaveRegular, {}), onClick: saveTexture }) })] })] }));
20180
20273
  };
20181
20274
 
20182
- const useStyles$6 = makeStyles({
20275
+ const useStyles$9 = makeStyles({
20183
20276
  statusBar: {
20184
20277
  display: "flex",
20185
20278
  backgroundColor: tokens.colorNeutralBackground1,
@@ -20207,14 +20300,14 @@ const useStyles$6 = makeStyles({
20207
20300
  */
20208
20301
  const StatusBar = (props) => {
20209
20302
  const { texture, mipLevel } = props;
20210
- const classes = useStyles$6();
20303
+ const classes = useStyles$9();
20211
20304
  const factor = Math.pow(2, mipLevel);
20212
20305
  const width = Math.ceil(texture.getSize().width / factor);
20213
20306
  const height = Math.ceil(texture.getSize().height / factor);
20214
20307
  return (jsxs("div", { className: classes.statusBar, children: [jsx("span", { className: classes.fileName, children: texture.name }), !texture.noMipmap && (jsxs("span", { className: classes.mipInfo, children: ["MIP Preview: ", mipLevel, " (", width, "\u00D7", height, ")"] }))] }));
20215
20308
  };
20216
20309
 
20217
- const useStyles$5 = makeStyles({
20310
+ const useStyles$8 = makeStyles({
20218
20311
  toolbar: {
20219
20312
  display: "flex",
20220
20313
  flexDirection: "column",
@@ -20251,7 +20344,7 @@ const useStyles$5 = makeStyles({
20251
20344
  */
20252
20345
  const ToolBar = (props) => {
20253
20346
  const { tools, changeTool, activeToolIndex, metadata, setMetadata, hasAlpha } = props;
20254
- const classes = useStyles$5();
20347
+ const classes = useStyles$8();
20255
20348
  const computeRGBAColor = useCallback(() => {
20256
20349
  const opacityInt = Math.floor(metadata.alpha * 255);
20257
20350
  const opacityHex = opacityInt.toString(16).padStart(2, "0");
@@ -20280,7 +20373,7 @@ const ToolBar = (props) => {
20280
20373
  }) })] }));
20281
20374
  };
20282
20375
 
20283
- const useStyles$4 = makeStyles({
20376
+ const useStyles$7 = makeStyles({
20284
20377
  textureEditor: {
20285
20378
  display: "flex",
20286
20379
  flexDirection: "column",
@@ -20344,7 +20437,7 @@ const PREVIEW_UPDATE_DELAY_MS = 160;
20344
20437
  */
20345
20438
  const TextureEditor = (props) => {
20346
20439
  const { texture, toolProviders = [], window: editorWindow, onUpdate } = props;
20347
- const classes = useStyles$4();
20440
+ const classes = useStyles$7();
20348
20441
  // Canvas refs
20349
20442
  const uiCanvasRef = useRef(null);
20350
20443
  const canvas2DRef = useRef(null);
@@ -20492,7 +20585,7 @@ const TextureEditor = (props) => {
20492
20585
  return (jsxs("div", { className: classes.textureEditor, children: [jsx(PropertiesBar, { texture: texture, saveTexture: saveTexture, pixelData: pixelData, face: face, setFace: setFace, resetTexture: resetTexture, resizeTexture: resizeTexture, uploadTexture: uploadTexture, mipLevel: mipLevel, setMipLevel: setMipLevel, size: canvasManagerRef.current?.size || size }), jsxs("div", { className: classes.mainContent, children: [jsxs("div", { className: classes.canvasContainer, style: { cursor }, children: [jsx("canvas", { ref: uiCanvasRef, className: classes.canvasUI, tabIndex: 1 }), jsx("canvas", { ref: canvas2DRef, className: classes.canvas2D }), jsx("canvas", { ref: canvas3DRef, className: classes.canvas3D })] }), CurrentToolSettings && (jsx("div", { className: classes.toolSettingsContainer, children: jsx(CurrentToolSettings, {}) })), !texture.isCube && (jsx("div", { className: classes.sidebarLeft, children: jsx(ToolBar, { tools: toolProviders, activeToolIndex: activeToolIndex, changeTool: changeTool, metadata: metadata, setMetadata: setMetadata, hasAlpha: hasAlpha }) })), jsx("div", { className: classes.sidebarRight, children: jsx(ChannelsBar, { channels: channels, setChannels: setChannels }) })] }), jsx(StatusBar, { texture: texture, mipLevel: mipLevel })] }));
20493
20586
  };
20494
20587
 
20495
- const useStyles$3 = makeStyles({
20588
+ const useStyles$6 = makeStyles({
20496
20589
  settingsContainer: {
20497
20590
  display: "flex",
20498
20591
  flexDirection: "column",
@@ -20512,7 +20605,7 @@ const Contrast = {
20512
20605
  name: "Contrast/Exposure",
20513
20606
  order: 500,
20514
20607
  icon: () => {
20515
- const classes = useStyles$3();
20608
+ const classes = useStyles$6();
20516
20609
  return jsx(CircleHalfFillRegular, { className: classes.icon });
20517
20610
  },
20518
20611
  is3D: true,
@@ -20577,7 +20670,7 @@ const Contrast = {
20577
20670
  setContrast(0);
20578
20671
  },
20579
20672
  settingsComponent: () => {
20580
- const classes = useStyles$3();
20673
+ const classes = useStyles$6();
20581
20674
  const [contrast, exposure] = useObservableState(useCallback(() => [_contrast, _exposure], []), stateChangedObservable);
20582
20675
  const handleContrastChange = (_, data) => {
20583
20676
  setContrast(data.value);
@@ -20678,7 +20771,7 @@ const Floodfill = {
20678
20771
  },
20679
20772
  };
20680
20773
 
20681
- const useStyles$2 = makeStyles({
20774
+ const useStyles$5 = makeStyles({
20682
20775
  settingsContainer: {
20683
20776
  display: "flex",
20684
20777
  flexDirection: "column",
@@ -20802,7 +20895,7 @@ const Paintbrush = {
20802
20895
  pointerObserver?.remove();
20803
20896
  },
20804
20897
  settingsComponent: () => {
20805
- const classes = useStyles$2();
20898
+ const classes = useStyles$5();
20806
20899
  const width = useObservableState(useCallback(() => _width, []), stateChangedObservable);
20807
20900
  const handleWidthChange = (_, data) => {
20808
20901
  setWidth(data.value);
@@ -22796,6 +22889,411 @@ const ExportServiceDefinition = {
22796
22889
  },
22797
22890
  };
22798
22891
 
22892
+ const useStyles$4 = makeStyles({
22893
+ statusMessage: {
22894
+ padding: `${tokens.spacingVerticalXXS} ${tokens.spacingHorizontalS}`,
22895
+ opacity: 0.7,
22896
+ },
22897
+ busyMessage: {
22898
+ display: "flex",
22899
+ alignItems: "center",
22900
+ gap: tokens.spacingHorizontalXS,
22901
+ padding: `${tokens.spacingVerticalXXS} ${tokens.spacingHorizontalS}`,
22902
+ },
22903
+ });
22904
+ /**
22905
+ * Save/load controls for a scene's Smart Asset map.
22906
+ * @param props - Component props.
22907
+ * @returns The Smart Asset map controls.
22908
+ */
22909
+ const SmartAssetProjectTools = (props) => {
22910
+ const { scene } = props;
22911
+ const [statusMessage, setStatusMessage] = useState("");
22912
+ const [busyMessage, setBusyMessage] = useState("");
22913
+ const styles = useStyles$4();
22914
+ const isBusy = busyMessage !== "";
22915
+ const onSaveAssetMap = useCallback(async () => {
22916
+ if (isBusy) {
22917
+ return;
22918
+ }
22919
+ setBusyMessage("Saving assets...");
22920
+ setStatusMessage("");
22921
+ try {
22922
+ const assetMap = SerializeSmartAssetManagerMap(scene);
22923
+ const jsonBlob = new Blob([JSON.stringify(assetMap, null, 2)], { type: "application/json" });
22924
+ Tools.Download(jsonBlob, "smart-assets.json");
22925
+ setStatusMessage(`Saved: ${Object.keys(assetMap.assets).length} assets`);
22926
+ }
22927
+ catch (err) {
22928
+ setStatusMessage(`Save error: ${err}`);
22929
+ }
22930
+ finally {
22931
+ setBusyMessage("");
22932
+ }
22933
+ }, [scene, isBusy]);
22934
+ const onLoadAssetMap = useCallback(async (files) => {
22935
+ const file = files[0];
22936
+ if (!file) {
22937
+ return;
22938
+ }
22939
+ if (isBusy) {
22940
+ return;
22941
+ }
22942
+ setBusyMessage("Loading assets...");
22943
+ setStatusMessage("");
22944
+ try {
22945
+ await Promise.all(Array.from(GetAllSmartAssets(scene).keys()).map(async (key) => await RemoveSmartAssetAsync(scene, key)));
22946
+ await LoadSmartAssetMapAsync(scene, file);
22947
+ setStatusMessage(`Loaded: ${GetAllSmartAssets(scene).size} assets`);
22948
+ }
22949
+ catch (err) {
22950
+ setStatusMessage(`Load error: ${err}`);
22951
+ }
22952
+ finally {
22953
+ setBusyMessage("");
22954
+ }
22955
+ }, [scene, isBusy]);
22956
+ return (jsxs(Fragment, { children: [jsx(ButtonLine, { label: "Save Smart Assets", icon: ArrowDownloadRegular, onClick: onSaveAssetMap, disabled: isBusy }), jsx(FileUploadLine, { label: "Load Smart Assets", accept: ".json", onClick: onLoadAssetMap, disabled: isBusy }), jsx(Collapse, { visible: isBusy, children: jsxs("div", { className: styles.busyMessage, children: [jsx(Spinner, { size: "extra-small" }), jsx(Caption1, { children: busyMessage })] }) }), jsx(Collapse, { visible: statusMessage !== "", children: jsx(Caption1, { className: styles.statusMessage, children: statusMessage }) })] }));
22957
+ };
22958
+
22959
+ const SmartAssetsPaneKey$1 = "Smart Assets";
22960
+ const SceneFileAccept = [".glb", ".gltf", ".babylon", ".obj"];
22961
+ const TextureFileAccept = Array.from(GetSmartAssetTextureExtensions());
22962
+ const AllAcceptString = [...SceneFileAccept, ...TextureFileAccept].join(",");
22963
+ // eslint-disable-next-line @typescript-eslint/naming-convention
22964
+ function _isTextureExtension(ext) {
22965
+ return GetSmartAssetTextureExtensions().has(ext);
22966
+ }
22967
+ /**
22968
+ * Inspector pane service that hosts the Smart Assets pane.
22969
+ */
22970
+ const SmartAssetsServiceDefinition = {
22971
+ friendlyName: "Smart Assets",
22972
+ consumes: [ShellServiceIdentity, SceneContextIdentity, SelectionServiceIdentity],
22973
+ factory: (shellService, sceneContext, selectionService) => {
22974
+ const paneRegistration = shellService.addSidePane({
22975
+ key: SmartAssetsPaneKey$1,
22976
+ title: SmartAssetsPaneKey$1,
22977
+ icon: CubeRegular,
22978
+ horizontalLocation: "right",
22979
+ verticalLocation: "top",
22980
+ order: 395,
22981
+ teachingMoment: false,
22982
+ content: () => {
22983
+ const scene = useObservableState(() => sceneContext.currentScene, sceneContext.currentSceneObservable);
22984
+ return scene ? jsx(SmartAssetsPane, { scene: scene, selectionService: selectionService }) : null;
22985
+ },
22986
+ });
22987
+ return {
22988
+ dispose: () => {
22989
+ paneRegistration.dispose();
22990
+ },
22991
+ };
22992
+ },
22993
+ };
22994
+ // ── Styles ──
22995
+ const useStyles$3 = makeStyles({
22996
+ assetRow: {
22997
+ display: "flex",
22998
+ alignItems: "center",
22999
+ gap: tokens.spacingHorizontalXS,
23000
+ padding: `${tokens.spacingVerticalXS} ${tokens.spacingHorizontalS}`,
23001
+ fontSize: tokens.fontSizeBase100,
23002
+ borderBottom: `1px solid ${tokens.colorNeutralStroke3}`,
23003
+ },
23004
+ assetKey: {
23005
+ fontWeight: tokens.fontWeightSemibold,
23006
+ minWidth: 0,
23007
+ flex: 1,
23008
+ overflow: "hidden",
23009
+ textOverflow: "ellipsis",
23010
+ whiteSpace: "nowrap",
23011
+ },
23012
+ assetUrl: {
23013
+ overflow: "hidden",
23014
+ textOverflow: "ellipsis",
23015
+ whiteSpace: "nowrap",
23016
+ minWidth: 0,
23017
+ flex: 1,
23018
+ opacity: 0.7,
23019
+ },
23020
+ assetActions: {
23021
+ display: "flex",
23022
+ gap: tokens.spacingHorizontalXXS,
23023
+ flexShrink: 0,
23024
+ },
23025
+ emptyMessage: {
23026
+ padding: `${tokens.spacingVerticalS} ${tokens.spacingHorizontalS}`,
23027
+ fontSize: tokens.fontSizeBase100,
23028
+ opacity: 0.5,
23029
+ fontStyle: "italic",
23030
+ },
23031
+ statusMessage: {
23032
+ padding: `${tokens.spacingVerticalXXS} ${tokens.spacingHorizontalS}`,
23033
+ fontSize: tokens.fontSizeBase100,
23034
+ opacity: 0.7,
23035
+ },
23036
+ hiddenInput: {
23037
+ display: "none",
23038
+ },
23039
+ });
23040
+ // ── Smart Assets Pane ──
23041
+ const SmartAssetsPane = (props) => {
23042
+ const { scene, selectionService } = props;
23043
+ return (jsxs(Accordion, { uniqueId: "SmartAssets", enablePinnedItems: true, enableHiddenItems: true, enableSearchItems: true, children: [jsx(AccordionSection, { title: "Assets", children: jsx(SmartAssetList, { scene: scene, selectionService: selectionService }) }), jsx(AccordionSection, { title: "Asset Map", children: jsx(SmartAssetProjectTools, { scene: scene }) })] }));
23044
+ };
23045
+ // ── Smart Asset List ──
23046
+ const SmartAssetList = (props) => {
23047
+ const { scene, selectionService } = props;
23048
+ const styles = useStyles$3();
23049
+ const fileInputRef = useRef(null);
23050
+ const [status, setStatus] = useState("");
23051
+ // Force the icon-only row action buttons to render in compact ("small") size
23052
+ // even when the inspector's global ToolContext is "medium". They live in a
23053
+ // tight row and the medium-size buttons crowd the layout.
23054
+ const toolContext = useContext(ToolContext);
23055
+ const compactToolContext = useMemo(() => ({ ...toolContext, size: "small" }), [toolContext]);
23056
+ // Subscribe reactively to changes — re-renders the asset list whenever
23057
+ // RegisterSmartAsset / Load / Remove / Reload fire onChangedObservable.
23058
+ const sam = GetSmartAssetManager(scene);
23059
+ const assets = useObservableState(useCallback(() => Array.from(GetAllSmartAssets(scene), ([key, url]) => ({ key, url })), [scene]), sam.onChangedObservable);
23060
+ const onAddAsset = useCallback(() => {
23061
+ fileInputRef.current?.click();
23062
+ }, []);
23063
+ const onFileSelected = useCallback(async (e) => {
23064
+ const files = e.target.files;
23065
+ if (!files || files.length === 0) {
23066
+ return;
23067
+ }
23068
+ for (let i = 0; i < files.length; i++) {
23069
+ const file = files[i];
23070
+ const key = file.name.replace(/\.[^/.]+$/, "");
23071
+ const ext = _getExtension(file.name).toLowerCase();
23072
+ const isTexture = _isTextureExtension(ext);
23073
+ const blobUrl = URL.createObjectURL(file);
23074
+ const options = { ...(ext ? { extension: ext } : {}), ...(isTexture ? { type: "texture" } : {}) };
23075
+ try {
23076
+ if (isTexture) {
23077
+ // eslint-disable-next-line no-await-in-loop
23078
+ await LoadSmartAssetTextureAsync(scene, key, blobUrl, options);
23079
+ }
23080
+ else {
23081
+ // Temporarily set onAssetNotFound to return the File so SAM's
23082
+ // retry path can use the extension hint and track loaded objects.
23083
+ const samForOverride = GetSmartAssetManager(scene);
23084
+ const savedHandler = samForOverride.onAssetNotFound;
23085
+ samForOverride.onAssetNotFound = async () => file;
23086
+ try {
23087
+ // eslint-disable-next-line no-await-in-loop
23088
+ await LoadSmartAssetAsync(scene, key, blobUrl, options);
23089
+ }
23090
+ finally {
23091
+ samForOverride.onAssetNotFound = savedHandler;
23092
+ }
23093
+ }
23094
+ setStatus(`Added: ${key}`);
23095
+ }
23096
+ catch {
23097
+ setStatus(`Failed to load: ${key}`);
23098
+ }
23099
+ }
23100
+ if (fileInputRef.current) {
23101
+ fileInputRef.current.value = "";
23102
+ }
23103
+ }, [scene]);
23104
+ const onRemoveAsset = useCallback(async (key) => {
23105
+ await RemoveSmartAssetAsync(scene, key);
23106
+ setStatus(`Removed: ${key}`);
23107
+ }, [scene]);
23108
+ const onReloadAsset = useCallback(async (key) => {
23109
+ await ReloadSmartAssetAsync(scene, key);
23110
+ setStatus(`Reloaded: ${key}`);
23111
+ }, [scene]);
23112
+ const onSwapAsset = useCallback((key) => {
23113
+ const doSwapAsync = async (file, fileHandle) => {
23114
+ const sam = GetSmartAssetManager(scene);
23115
+ const blobUrl = URL.createObjectURL(file);
23116
+ const ext = _getExtension(file.name).toLowerCase();
23117
+ const isTexture = _isTextureExtension(ext);
23118
+ if (isTexture) {
23119
+ // Find the old texture tracked by this key
23120
+ let oldTex;
23121
+ for (const tex of scene.textures) {
23122
+ if (FindSmartAssetKeyForObject(scene, tex) === key) {
23123
+ oldTex = tex;
23124
+ break;
23125
+ }
23126
+ }
23127
+ // Load the new texture via SAM so it stays tracked by key.
23128
+ // Pass reloadSource so Reload can re-read the file from disk.
23129
+ const newTex = await LoadSmartAssetTextureAsync(scene, key, blobUrl, {
23130
+ ...(ext ? { extension: ext } : {}),
23131
+ type: "texture",
23132
+ reloadSource: fileHandle ? async () => await fileHandle.getFile() : undefined,
23133
+ });
23134
+ // Replace references on all materials that used the old texture.
23135
+ // Only Standard/PBR-style slot names are rewritten here; NodeMaterial,
23136
+ // ShaderMaterial, BackgroundMaterial, and other custom materials with
23137
+ // non-standard texture slots will need to be updated manually.
23138
+ if (oldTex) {
23139
+ const texSlots = ["albedoTexture", "bumpTexture", "metallicTexture", "emissiveTexture", "ambientTexture", "reflectivityTexture", "opacityTexture"];
23140
+ for (const mat of scene.materials) {
23141
+ for (const slot of texSlots) {
23142
+ if (mat[slot] === oldTex) {
23143
+ mat[slot] = newTex;
23144
+ }
23145
+ }
23146
+ }
23147
+ oldTex.dispose();
23148
+ }
23149
+ }
23150
+ else {
23151
+ // Scene file swap (GLB, glTF, etc.)
23152
+ await UnloadSmartAssetAsync(scene, key);
23153
+ // Use onAssetNotFound to provide the File so SAM tracks loaded objects.
23154
+ const savedHandler = sam.onAssetNotFound;
23155
+ sam.onAssetNotFound = async () => file;
23156
+ try {
23157
+ // Pass reloadSource so Reload can re-read the file from disk.
23158
+ await LoadSmartAssetAsync(scene, key, blobUrl, {
23159
+ ...(ext ? { extension: ext } : {}),
23160
+ reloadSource: fileHandle ? async () => await fileHandle.getFile() : undefined,
23161
+ });
23162
+ }
23163
+ finally {
23164
+ sam.onAssetNotFound = savedHandler;
23165
+ }
23166
+ }
23167
+ // Notify after everything is loaded and tracked so the UI re-renders
23168
+ // with the correct provEntity links
23169
+ sam.onChangedObservable.notifyObservers();
23170
+ setStatus(`Swapped: ${key}`);
23171
+ };
23172
+ // Prefer showOpenFilePicker (File System Access API) so we get a
23173
+ // FileSystemFileHandle that lets Reload re-read fresh contents from disk.
23174
+ const windowWithPicker = window;
23175
+ if (typeof windowWithPicker.showOpenFilePicker === "function") {
23176
+ const pickerAsync = async () => {
23177
+ let handle;
23178
+ try {
23179
+ [handle] = await windowWithPicker.showOpenFilePicker({
23180
+ types: [
23181
+ {
23182
+ description: "Assets",
23183
+ accept: {
23184
+ // eslint-disable-next-line @typescript-eslint/naming-convention
23185
+ "model/*": SceneFileAccept,
23186
+ // eslint-disable-next-line @typescript-eslint/naming-convention
23187
+ "image/*": TextureFileAccept,
23188
+ },
23189
+ },
23190
+ ],
23191
+ });
23192
+ }
23193
+ catch (err) {
23194
+ // User cancelled the picker (AbortError) — silently ignore.
23195
+ if (err?.name !== "AbortError") {
23196
+ setStatus(`Swap cancelled: ${err}`);
23197
+ }
23198
+ return;
23199
+ }
23200
+ try {
23201
+ const file = await handle.getFile();
23202
+ await doSwapAsync(file, handle);
23203
+ }
23204
+ catch (err) {
23205
+ setStatus(`Failed to swap ${key}: ${err}`);
23206
+ }
23207
+ };
23208
+ void pickerAsync();
23209
+ }
23210
+ else {
23211
+ // Fallback for browsers without File System Access API
23212
+ const input = document.createElement("input");
23213
+ input.type = "file";
23214
+ input.accept = AllAcceptString;
23215
+ input.onchange = async () => {
23216
+ const file = input.files?.[0];
23217
+ if (!file) {
23218
+ return;
23219
+ }
23220
+ try {
23221
+ await doSwapAsync(file);
23222
+ }
23223
+ catch (err) {
23224
+ setStatus(`Failed to swap ${key}: ${err}`);
23225
+ }
23226
+ };
23227
+ input.click();
23228
+ }
23229
+ }, [scene]);
23230
+ return (jsxs(Fragment, { children: [assets.length === 0 && jsx(Caption1, { className: styles.emptyMessage, children: "No smart assets registered. Add assets to begin." }), assets.map((a) => {
23231
+ // Find the first mesh produced by this key for click-to-select
23232
+ const provEntity = _findFirstEntityForKey(a.key, scene);
23233
+ return (jsxs("div", { className: styles.assetRow, children: [jsx(CubeRegular, { fontSize: 14 }), provEntity ? (jsx(Caption1, { className: styles.assetKey, title: a.key, children: jsx(Link, { value: a.key, size: "small", onLink: () => (selectionService.selectedEntity = provEntity) }) })) : (jsx(Caption1, { className: styles.assetKey, title: a.key, children: a.key })), jsx(Caption1, { className: styles.assetUrl, title: a.url, children: _shortenUrl(a.url) }), jsx(ToolContext.Provider, { value: compactToolContext, children: jsxs("div", { className: styles.assetActions, children: [jsx(Button, { appearance: "subtle", icon: LinkRegular, title: `Swap URL for ${a.key}`, onClick: () => onSwapAsset(a.key) }), jsx(Button, { appearance: "subtle", icon: ArrowSyncRegular, title: `Reload ${a.key}`, onClick: async () => await onReloadAsset(a.key) }), jsx(Button, { appearance: "subtle", icon: DeleteRegular, title: `Remove ${a.key}`, onClick: async () => await onRemoveAsset(a.key) })] }) })] }, a.key));
23234
+ }), jsx(ButtonLine, { label: "Add Asset", icon: AddRegular, onClick: onAddAsset }), jsx("input", { ref: fileInputRef, type: "file", accept: AllAcceptString, multiple: true, className: styles.hiddenInput, onChange: onFileSelected }), status && jsx(Caption1, { className: styles.statusMessage, children: status })] }));
23235
+ };
23236
+ // ── Utilities ──
23237
+ /**
23238
+ * Finds the first scene entity produced by a smart asset key, for click-to-select.
23239
+ * Prefers non-root meshes, then materials, then textures.
23240
+ * @param key - The smart asset key.
23241
+ * @param scene - The scene to search.
23242
+ * @returns The first matching entity, or null if not found.
23243
+ */
23244
+ // eslint-disable-next-line @typescript-eslint/naming-convention
23245
+ function _findFirstEntityForKey(key, scene) {
23246
+ for (const mesh of scene.meshes) {
23247
+ if (mesh.name !== "__root__" && FindSmartAssetKeyForObject(scene, mesh) === key) {
23248
+ return mesh;
23249
+ }
23250
+ }
23251
+ for (const mat of scene.materials) {
23252
+ if (FindSmartAssetKeyForObject(scene, mat) === key) {
23253
+ return mat;
23254
+ }
23255
+ }
23256
+ for (const tex of scene.textures) {
23257
+ if (FindSmartAssetKeyForObject(scene, tex) === key) {
23258
+ return tex;
23259
+ }
23260
+ }
23261
+ return null;
23262
+ }
23263
+ /**
23264
+ * Shortens a URL for display, collapsing blob/data URLs and long paths.
23265
+ * @param url - The URL to shorten.
23266
+ * @returns A shortened display string.
23267
+ */
23268
+ // eslint-disable-next-line @typescript-eslint/naming-convention
23269
+ function _shortenUrl(url) {
23270
+ if (url.startsWith("blob:")) {
23271
+ return "(local file — blob)";
23272
+ }
23273
+ if (url.startsWith("data:")) {
23274
+ const mimeEnd = url.indexOf(";");
23275
+ const mime = mimeEnd > 5 ? url.substring(5, mimeEnd) : "binary";
23276
+ return `(embedded ${mime})`;
23277
+ }
23278
+ const parts = url.split("/");
23279
+ return parts.length > 3 ? "…/" + parts.slice(-2).join("/") : url;
23280
+ }
23281
+ /**
23282
+ * Returns the file extension from a filename or URL, including the leading dot.
23283
+ * @param url - The URL or filename to extract the extension from.
23284
+ * @returns The extension string (e.g. ".glb"), or empty string if none found.
23285
+ */
23286
+ // eslint-disable-next-line @typescript-eslint/naming-convention
23287
+ function _getExtension(url) {
23288
+ const clean = url.split("?")[0].split("#")[0];
23289
+ const lastDot = clean.lastIndexOf(".");
23290
+ const lastSlash = Math.max(clean.lastIndexOf("/"), clean.lastIndexOf("\\"));
23291
+ if (lastDot > lastSlash && lastDot >= 0) {
23292
+ return clean.substring(lastDot);
23293
+ }
23294
+ return "";
23295
+ }
23296
+
22799
23297
  const AnimationGroupLoadingModes = [
22800
23298
  { label: "Clean", value: 0 /* SceneLoaderAnimationGroupLoadingMode.Clean */ },
22801
23299
  { label: "Stop", value: 1 /* SceneLoaderAnimationGroupLoadingMode.Stop */ },
@@ -23279,6 +23777,143 @@ const UserFeedbackServiceDefinition = {
23279
23777
  },
23280
23778
  };
23281
23779
 
23780
+ /**
23781
+ * A shared dialog component wrapping Fluent UI Dialog with Babylon conventions.
23782
+ *
23783
+ * @example
23784
+ * ```tsx
23785
+ * <Dialog
23786
+ * open={isOpen}
23787
+ * title="Confirm Action"
23788
+ * onDismiss={() => setIsOpen(false)}
23789
+ * actions={[
23790
+ * { label: "Cancel", onClick: () => setIsOpen(false) },
23791
+ * { label: "Confirm", onClick: handleConfirm, appearance: "primary" },
23792
+ * ]}
23793
+ * >
23794
+ * <Text>Are you sure you want to proceed?</Text>
23795
+ * </Dialog>
23796
+ * ```
23797
+ *
23798
+ * @param props - The dialog props.
23799
+ * @returns The dialog element.
23800
+ */
23801
+ const Dialog = (props) => {
23802
+ const { open, title, children, actions, onDismiss } = props;
23803
+ return (jsx(Dialog$1, { open: open, modalType: "modal", onOpenChange: (_, data) => {
23804
+ if (!data.open && onDismiss) {
23805
+ onDismiss();
23806
+ }
23807
+ }, children: jsx(DialogSurface, { children: jsxs(DialogBody, { children: [jsx(DialogTitle, { action: onDismiss ? (jsx(DialogTrigger, { action: "close", children: jsx(Button, { appearance: "subtle", "aria-label": "close", icon: DismissRegular }) })) : undefined, children: title }), jsx(DialogContent, { children: children }), actions && actions.length > 0 && (jsx(DialogActions, { children: actions.map((action, index) => (jsx(Button, { appearance: action.appearance ?? "secondary", onClick: action.onClick, label: action.label }, index))) }))] }) }) }));
23808
+ };
23809
+
23810
+ const SmartAssetsPaneKey = "Smart Assets";
23811
+ const SceneFileExtensions = [".glb", ".gltf", ".babylon", ".obj"];
23812
+ const PromptAcceptString = [...SceneFileExtensions, ...Array.from(GetSmartAssetTextureExtensions())].join(",");
23813
+ const useStyles$2 = makeStyles({
23814
+ hiddenInput: {
23815
+ display: "none",
23816
+ },
23817
+ body: {
23818
+ display: "flex",
23819
+ flexDirection: "column",
23820
+ gap: tokens.spacingVerticalS,
23821
+ },
23822
+ keyRow: {
23823
+ display: "flex",
23824
+ gap: tokens.spacingHorizontalXS,
23825
+ },
23826
+ keyLabel: {
23827
+ fontWeight: tokens.fontWeightSemibold,
23828
+ },
23829
+ url: {
23830
+ fontFamily: tokens.fontFamilyMonospace,
23831
+ wordBreak: "break-all",
23832
+ opacity: 0.8,
23833
+ },
23834
+ });
23835
+ const SmartAssetMissingPromptHost = (props) => {
23836
+ const { onAssetSelected } = props;
23837
+ const inputRef = useRef(null);
23838
+ const pendingMissingAssetRef = useRef(null);
23839
+ const [pendingMissingAsset, setPendingMissingAsset] = useState(null);
23840
+ const classes = useStyles$2();
23841
+ const resolvePendingMissingAsset = useCallback((value) => {
23842
+ pendingMissingAssetRef.current?.resolve(value);
23843
+ pendingMissingAssetRef.current = null;
23844
+ setPendingMissingAsset(null);
23845
+ }, []);
23846
+ useEffect(() => {
23847
+ SetInspectorAssetNotFoundPromptHandler(async (keyName, expectedUrl) => await new Promise((resolve) => {
23848
+ const request = { keyName, expectedUrl, resolve };
23849
+ pendingMissingAssetRef.current = request;
23850
+ setPendingMissingAsset(request);
23851
+ }));
23852
+ return () => {
23853
+ SetInspectorAssetNotFoundPromptHandler(null);
23854
+ pendingMissingAssetRef.current?.resolve(null);
23855
+ pendingMissingAssetRef.current = null;
23856
+ };
23857
+ }, []);
23858
+ const onSkip = useCallback(() => {
23859
+ resolvePendingMissingAsset(null);
23860
+ }, [resolvePendingMissingAsset]);
23861
+ const onLocate = useCallback(() => {
23862
+ inputRef.current?.click();
23863
+ }, []);
23864
+ const onFileSelected = useCallback((event) => {
23865
+ const file = event.target.files?.[0] ?? null;
23866
+ resolvePendingMissingAsset(file);
23867
+ event.target.value = "";
23868
+ if (file) {
23869
+ onAssetSelected();
23870
+ }
23871
+ }, [resolvePendingMissingAsset, onAssetSelected]);
23872
+ if (!pendingMissingAsset) {
23873
+ return null;
23874
+ }
23875
+ const shortUrl = pendingMissingAsset.expectedUrl.length > 60 ? "..." + pendingMissingAsset.expectedUrl.slice(-50) : pendingMissingAsset.expectedUrl;
23876
+ return (jsx(Dialog, { open: true, title: "Asset not found", onDismiss: onSkip, actions: [
23877
+ { label: "Skip", onClick: onSkip },
23878
+ { label: "Locate File...", appearance: "primary", onClick: onLocate },
23879
+ ], children: jsxs("div", { className: classes.body, children: [jsxs("div", { className: classes.keyRow, children: [jsx(Body1, { className: classes.keyLabel, children: "Key:" }), jsx(Body1, { children: pendingMissingAsset.keyName })] }), jsx(Caption1, { className: classes.url, title: pendingMissingAsset.expectedUrl, children: shortUrl }), jsx(Body1, { children: "Locate the file or click Skip to continue without it." }), jsx("input", { ref: inputRef, type: "file", accept: PromptAcceptString, className: classes.hiddenInput, onChange: onFileSelected })] }) }));
23880
+ };
23881
+ /**
23882
+ * Registers the Inspector Smart Asset missing-file prompt host.
23883
+ */
23884
+ const SmartAssetPromptServiceDefinition = {
23885
+ friendlyName: "Smart Asset Prompt Service",
23886
+ consumes: [ShellServiceIdentity, SceneContextIdentity],
23887
+ factory: (shellService, sceneContext) => {
23888
+ const focusSmartAssetsPane = () => {
23889
+ shellService.sidePanes.find((pane) => pane.key === SmartAssetsPaneKey)?.select();
23890
+ };
23891
+ const registration = shellService.addCentralContent({
23892
+ key: "Smart Asset Missing Prompt",
23893
+ component: () => jsx(SmartAssetMissingPromptHost, { onAssetSelected: focusSmartAssetsPane }),
23894
+ order: 1000,
23895
+ });
23896
+ let restoreHandler = null;
23897
+ const installForCurrentScene = () => {
23898
+ restoreHandler?.();
23899
+ restoreHandler = null;
23900
+ const currentScene = sceneContext.currentScene;
23901
+ if (currentScene) {
23902
+ restoreHandler = installInspectorAssetNotFoundHandler(GetSmartAssetManager(currentScene));
23903
+ }
23904
+ };
23905
+ installForCurrentScene();
23906
+ const sceneObserver = sceneContext.currentSceneObservable.add(installForCurrentScene);
23907
+ return {
23908
+ dispose: () => {
23909
+ registration.dispose();
23910
+ sceneObserver.remove();
23911
+ restoreHandler?.();
23912
+ },
23913
+ };
23914
+ },
23915
+ };
23916
+
23282
23917
  // TODO: The key should probably be the Canvas, because we only want to show one inspector instance per canvas.
23283
23918
  // If it is called for a different scene that is rendering to the same canvas, then we should probably
23284
23919
  // switch the inspector instance to that scene (once this is supported).
@@ -23461,7 +24096,7 @@ function ShowInspector(scene, options = {}) {
23461
24096
  // Stats pane tab and related services.
23462
24097
  StatsServiceDefinition,
23463
24098
  // Tools pane tab and related services.
23464
- ToolsServiceDefinition, ExportServiceDefinition, GLTFAnimationImportServiceDefinition, GLTFLoaderOptionsServiceDefinition, GLTFValidationServiceDefinition, CaptureToolsDefinition,
24099
+ ToolsServiceDefinition, ExportServiceDefinition, SmartAssetPromptServiceDefinition, SmartAssetsServiceDefinition, GLTFAnimationImportServiceDefinition, GLTFLoaderOptionsServiceDefinition, GLTFValidationServiceDefinition, CaptureToolsDefinition,
23465
24100
  // Settings pane tab and related services.
23466
24101
  SettingsServiceDefinition, InspectorSettingsServiceDefinition, WatcherSettingsServiceDefinition, ShellSettingsServiceDefinition,
23467
24102
  // Adds a button to refresh all properties manually (when watcher is in "manual" mode).
@@ -24322,5 +24957,5 @@ const TextAreaPropertyLine = (props) => {
24322
24957
  // Attach Inspector v2 to Scene.debugLayer as a side effect for back compat.
24323
24958
  AttachDebugLayer();
24324
24959
 
24325
- export { GetPropertyDescriptor as $, Accordion as A, Button as B, CheckboxPropertyLine as C, Color4PropertyLine as D, ColorPickerPopup as E, ColorStepGradientComponent as F, ComboBox as G, ComboBoxPropertyLine as H, ConstructorFactory as I, ConvertOptions as J, DebugServiceIdentity as K, Link as L, MessageBar as M, NumberInputPropertyLine as N, DetachDebugLayer as O, Popover as P, DraggableLine as Q, Dropdown as R, ShellServiceIdentity as S, TextInputPropertyLine as T, EntitySelector as U, Vector3PropertyLine as V, ErrorBoundary as W, ExtensibleAccordion as X, FactorGradientComponent as Y, FactorGradientList as Z, FileUploadLine as _, useToast as a, ThemeServiceIdentity as a$, GizmoServiceIdentity as a0, HexPropertyLine as a1, InfoLabel as a2, InputHexField as a3, InputHsvField as a4, Inspector as a5, InterceptFunction as a6, InterceptProperty as a7, IsPropertyReadonly as a8, LineContainer as a9, SearchBox as aA, SelectionServiceDefinition as aB, SettingsServiceIdentity as aC, SettingsStore as aD, SettingsStoreIdentity as aE, ShowInspector as aF, SidePaneContainer as aG, SkeletonSelector as aH, Slider as aI, SpinButton as aJ, StartInspectable as aK, StatsServiceIdentity as aL, StringDropdown as aM, StringDropdownPropertyLine as aN, StringifiedPropertyLine as aO, Switch as aP, SwitchPropertyLine as aQ, SyncedSliderInput as aR, SyncedSliderPropertyLine as aS, TeachingMoment as aT, TextAreaPropertyLine as aU, TextInput as aV, TextPropertyLine as aW, Textarea as aX, TextureSelector as aY, TextureUpload as aZ, Theme as a_, LinkPropertyLine as aa, LinkToEntityPropertyLine as ab, List as ac, MakeDialogTeachingMoment as ad, MakeLazyComponent as ae, MakeModularBridge as af, MakeModularTool as ag, MakePopoverTeachingMoment as ah, MakePropertyHook as ai, MakeTeachingMoment as aj, MaterialSelector as ak, NodeSelector as al, NumberDropdown as am, NumberDropdownPropertyLine as an, ObservableCollection as ao, Pane as ap, PlaceholderPropertyLine as aq, PositionedPopover as ar, PropertiesServiceIdentity as as, Property as at, PropertyContext as au, PropertyLine as av, QuaternionPropertyLine as aw, RotationVectorPropertyLine as ax, SceneExplorerServiceIdentity as ay, SearchBar as az, useInterceptObservable as b, ToastProvider as b0, ToggleButton as b1, Tooltip as b2, UploadButton as b3, Vector2PropertyLine as b4, Vector4PropertyLine as b5, WatcherServiceIdentity as b6, useAngleConverters as b7, useAsyncResource as b8, useColor3Property as b9, useColor4Property as ba, useEventListener as bb, useEventfulState as bc, useKeyListener as bd, useKeyState as be, useObservableCollection as bf, useOrderedObservableCollection as bg, usePollingObservable as bh, usePropertyChangedNotifier as bi, useQuaternionProperty as bj, useResource as bk, useTheme as bl, useThemeMode as bm, useVector3Property as bn, LinkToEntity as c, SpinButtonPropertyLine as d, useProperty as e, SceneContextIdentity as f, SelectionServiceIdentity as g, useObservableState as h, AccordionSection as i, ButtonLine as j, ToolsServiceIdentity as k, AccordionSectionItem as l, AttachDebugLayer as m, BooleanBadgePropertyLine as n, BoundProperty as o, BridgeCommandRegistryIdentity as p, BuiltInsExtensionFeed as q, Checkbox as r, ChildWindow as s, Collapse as t, useExtensionManager as u, Color3GradientComponent as v, Color3GradientList as w, Color3PropertyLine as x, Color4GradientComponent as y, Color4GradientList as z };
24326
- //# sourceMappingURL=index-D0JXVQQf.js.map
24960
+ export { GetPropertyDescriptor as $, Accordion as A, Button as B, CheckboxPropertyLine as C, Color4PropertyLine as D, ColorPickerPopup as E, ColorStepGradientComponent as F, ComboBox as G, ComboBoxPropertyLine as H, ConstructorFactory as I, ConvertOptions as J, DebugServiceIdentity as K, Link as L, MessageBar as M, NumberInputPropertyLine as N, DetachDebugLayer as O, Popover as P, DraggableLine as Q, Dropdown as R, ShellServiceIdentity as S, TextInputPropertyLine as T, EntitySelector as U, Vector3PropertyLine as V, ErrorBoundary as W, ExtensibleAccordion as X, FactorGradientComponent as Y, FactorGradientList as Z, FileUploadLine as _, useToast as a, ThemeServiceIdentity as a$, GizmoServiceIdentity as a0, HexPropertyLine as a1, InfoLabel as a2, InputHexField as a3, InputHsvField as a4, Inspector as a5, InterceptFunction as a6, InterceptProperty as a7, IsPropertyReadonly as a8, LineContainer as a9, SearchBox as aA, SelectionServiceDefinition as aB, SettingsServiceIdentity as aC, SettingsStore as aD, SettingsStoreIdentity as aE, ShowInspector as aF, SidePaneContainer as aG, SkeletonSelector as aH, Slider as aI, SpinButton as aJ, StartInspectable as aK, StatsServiceIdentity as aL, StringDropdown as aM, StringDropdownPropertyLine as aN, StringifiedPropertyLine as aO, Switch as aP, SwitchPropertyLine as aQ, SyncedSliderInput as aR, SyncedSliderPropertyLine as aS, TeachingMoment as aT, TextAreaPropertyLine as aU, TextInput as aV, TextPropertyLine as aW, Textarea as aX, TextureSelector as aY, TextureUpload as aZ, Theme as a_, LinkPropertyLine as aa, LinkToEntityPropertyLine as ab, List as ac, MakeDialogTeachingMoment as ad, MakeLazyComponent as ae, MakeModularBridge as af, MakeModularTool as ag, MakePopoverTeachingMoment as ah, MakePropertyHook as ai, MakeTeachingMoment as aj, MaterialSelector as ak, NodeSelector as al, NumberDropdown as am, NumberDropdownPropertyLine as an, ObservableCollection as ao, Pane as ap, PlaceholderPropertyLine as aq, PositionedPopover as ar, PropertiesServiceIdentity as as, Property as at, PropertyContext as au, PropertyLine as av, QuaternionPropertyLine as aw, RotationVectorPropertyLine as ax, SceneExplorerServiceIdentity as ay, SearchBar as az, useInterceptObservable as b, ToastProvider as b0, ToggleButton as b1, Tooltip as b2, UploadButton as b3, Vector2PropertyLine as b4, Vector4PropertyLine as b5, WatcherServiceIdentity as b6, inspectorAssetNotFoundHandler as b7, useAngleConverters as b8, useAsyncResource as b9, useColor3Property as ba, useColor4Property as bb, useEventListener as bc, useEventfulState as bd, useKeyListener as be, useKeyState as bf, useObservableCollection as bg, useOrderedObservableCollection as bh, usePollingObservable as bi, usePropertyChangedNotifier as bj, useQuaternionProperty as bk, useResource as bl, useTheme as bm, useThemeMode as bn, useVector3Property as bo, LinkToEntity as c, SpinButtonPropertyLine as d, useProperty as e, SceneContextIdentity as f, SelectionServiceIdentity as g, useObservableState as h, AccordionSection as i, ButtonLine as j, ToolsServiceIdentity as k, AccordionSectionItem as l, AttachDebugLayer as m, BooleanBadgePropertyLine as n, BoundProperty as o, BridgeCommandRegistryIdentity as p, BuiltInsExtensionFeed as q, Checkbox as r, ChildWindow as s, Collapse as t, useExtensionManager as u, Color3GradientComponent as v, Color3GradientList as w, Color3PropertyLine as x, Color4GradientComponent as y, Color4GradientList as z };
24961
+ //# sourceMappingURL=index-PYblOaAV.js.map