@babylonjs/inspector 8.51.2 → 8.52.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/{extensionsListService-Dj1XtRzF.js → extensionsListService-Duej2zkq.js} +12 -11
- package/lib/extensionsListService-Duej2zkq.js.map +1 -0
- package/lib/{index-DrQrky1o.js → index-BvYg0Psk.js} +1347 -801
- package/lib/index-BvYg0Psk.js.map +1 -0
- package/lib/index.d.ts +68 -15
- package/lib/index.js +3 -3
- package/lib/{quickCreateToolsService-BuEys230.js → quickCreateToolsService-DHfs_EZ6.js} +4 -4
- package/lib/{quickCreateToolsService-BuEys230.js.map → quickCreateToolsService-DHfs_EZ6.js.map} +1 -1
- package/lib/{reflectorService-Dwsb8ijf.js → reflectorService-DEPuGTAZ.js} +4 -4
- package/lib/{reflectorService-Dwsb8ijf.js.map → reflectorService-DEPuGTAZ.js.map} +1 -1
- package/package.json +1 -1
- package/lib/extensionsListService-Dj1XtRzF.js.map +0 -1
- package/lib/index-DrQrky1o.js.map +0 -1
|
@@ -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, cloneElement, useImperativeHandle, createElement, Suspense, memo, Fragment as Fragment$1, lazy } from 'react';
|
|
3
|
-
import { tokens, makeStyles, Tooltip as Tooltip$1, Button as Button$1, Spinner, Link as Link$1, Caption1, Body1, useFluent, Accordion as Accordion$1, AccordionHeader, Subtitle2Stronger, AccordionPanel, Divider, MessageBar as MessageBar$1, MessageBarBody, AccordionItem, SearchBox as SearchBox$1, Portal, ToggleButton as ToggleButton$1, InfoLabel as InfoLabel$1, mergeClasses, Body1Strong, useId, useToastController, Toast, ToastTitle, FluentProvider, Toaster, Checkbox as Checkbox$1, createLightTheme, createDarkTheme, TeachingPopover, TeachingPopoverSurface, TeachingPopoverHeader, TeachingPopoverBody, Switch as Switch$1,
|
|
4
|
-
import { ErrorCircleRegular, EyeFilled, EyeOffRegular, CheckmarkFilled, EditRegular, FilterRegular, PinFilled, PinRegular, ArrowCircleUpRegular, ChevronCircleRight16Regular, ChevronCircleRight20Regular, ChevronCircleDown16Regular, ChevronCircleDown20Regular, Copy16Regular, CopyRegular, PanelLeftExpandRegular, PanelRightExpandRegular, PanelLeftContractRegular, PanelRightContractRegular, PictureInPictureEnterRegular, MoreHorizontalRegular, LayoutColumnTwoFocusLeftFilled, LayoutColumnTwoSplitLeftFocusTopLeftFilled, LayoutColumnTwoSplitLeftFocusBottomLeftFilled, LayoutColumnTwoFocusRightFilled, LayoutColumnTwoSplitRightFocusTopRightFilled, LayoutColumnTwoSplitRightFocusBottomRightFilled, SettingsRegular, DocumentTextRegular, createFluentIcon, TextSortAscendingRegular, GlobeRegular, ArrowExpandAllRegular, ArrowCollapseAllRegular, CubeTreeRegular, BugRegular, ArrowUploadRegular, ArrowDownloadRegular, StopRegular, RecordRegular, DataBarHorizontalRegular, WrenchRegular, WeatherSunnyRegular, WeatherMoonRegular, ArrowRotateClockwiseRegular, ArrowExpandRegular, SelectObjectRegular, CubeRegular, CameraRegular, AddRegular, DeleteRegular, FullScreenMaximizeRegular, ChevronDownRegular, ChevronRightRegular, CircleSmallFilled, SaveRegular, PreviousRegular, ArrowPreviousRegular, TriangleLeftRegular, RecordStopRegular, PlayRegular, ArrowNextRegular, NextRegular, PauseRegular, LinkDismissRegular, LinkEditRegular, ArrowUndoRegular, BracesRegular, BracesDismiss16Regular, EyeRegular, CloudArrowUpRegular, CloudArrowDownRegular, EyeOffFilled, ArrowMoveFilled, StopFilled, PlayFilled, LockOpenRegular, LockClosedRegular, ResizeRegular, ChevronUpRegular, ArrowResetRegular, CircleHalfFillRegular, EyedropperRegular, PaintBucketRegular, InkStrokeRegular, StackRegular, FilmstripRegular, PauseFilled, WeatherSunnyLowFilled, LayerRegular, FrameRegular, AppGenericRegular, RectangleLandscapeRegular, BorderOutsideRegular, BorderNoneRegular, MyLocationRegular, 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, mergeClasses, Body1Strong, useId, useToastController, Toast, ToastTitle, FluentProvider, Toaster, Checkbox as Checkbox$1, createLightTheme, createDarkTheme, TeachingPopover, TeachingPopoverSurface, TeachingPopoverHeader, TeachingPopoverBody, Switch as Switch$1, createDOMRenderer, RendererProvider, Menu, MenuTrigger, SplitButton, MenuPopover, MenuList, MenuItem, Toolbar as Toolbar$1, ToolbarRadioButton, MenuGroup, MenuGroupHeader, treeItemLevelToken, FlatTree, FlatTreeItem, TreeItemLayout, MenuDivider, MenuItemCheckbox, useMergedRefs, Input, Dropdown as Dropdown$1, Option, Popover as Popover$1, PopoverTrigger, PopoverSurface, ColorPicker, ColorArea, ColorSlider, AlphaSlider, ColorSwatch, PresenceBadge, Slider as Slider$1, MenuItemRadio, Dialog, DialogSurface, DialogBody, DialogTitle, DialogContent, DialogActions, List as List$1, ListItem, Badge, Label, MessageBarTitle, Subtitle2, useComboboxFilter, Combobox, Textarea as Textarea$1, ToolbarButton, ToolbarDivider, Field } from '@fluentui/react-components';
|
|
4
|
+
import { ErrorCircleRegular, EyeFilled, EyeOffRegular, CheckmarkFilled, EditRegular, FilterRegular, PinFilled, PinRegular, ArrowCircleUpRegular, ChevronCircleRight16Regular, ChevronCircleRight20Regular, ChevronCircleDown16Regular, ChevronCircleDown20Regular, Copy16Regular, CopyRegular, PanelLeftExpandRegular, PanelRightExpandRegular, PanelLeftContractRegular, PanelRightContractRegular, PictureInPictureEnterRegular, MoreHorizontalRegular, LayoutColumnTwoFocusLeftFilled, LayoutColumnTwoSplitLeftFocusTopLeftFilled, LayoutColumnTwoSplitLeftFocusBottomLeftFilled, LayoutColumnTwoFocusRightFilled, LayoutColumnTwoSplitRightFocusTopRightFilled, LayoutColumnTwoSplitRightFocusBottomRightFilled, SettingsRegular, DocumentTextRegular, createFluentIcon, TextSortAscendingRegular, GlobeRegular, ArrowExpandAllRegular, ArrowCollapseAllRegular, CubeTreeRegular, BugRegular, ArrowUploadRegular, ArrowBidirectionalUpDownFilled, ArrowDownloadRegular, StopRegular, RecordRegular, DataBarHorizontalRegular, WrenchRegular, ArrowClockwiseRegular, WeatherSunnyRegular, WeatherMoonRegular, ArrowRotateClockwiseRegular, ArrowExpandRegular, SelectObjectRegular, CubeRegular, CameraRegular, AddRegular, DeleteRegular, FullScreenMaximizeRegular, ChevronDownRegular, ChevronRightRegular, CircleSmallFilled, SaveRegular, PreviousRegular, ArrowPreviousRegular, TriangleLeftRegular, RecordStopRegular, PlayRegular, ArrowNextRegular, NextRegular, PauseRegular, LinkDismissRegular, LinkEditRegular, ArrowUndoRegular, BracesRegular, BracesDismiss16Regular, EyeRegular, CloudArrowUpRegular, CloudArrowDownRegular, EyeOffFilled, ArrowMoveFilled, StopFilled, PlayFilled, LockOpenRegular, LockClosedRegular, ResizeRegular, ChevronUpRegular, ArrowResetRegular, CircleHalfFillRegular, EyedropperRegular, PaintBucketRegular, InkStrokeRegular, StackRegular, FilmstripRegular, PauseFilled, WeatherSunnyLowFilled, LayerRegular, FrameRegular, AppGenericRegular, RectangleLandscapeRegular, BorderOutsideRegular, BorderNoneRegular, MyLocationRegular, LightbulbRegular, VideoFilled, VideoRegular, FlashlightRegular, FlashlightOffRegular, DropRegular, BlurRegular, PipelineRegular, PersonWalkingRegular, DataLineRegular, SoundWaveCircleRegular, PersonSquareRegular, LayerDiagonalPersonRegular, ImageEditRegular, ImageRegular, TargetRegular, PersonFeedbackRegular, BranchRegular, DeleteFilled } from '@fluentui/react-icons';
|
|
5
5
|
import { Color3, Color4 } from '@babylonjs/core/Maths/math.color.js';
|
|
6
6
|
import { Vector3, Quaternion, Matrix, Vector2, Vector4, TmpVectors } from '@babylonjs/core/Maths/math.vector.js';
|
|
7
7
|
import { Observable } from '@babylonjs/core/Misc/observable.js';
|
|
@@ -45,6 +45,8 @@ import { Light } from '@babylonjs/core/Lights/light.js';
|
|
|
45
45
|
import { AbstractMesh } from '@babylonjs/core/Meshes/abstractMesh.js';
|
|
46
46
|
import { Node as Node$1 } from '@babylonjs/core/node.js';
|
|
47
47
|
import { createRoot } from 'react-dom/client';
|
|
48
|
+
import { SelectionOutlineLayer } from '@babylonjs/core/Layers/selectionOutlineLayer.js';
|
|
49
|
+
import { GaussianSplattingMesh } from '@babylonjs/core/Meshes/GaussianSplatting/gaussianSplattingMesh.js';
|
|
48
50
|
import { AnimationGroup, TargetedAnimation } from '@babylonjs/core/Animations/animationGroup.js';
|
|
49
51
|
import { Animation } from '@babylonjs/core/Animations/animation.js';
|
|
50
52
|
import { AnimationPropertiesOverride } from '@babylonjs/core/Animations/animationPropertiesOverride.js';
|
|
@@ -80,7 +82,6 @@ import { NodeMaterialBlockConnectionPointTypes } from '@babylonjs/core/Materials
|
|
|
80
82
|
import { FactorGradient, Color3Gradient, ColorGradient } from '@babylonjs/core/Misc/gradients.js';
|
|
81
83
|
import { ReadFile } from '@babylonjs/core/Misc/fileTools.js';
|
|
82
84
|
import { CubeTexture } from '@babylonjs/core/Materials/Textures/cubeTexture.js';
|
|
83
|
-
import { GaussianSplattingMesh } from '@babylonjs/core/Meshes/GaussianSplatting/gaussianSplattingMesh.js';
|
|
84
85
|
import { Mesh } from '@babylonjs/core/Meshes/mesh.js';
|
|
85
86
|
import { SkeletonViewer } from '@babylonjs/core/Debug/skeletonViewer.js';
|
|
86
87
|
import { VertexBuffer } from '@babylonjs/core/Meshes/buffer.js';
|
|
@@ -144,7 +145,6 @@ import { ImportAnimationsAsync, SceneLoader } from '@babylonjs/core/Loading/scen
|
|
|
144
145
|
import { FilesInput } from '@babylonjs/core/Misc/filesInput.js';
|
|
145
146
|
import { GLTFLoaderAnimationStartMode, GLTFLoaderCoordinateSystemMode, GLTFLoaderDefaultOptions } from '@babylonjs/loaders/glTF/glTFFileLoader.js';
|
|
146
147
|
import { GLTFValidation } from '@babylonjs/loaders/glTF/glTFValidation.js';
|
|
147
|
-
import { SelectionOutlineLayer } from '@babylonjs/core/Layers/selectionOutlineLayer.js';
|
|
148
148
|
import { EngineStore } from '@babylonjs/core/Engines/engineStore.js';
|
|
149
149
|
import { DebugLayer } from '@babylonjs/core/Debug/debugLayer.js';
|
|
150
150
|
import { Lazy } from '@babylonjs/core/Misc/lazy.js';
|
|
@@ -276,7 +276,7 @@ const Button = forwardRef((props, ref) => {
|
|
|
276
276
|
});
|
|
277
277
|
Button.displayName = "Button";
|
|
278
278
|
|
|
279
|
-
const useStyles$
|
|
279
|
+
const useStyles$U = makeStyles({
|
|
280
280
|
root: {
|
|
281
281
|
display: "flex",
|
|
282
282
|
flexDirection: "column",
|
|
@@ -357,7 +357,7 @@ class ErrorBoundary extends Component {
|
|
|
357
357
|
}
|
|
358
358
|
}
|
|
359
359
|
function ErrorFallback({ error, onRetry }) {
|
|
360
|
-
const styles = useStyles$
|
|
360
|
+
const styles = useStyles$U();
|
|
361
361
|
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 })] }));
|
|
362
362
|
}
|
|
363
363
|
|
|
@@ -369,82 +369,6 @@ function usePropertyChangedNotifier() {
|
|
|
369
369
|
}, [propertyContext]);
|
|
370
370
|
}
|
|
371
371
|
|
|
372
|
-
const InterceptorHooksMaps$1 = new WeakMap();
|
|
373
|
-
/** @internal */
|
|
374
|
-
function InterceptFunction(target, propertyKey, hooks) {
|
|
375
|
-
if (!hooks.afterCall) {
|
|
376
|
-
throw new Error("At least one hook must be provided.");
|
|
377
|
-
}
|
|
378
|
-
const originalFunction = Reflect.get(target, propertyKey, target);
|
|
379
|
-
if (typeof originalFunction !== "function") {
|
|
380
|
-
throw new Error(`Property "${propertyKey.toString()}" of object "${target}" is not a function.`);
|
|
381
|
-
}
|
|
382
|
-
// Make sure the property is configurable and writable, otherwise it is immutable and cannot be intercepted.
|
|
383
|
-
const propertyDescriptor = Reflect.getOwnPropertyDescriptor(target, propertyKey);
|
|
384
|
-
if (propertyDescriptor) {
|
|
385
|
-
if (!propertyDescriptor.configurable) {
|
|
386
|
-
throw new Error(`Property "${propertyKey.toString()}" of object "${target}" is not configurable.`);
|
|
387
|
-
}
|
|
388
|
-
if (propertyDescriptor.writable === false || (propertyDescriptor.writable === undefined && !propertyDescriptor.set)) {
|
|
389
|
-
throw new Error(`Property "${propertyKey.toString()}" of object "${target}" is readonly.`);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
// Get or create the hooks map for the target object.
|
|
393
|
-
let hooksMap = InterceptorHooksMaps$1.get(target);
|
|
394
|
-
if (!hooksMap) {
|
|
395
|
-
InterceptorHooksMaps$1.set(target, (hooksMap = new Map()));
|
|
396
|
-
}
|
|
397
|
-
// Get or create the hooks array for the property key.
|
|
398
|
-
let hooksForKey = hooksMap.get(propertyKey);
|
|
399
|
-
if (!hooksForKey) {
|
|
400
|
-
hooksMap.set(propertyKey, (hooksForKey = []));
|
|
401
|
-
if (
|
|
402
|
-
// Replace the function with a new one that calls the hooks in addition to the original function.
|
|
403
|
-
!Reflect.set(target, propertyKey, (...args) => {
|
|
404
|
-
const result = Reflect.apply(originalFunction, target, args);
|
|
405
|
-
for (const { afterCall } of hooksForKey) {
|
|
406
|
-
afterCall?.(...args);
|
|
407
|
-
}
|
|
408
|
-
return result;
|
|
409
|
-
})) {
|
|
410
|
-
throw new Error(`Failed to define new function "${propertyKey.toString()}" on object "${target}".`);
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
hooksForKey.push(hooks);
|
|
414
|
-
let isDisposed = false;
|
|
415
|
-
return {
|
|
416
|
-
dispose: () => {
|
|
417
|
-
if (!isDisposed) {
|
|
418
|
-
// Remove the hooks from the hooks array for the property key.
|
|
419
|
-
hooksForKey.splice(hooksForKey.indexOf(hooks), 1);
|
|
420
|
-
// If there are no more hooks for the property key, remove the property from the hooks map.
|
|
421
|
-
if (hooksForKey.length === 0) {
|
|
422
|
-
hooksMap.delete(propertyKey);
|
|
423
|
-
// If there are no more hooks for the target object, remove the hooks map from the WeakMap.
|
|
424
|
-
if (hooksMap.size === 0) {
|
|
425
|
-
InterceptorHooksMaps$1.delete(target);
|
|
426
|
-
}
|
|
427
|
-
if (propertyDescriptor) {
|
|
428
|
-
// If we have a property descriptor, it means the property was defined directly on the target object,
|
|
429
|
-
// in which case we replaced it and the original property descriptor needs to be restored.
|
|
430
|
-
if (!Reflect.defineProperty(target, propertyKey, propertyDescriptor)) {
|
|
431
|
-
throw new Error(`Failed to restore original function "${propertyKey.toString()}" on object "${target}".`);
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
else {
|
|
435
|
-
// Otherwise, the property was inherited through the prototype chain, and so we can simply delete it from
|
|
436
|
-
// the target object to allow it to fall back to the prototype chain as it did originally.
|
|
437
|
-
if (!Reflect.deleteProperty(target, propertyKey)) {
|
|
438
|
-
throw new Error(`Failed to delete transient function "${propertyKey.toString()}" on object "${target}".`);
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
isDisposed = true;
|
|
443
|
-
}
|
|
444
|
-
},
|
|
445
|
-
};
|
|
446
|
-
}
|
|
447
|
-
|
|
448
372
|
/**
|
|
449
373
|
* Gets the property descriptor for a property on an object, including inherited properties.
|
|
450
374
|
* @param target The object containing the property.
|
|
@@ -474,7 +398,7 @@ function IsPropertyReadonly(propertyDescriptor) {
|
|
|
474
398
|
// If the property is not writable, it is readonly.
|
|
475
399
|
return propertyDescriptor.writable === false || (propertyDescriptor.writable === undefined && !propertyDescriptor.set);
|
|
476
400
|
}
|
|
477
|
-
const InterceptorHooksMaps = new WeakMap();
|
|
401
|
+
const InterceptorHooksMaps$1 = new WeakMap();
|
|
478
402
|
/** @internal */
|
|
479
403
|
function InterceptProperty(target, propertyKey, hooks) {
|
|
480
404
|
// Find the property descriptor and note the owning object (might be inherited through the prototype chain).
|
|
@@ -505,9 +429,9 @@ function InterceptProperty(target, propertyKey, hooks) {
|
|
|
505
429
|
}
|
|
506
430
|
}
|
|
507
431
|
// Get or create the hooks map for the target object.
|
|
508
|
-
let hooksMap = InterceptorHooksMaps.get(target);
|
|
432
|
+
let hooksMap = InterceptorHooksMaps$1.get(target);
|
|
509
433
|
if (!hooksMap) {
|
|
510
|
-
InterceptorHooksMaps.set(target, (hooksMap = new Map()));
|
|
434
|
+
InterceptorHooksMaps$1.set(target, (hooksMap = new Map()));
|
|
511
435
|
}
|
|
512
436
|
// Get or create the hooks array for the property key.
|
|
513
437
|
let hooksForKey = hooksMap.get(propertyKey);
|
|
@@ -547,7 +471,7 @@ function InterceptProperty(target, propertyKey, hooks) {
|
|
|
547
471
|
hooksMap.delete(propertyKey);
|
|
548
472
|
// If there are no more hooks for the target object, remove the hooks map from the WeakMap.
|
|
549
473
|
if (hooksMap.size === 0) {
|
|
550
|
-
InterceptorHooksMaps.delete(target);
|
|
474
|
+
InterceptorHooksMaps$1.delete(target);
|
|
551
475
|
}
|
|
552
476
|
const shouldRestorePropertyDescriptor =
|
|
553
477
|
// If the property is owned by the target object, then we may have replaced an original property descriptor that needs to be restore.
|
|
@@ -573,6 +497,95 @@ function InterceptProperty(target, propertyKey, hooks) {
|
|
|
573
497
|
};
|
|
574
498
|
}
|
|
575
499
|
|
|
500
|
+
const DefaultWatcher = {
|
|
501
|
+
watchProperty(target, propertyKey, onChanged) {
|
|
502
|
+
return InterceptProperty(target, propertyKey, {
|
|
503
|
+
afterSet: (value) => onChanged(value),
|
|
504
|
+
});
|
|
505
|
+
},
|
|
506
|
+
refresh: () => { },
|
|
507
|
+
};
|
|
508
|
+
const WatcherContext = createContext(DefaultWatcher);
|
|
509
|
+
function useWatcher() {
|
|
510
|
+
return useContext(WatcherContext);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const InterceptorHooksMaps = new WeakMap();
|
|
514
|
+
/** @internal */
|
|
515
|
+
function InterceptFunction(target, propertyKey, hooks) {
|
|
516
|
+
if (!hooks.afterCall) {
|
|
517
|
+
throw new Error("At least one hook must be provided.");
|
|
518
|
+
}
|
|
519
|
+
const originalFunction = Reflect.get(target, propertyKey, target);
|
|
520
|
+
if (typeof originalFunction !== "function") {
|
|
521
|
+
throw new Error(`Property "${propertyKey.toString()}" of object "${target}" is not a function.`);
|
|
522
|
+
}
|
|
523
|
+
// Make sure the property is configurable and writable, otherwise it is immutable and cannot be intercepted.
|
|
524
|
+
const propertyDescriptor = Reflect.getOwnPropertyDescriptor(target, propertyKey);
|
|
525
|
+
if (propertyDescriptor) {
|
|
526
|
+
if (!propertyDescriptor.configurable) {
|
|
527
|
+
throw new Error(`Property "${propertyKey.toString()}" of object "${target}" is not configurable.`);
|
|
528
|
+
}
|
|
529
|
+
if (propertyDescriptor.writable === false || (propertyDescriptor.writable === undefined && !propertyDescriptor.set)) {
|
|
530
|
+
throw new Error(`Property "${propertyKey.toString()}" of object "${target}" is readonly.`);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
// Get or create the hooks map for the target object.
|
|
534
|
+
let hooksMap = InterceptorHooksMaps.get(target);
|
|
535
|
+
if (!hooksMap) {
|
|
536
|
+
InterceptorHooksMaps.set(target, (hooksMap = new Map()));
|
|
537
|
+
}
|
|
538
|
+
// Get or create the hooks array for the property key.
|
|
539
|
+
let hooksForKey = hooksMap.get(propertyKey);
|
|
540
|
+
if (!hooksForKey) {
|
|
541
|
+
hooksMap.set(propertyKey, (hooksForKey = []));
|
|
542
|
+
if (
|
|
543
|
+
// Replace the function with a new one that calls the hooks in addition to the original function.
|
|
544
|
+
!Reflect.set(target, propertyKey, (...args) => {
|
|
545
|
+
const result = Reflect.apply(originalFunction, target, args);
|
|
546
|
+
for (const { afterCall } of hooksForKey) {
|
|
547
|
+
afterCall?.(...args);
|
|
548
|
+
}
|
|
549
|
+
return result;
|
|
550
|
+
})) {
|
|
551
|
+
throw new Error(`Failed to define new function "${propertyKey.toString()}" on object "${target}".`);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
hooksForKey.push(hooks);
|
|
555
|
+
let isDisposed = false;
|
|
556
|
+
return {
|
|
557
|
+
dispose: () => {
|
|
558
|
+
if (!isDisposed) {
|
|
559
|
+
// Remove the hooks from the hooks array for the property key.
|
|
560
|
+
hooksForKey.splice(hooksForKey.indexOf(hooks), 1);
|
|
561
|
+
// If there are no more hooks for the property key, remove the property from the hooks map.
|
|
562
|
+
if (hooksForKey.length === 0) {
|
|
563
|
+
hooksMap.delete(propertyKey);
|
|
564
|
+
// If there are no more hooks for the target object, remove the hooks map from the WeakMap.
|
|
565
|
+
if (hooksMap.size === 0) {
|
|
566
|
+
InterceptorHooksMaps.delete(target);
|
|
567
|
+
}
|
|
568
|
+
if (propertyDescriptor) {
|
|
569
|
+
// If we have a property descriptor, it means the property was defined directly on the target object,
|
|
570
|
+
// in which case we replaced it and the original property descriptor needs to be restored.
|
|
571
|
+
if (!Reflect.defineProperty(target, propertyKey, propertyDescriptor)) {
|
|
572
|
+
throw new Error(`Failed to restore original function "${propertyKey.toString()}" on object "${target}".`);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
else {
|
|
576
|
+
// Otherwise, the property was inherited through the prototype chain, and so we can simply delete it from
|
|
577
|
+
// the target object to allow it to fall back to the prototype chain as it did originally.
|
|
578
|
+
if (!Reflect.deleteProperty(target, propertyKey)) {
|
|
579
|
+
throw new Error(`Failed to delete transient function "${propertyKey.toString()}" on object "${target}".`);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
isDisposed = true;
|
|
584
|
+
}
|
|
585
|
+
},
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
|
|
576
589
|
/**
|
|
577
590
|
* Provides an observable that fires when a specified function/property is called/set.
|
|
578
591
|
* @param type The type of the interceptor, either "function" or "property".
|
|
@@ -583,6 +596,7 @@ function InterceptProperty(target, propertyKey, hooks) {
|
|
|
583
596
|
function useInterceptObservable(type, target, propertyKey) {
|
|
584
597
|
// Create a cached observable. It effectively has the lifetime of the component that uses this hook.
|
|
585
598
|
const observable = useMemo(() => new Observable(), []);
|
|
599
|
+
const watcher = useWatcher();
|
|
586
600
|
// Whenever the type, target, or property key changes, we need to set up a new interceptor.
|
|
587
601
|
useEffect(() => {
|
|
588
602
|
let interceptToken = null;
|
|
@@ -595,11 +609,7 @@ function useInterceptObservable(type, target, propertyKey) {
|
|
|
595
609
|
});
|
|
596
610
|
}
|
|
597
611
|
else if (type === "property") {
|
|
598
|
-
interceptToken =
|
|
599
|
-
afterSet: () => {
|
|
600
|
-
observable.notifyObservers();
|
|
601
|
-
},
|
|
602
|
-
});
|
|
612
|
+
interceptToken = watcher.watchProperty(target, propertyKey, () => observable.notifyObservers());
|
|
603
613
|
}
|
|
604
614
|
else {
|
|
605
615
|
throw new Error(`Unknown interceptor type: ${type}`);
|
|
@@ -1068,14 +1078,20 @@ function useKeyState(key, options) {
|
|
|
1068
1078
|
useKeyListener({
|
|
1069
1079
|
onKeyDown: useCallback((e) => {
|
|
1070
1080
|
if (e.key === key) {
|
|
1081
|
+
if (options?.preventDefault) {
|
|
1082
|
+
e.preventDefault();
|
|
1083
|
+
}
|
|
1071
1084
|
setIsPressed(true);
|
|
1072
1085
|
}
|
|
1073
|
-
}, [key]),
|
|
1086
|
+
}, [key, options?.preventDefault]),
|
|
1074
1087
|
onKeyUp: useCallback((e) => {
|
|
1075
1088
|
if (e.key === key) {
|
|
1089
|
+
if (options?.preventDefault) {
|
|
1090
|
+
e.preventDefault();
|
|
1091
|
+
}
|
|
1076
1092
|
setIsPressed(false);
|
|
1077
1093
|
}
|
|
1078
|
-
}, [key]),
|
|
1094
|
+
}, [key, options?.preventDefault]),
|
|
1079
1095
|
}, options);
|
|
1080
1096
|
useEventListener("window", "blur", useCallback(() => setIsPressed(false), []), options); // Reset state on window blur to avoid stuck keys
|
|
1081
1097
|
return isPressed;
|
|
@@ -1258,9 +1274,9 @@ function useAccordionSectionItemState(props) {
|
|
|
1258
1274
|
}
|
|
1259
1275
|
prevItemIdRef.current = itemId;
|
|
1260
1276
|
}, [itemId, sectionCtx?.sectionId]);
|
|
1261
|
-
// Register item and detect duplicates
|
|
1277
|
+
// Register item and detect duplicates (skip nested items, as children of other AccordionSectionItem should not participate in pin/hide/search).
|
|
1262
1278
|
useEffect(() => {
|
|
1263
|
-
if (!accordionCtx || !itemUniqueId) {
|
|
1279
|
+
if (!accordionCtx || !itemUniqueId || isNested) {
|
|
1264
1280
|
return;
|
|
1265
1281
|
}
|
|
1266
1282
|
const { registeredItemIds } = accordionCtx;
|
|
@@ -1272,7 +1288,7 @@ function useAccordionSectionItemState(props) {
|
|
|
1272
1288
|
return () => {
|
|
1273
1289
|
registeredItemIds.delete(itemUniqueId);
|
|
1274
1290
|
};
|
|
1275
|
-
}, [accordionCtx, itemUniqueId, itemId, itemLabel, sectionCtx?.sectionId]);
|
|
1291
|
+
}, [accordionCtx, itemUniqueId, itemId, itemLabel, sectionCtx?.sectionId, isNested]);
|
|
1276
1292
|
// If no context, static item, or nested, return undefined
|
|
1277
1293
|
if (!accordionCtx || staticItem) {
|
|
1278
1294
|
return undefined;
|
|
@@ -1338,7 +1354,7 @@ function useIsSectionEmpty(sectionId) {
|
|
|
1338
1354
|
return hasItems;
|
|
1339
1355
|
}
|
|
1340
1356
|
|
|
1341
|
-
const useStyles$
|
|
1357
|
+
const useStyles$T = makeStyles({
|
|
1342
1358
|
accordion: {
|
|
1343
1359
|
display: "flex",
|
|
1344
1360
|
flexDirection: "column",
|
|
@@ -1425,7 +1441,7 @@ const useStyles$S = makeStyles({
|
|
|
1425
1441
|
*/
|
|
1426
1442
|
const AccordionMenuBar = () => {
|
|
1427
1443
|
AccordionMenuBar.displayName = "AccordionMenuBar";
|
|
1428
|
-
const classes = useStyles$
|
|
1444
|
+
const classes = useStyles$T();
|
|
1429
1445
|
const accordionCtx = useContext(AccordionContext);
|
|
1430
1446
|
if (!accordionCtx) {
|
|
1431
1447
|
return null;
|
|
@@ -1468,7 +1484,7 @@ const AccordionSectionBlock = (props) => {
|
|
|
1468
1484
|
const AccordionSectionItem = (props) => {
|
|
1469
1485
|
AccordionSectionItem.displayName = "AccordionSectionItem";
|
|
1470
1486
|
const { children, staticItem } = props;
|
|
1471
|
-
const classes = useStyles$
|
|
1487
|
+
const classes = useStyles$T();
|
|
1472
1488
|
const accordionCtx = useContext(AccordionContext);
|
|
1473
1489
|
const itemState = useAccordionSectionItemState(props);
|
|
1474
1490
|
const [ctrlMode, setCtrlMode] = useState(false);
|
|
@@ -1508,7 +1524,7 @@ const AccordionSectionItem = (props) => {
|
|
|
1508
1524
|
*/
|
|
1509
1525
|
const AccordionPinnedContainer = () => {
|
|
1510
1526
|
AccordionPinnedContainer.displayName = "AccordionPinnedContainer";
|
|
1511
|
-
const classes = useStyles$
|
|
1527
|
+
const classes = useStyles$T();
|
|
1512
1528
|
const accordionCtx = useContext(AccordionContext);
|
|
1513
1529
|
return (jsx("div", { ref: accordionCtx?.pinnedContainerRef, className: classes.pinnedContainer, children: jsx(MessageBar$1, { className: classes.pinnedContainerEmpty, children: jsx(MessageBarBody, { children: "No pinned items" }) }) }));
|
|
1514
1530
|
};
|
|
@@ -1519,7 +1535,7 @@ const AccordionPinnedContainer = () => {
|
|
|
1519
1535
|
*/
|
|
1520
1536
|
const AccordionSearchBox = () => {
|
|
1521
1537
|
AccordionSearchBox.displayName = "AccordionSearchBox";
|
|
1522
|
-
const classes = useStyles$
|
|
1538
|
+
const classes = useStyles$T();
|
|
1523
1539
|
const accordionCtx = useContext(AccordionContext);
|
|
1524
1540
|
if (!accordionCtx?.features.search) {
|
|
1525
1541
|
return null;
|
|
@@ -1535,14 +1551,14 @@ const AccordionSearchBox = () => {
|
|
|
1535
1551
|
*/
|
|
1536
1552
|
const AccordionSection = (props) => {
|
|
1537
1553
|
AccordionSection.displayName = "AccordionSection";
|
|
1538
|
-
const classes = useStyles$
|
|
1554
|
+
const classes = useStyles$T();
|
|
1539
1555
|
return jsx("div", { className: classes.panelDiv, children: props.children });
|
|
1540
1556
|
};
|
|
1541
1557
|
const StringAccordion = Accordion$1;
|
|
1542
1558
|
const Accordion = forwardRef((props, ref) => {
|
|
1543
1559
|
Accordion.displayName = "Accordion";
|
|
1544
1560
|
const { children, highlightSections, ...rest } = props;
|
|
1545
|
-
const classes = useStyles$
|
|
1561
|
+
const classes = useStyles$T();
|
|
1546
1562
|
const { size } = useContext(ToolContext);
|
|
1547
1563
|
const accordionCtx = useAccordionContext(props);
|
|
1548
1564
|
const hasPinning = accordionCtx?.features.pinning ?? false;
|
|
@@ -1639,7 +1655,7 @@ const Collapse = (props) => {
|
|
|
1639
1655
|
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 }) }));
|
|
1640
1656
|
};
|
|
1641
1657
|
|
|
1642
|
-
const useStyles$
|
|
1658
|
+
const useStyles$S = makeStyles({
|
|
1643
1659
|
button: {
|
|
1644
1660
|
display: "flex",
|
|
1645
1661
|
alignItems: "center",
|
|
@@ -1657,15 +1673,15 @@ const ToggleButton = (props) => {
|
|
|
1657
1673
|
ToggleButton.displayName = "ToggleButton";
|
|
1658
1674
|
const { value, onChange, title, appearance = "subtle" } = props;
|
|
1659
1675
|
const { size } = useContext(ToolContext);
|
|
1660
|
-
const classes = useStyles$
|
|
1676
|
+
const classes = useStyles$S();
|
|
1661
1677
|
const [checked, setChecked] = useState(value);
|
|
1662
1678
|
const toggle = useCallback(() => {
|
|
1663
|
-
setChecked((
|
|
1664
|
-
const enabled = !
|
|
1679
|
+
setChecked((prevChecked) => {
|
|
1680
|
+
const enabled = !prevChecked;
|
|
1665
1681
|
onChange(enabled);
|
|
1666
1682
|
return enabled;
|
|
1667
1683
|
});
|
|
1668
|
-
}, [
|
|
1684
|
+
}, [onChange]);
|
|
1669
1685
|
useEffect(() => {
|
|
1670
1686
|
setChecked(props.value);
|
|
1671
1687
|
}, [props.value]);
|
|
@@ -1908,11 +1924,11 @@ const CompactModeSettingDescriptor = {
|
|
|
1908
1924
|
};
|
|
1909
1925
|
const UseDegreesSettingDescriptor = {
|
|
1910
1926
|
key: "UseDegrees",
|
|
1911
|
-
defaultValue:
|
|
1927
|
+
defaultValue: true,
|
|
1912
1928
|
};
|
|
1913
1929
|
const UseEulerSettingDescriptor = {
|
|
1914
1930
|
key: "UseEuler",
|
|
1915
|
-
defaultValue:
|
|
1931
|
+
defaultValue: true,
|
|
1916
1932
|
};
|
|
1917
1933
|
const DisableCopySettingDescriptor = {
|
|
1918
1934
|
key: "DisableCopy",
|
|
@@ -1996,7 +2012,7 @@ const UXContextProvider = (props) => {
|
|
|
1996
2012
|
function AsReadonlyArray(array) {
|
|
1997
2013
|
return array;
|
|
1998
2014
|
}
|
|
1999
|
-
const useStyles$
|
|
2015
|
+
const useStyles$R = makeStyles({
|
|
2000
2016
|
rootDiv: {
|
|
2001
2017
|
flex: 1,
|
|
2002
2018
|
overflow: "hidden",
|
|
@@ -2005,7 +2021,7 @@ const useStyles$Q = makeStyles({
|
|
|
2005
2021
|
},
|
|
2006
2022
|
});
|
|
2007
2023
|
function ExtensibleAccordion(props) {
|
|
2008
|
-
const classes = useStyles$
|
|
2024
|
+
const classes = useStyles$R();
|
|
2009
2025
|
const { children, sections, sectionContent, context, sectionsRef, ...rest } = props;
|
|
2010
2026
|
const defaultSections = useMemo(() => {
|
|
2011
2027
|
const defaultSections = [];
|
|
@@ -2130,7 +2146,7 @@ function ExtensibleAccordion(props) {
|
|
|
2130
2146
|
})] }) })) }));
|
|
2131
2147
|
}
|
|
2132
2148
|
|
|
2133
|
-
const useStyles$
|
|
2149
|
+
const useStyles$Q = makeStyles({
|
|
2134
2150
|
paneRootDiv: {
|
|
2135
2151
|
display: "flex",
|
|
2136
2152
|
flex: 1,
|
|
@@ -2143,7 +2159,7 @@ const useStyles$P = makeStyles({
|
|
|
2143
2159
|
*/
|
|
2144
2160
|
const SidePaneContainer = forwardRef((props, ref) => {
|
|
2145
2161
|
const { className, ...rest } = props;
|
|
2146
|
-
const classes = useStyles$
|
|
2162
|
+
const classes = useStyles$Q();
|
|
2147
2163
|
return (jsx("div", { className: mergeClasses(classes.paneRootDiv, className), ref: ref, ...rest, children: props.children }));
|
|
2148
2164
|
});
|
|
2149
2165
|
|
|
@@ -2393,7 +2409,7 @@ const Theme = (props) => {
|
|
|
2393
2409
|
return (jsx(FluentProvider, { theme: theme, applyStylesToPortals: applyStylesToPortals, ...rest, children: props.children }));
|
|
2394
2410
|
};
|
|
2395
2411
|
|
|
2396
|
-
const useStyles$
|
|
2412
|
+
const useStyles$P = makeStyles({
|
|
2397
2413
|
extensionTeachingPopover: {
|
|
2398
2414
|
maxWidth: "320px",
|
|
2399
2415
|
},
|
|
@@ -2404,7 +2420,7 @@ const useStyles$O = makeStyles({
|
|
|
2404
2420
|
* @returns The teaching moment popover.
|
|
2405
2421
|
*/
|
|
2406
2422
|
const TeachingMoment = ({ shouldDisplay, positioningRef, onOpenChange, title, description }) => {
|
|
2407
|
-
const classes = useStyles$
|
|
2423
|
+
const classes = useStyles$P();
|
|
2408
2424
|
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 })] }) }));
|
|
2409
2425
|
};
|
|
2410
2426
|
|
|
@@ -2588,13 +2604,13 @@ function ConstructorFactory(constructor) {
|
|
|
2588
2604
|
return (...args) => new constructor(...args);
|
|
2589
2605
|
}
|
|
2590
2606
|
|
|
2591
|
-
const useStyles$
|
|
2607
|
+
const useStyles$O = makeStyles({
|
|
2592
2608
|
placeholderDiv: {
|
|
2593
2609
|
padding: `${tokens.spacingVerticalM} ${tokens.spacingHorizontalM}`,
|
|
2594
2610
|
},
|
|
2595
2611
|
});
|
|
2596
2612
|
const PropertiesPane = (props) => {
|
|
2597
|
-
const classes = useStyles$
|
|
2613
|
+
const classes = useStyles$O();
|
|
2598
2614
|
const entity = props.context;
|
|
2599
2615
|
return entity != null ? (jsx(ExtensibleAccordion, { ...props })) : (jsx("div", { className: classes.placeholderDiv, children: jsx(Body1Strong, { italic: true, children: "No entity selected." }) }));
|
|
2600
2616
|
};
|
|
@@ -2752,22 +2768,9 @@ const ChildWindow = (props) => {
|
|
|
2752
2768
|
body.style.padding = "0";
|
|
2753
2769
|
body.style.display = "flex";
|
|
2754
2770
|
body.style.overflow = "hidden";
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
onOpenChange?.(true);
|
|
2759
|
-
};
|
|
2760
|
-
// Once the child window document is ready, setup the window state which will trigger another effect that renders into the child window.
|
|
2761
|
-
if (childWindow.document.readyState === "complete") {
|
|
2762
|
-
applyWindowState();
|
|
2763
|
-
}
|
|
2764
|
-
else {
|
|
2765
|
-
const onChildWindowLoad = () => {
|
|
2766
|
-
applyWindowState();
|
|
2767
|
-
};
|
|
2768
|
-
childWindow.addEventListener("load", onChildWindowLoad, { once: true });
|
|
2769
|
-
disposeActions.push(() => childWindow.removeEventListener("load", onChildWindowLoad));
|
|
2770
|
-
}
|
|
2771
|
+
// Setup the window state, including creating a Fluent/Griffel "renderer" for managing runtime styles/classes in the child window.
|
|
2772
|
+
setWindowState({ mountNode: body, renderer: createDOMRenderer(childWindow.document) });
|
|
2773
|
+
onOpenChange?.(true);
|
|
2771
2774
|
// When the child window is closed for any reason, transition back to a closed state.
|
|
2772
2775
|
const onChildWindowUnload = () => {
|
|
2773
2776
|
setWindowState(undefined);
|
|
@@ -2909,7 +2912,7 @@ const RightSidePaneHeightAdjustSettingDescriptor = {
|
|
|
2909
2912
|
};
|
|
2910
2913
|
const RootComponentServiceIdentity = Symbol("RootComponent");
|
|
2911
2914
|
const ShellServiceIdentity = Symbol("ShellService");
|
|
2912
|
-
const useStyles$
|
|
2915
|
+
const useStyles$N = makeStyles({
|
|
2913
2916
|
mainView: {
|
|
2914
2917
|
flex: 1,
|
|
2915
2918
|
display: "flex",
|
|
@@ -3112,34 +3115,38 @@ const DockMenu = (props) => {
|
|
|
3112
3115
|
};
|
|
3113
3116
|
const PaneHeader = (props) => {
|
|
3114
3117
|
const { id, title, dockOptions } = props;
|
|
3115
|
-
const classes = useStyles$
|
|
3118
|
+
const classes = useStyles$N();
|
|
3116
3119
|
return (jsxs("div", { className: classes.paneHeaderDiv, children: [jsx(Subtitle2Stronger, { className: classes.paneHeaderText, children: title }), jsx(DockMenu, { sidePaneId: id, dockOptions: dockOptions, children: jsx(Button$1, { className: classes.paneHeaderButton, appearance: "transparent", icon: jsx(MoreHorizontalRegular, {}) }) })] }));
|
|
3117
3120
|
};
|
|
3118
3121
|
// 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.
|
|
3119
|
-
const ToolbarItem = (
|
|
3120
|
-
|
|
3122
|
+
const ToolbarItem = (props) => {
|
|
3123
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
3124
|
+
const { verticalLocation, horizontalLocation, id, component: Component, displayName } = props;
|
|
3125
|
+
const classes = useStyles$N();
|
|
3121
3126
|
const useTeachingMoment = useMemo(() => MakePopoverTeachingMoment(`Bar/${verticalLocation}/${horizontalLocation}/${displayName ?? id}`), [displayName, id]);
|
|
3122
|
-
const teachingMoment = useTeachingMoment(
|
|
3123
|
-
|
|
3127
|
+
const teachingMoment = useTeachingMoment(props.teachingMoment === false);
|
|
3128
|
+
const title = typeof props.teachingMoment === "object" ? props.teachingMoment.title : (displayName ?? id);
|
|
3129
|
+
const description = typeof props.teachingMoment === "object" ? props.teachingMoment.description : `The "${displayName ?? id}" extension can be accessed here.`;
|
|
3130
|
+
return (jsxs(Fragment, { children: [jsx(TeachingMoment, { ...teachingMoment, shouldDisplay: teachingMoment.shouldDisplay, title: title, description: description }), jsx("div", { className: classes.barItem, ref: teachingMoment.targetRef, children: jsx(Component, {}) })] }));
|
|
3124
3131
|
};
|
|
3125
3132
|
// TODO: Handle overflow, possibly via https://react.fluentui.dev/?path=/docs/components-overflow--docs with priority.
|
|
3126
3133
|
// This component just renders a toolbar with left aligned toolbar items on the left and right aligned toolbar items on the right.
|
|
3127
3134
|
const Toolbar = ({ location, components }) => {
|
|
3128
|
-
const classes = useStyles$
|
|
3135
|
+
const classes = useStyles$N();
|
|
3129
3136
|
const leftComponents = useMemo(() => components.filter((entry) => entry.horizontalLocation === "left"), [components]);
|
|
3130
3137
|
const rightComponents = useMemo(() => components.filter((entry) => entry.horizontalLocation === "right"), [components]);
|
|
3131
|
-
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,
|
|
3138
|
+
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))) })] })) }));
|
|
3132
3139
|
};
|
|
3133
3140
|
// This is a wrapper for a tab in a side pane that simply adds a teaching moment, which is useful for dynamically added items, possibly from extensions.
|
|
3134
3141
|
const SidePaneTab = (props) => {
|
|
3135
3142
|
const { location, id, isSelected, isFirst, isLast, dockOptions,
|
|
3136
3143
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
3137
|
-
icon: Icon, title,
|
|
3138
|
-
const classes = useStyles$
|
|
3144
|
+
icon: Icon, title, } = props;
|
|
3145
|
+
const classes = useStyles$N();
|
|
3139
3146
|
const useTeachingMoment = useMemo(() => MakePopoverTeachingMoment(`Pane/${location}/${title ?? id}`), [title, id]);
|
|
3140
|
-
const teachingMoment = useTeachingMoment(
|
|
3147
|
+
const teachingMoment = useTeachingMoment(props.teachingMoment === false);
|
|
3141
3148
|
const tabClass = mergeClasses(classes.tab, isSelected ? classes.selectedTab : classes.unselectedTab, isFirst ? classes.firstTab : undefined, isLast ? classes.lastTab : undefined);
|
|
3142
|
-
return (jsxs(Fragment, { children: [jsx(TeachingMoment, { ...teachingMoment, shouldDisplay: teachingMoment.shouldDisplay
|
|
3149
|
+
return (jsxs(Fragment, { children: [jsx(TeachingMoment, { ...teachingMoment, shouldDisplay: teachingMoment.shouldDisplay, title: typeof props.teachingMoment === "object" ? props.teachingMoment.title : (title ?? "Extension"), description: typeof props.teachingMoment === "object" ? props.teachingMoment.description : `The "${title ?? id}" extension can be accessed here.` }), jsx("div", { className: tabClass, children: jsx(DockMenu, { openOnContext: true, sidePaneId: id, dockOptions: dockOptions, children: jsx(Tooltip, { content: title ?? id, children: jsx(ToolbarRadioButton, { ref: teachingMoment.targetRef, appearance: "transparent", className: classes.tabRadioButton, name: "selectedTab", value: id, icon: {
|
|
3143
3150
|
children: jsx(Icon, {}),
|
|
3144
3151
|
} }) }) }) })] }));
|
|
3145
3152
|
};
|
|
@@ -3147,7 +3154,7 @@ const SidePaneTab = (props) => {
|
|
|
3147
3154
|
// In "compact" mode, the tab list is integrated into the pane itself.
|
|
3148
3155
|
// In "full" mode, the returned tab list is later injected into the toolbar.
|
|
3149
3156
|
function usePane(location, defaultWidth, minWidth, sidePanes, onSelectSidePane, dockOperations, toolbarMode, topBarItems, bottomBarItems, initialCollapsed) {
|
|
3150
|
-
const classes = useStyles$
|
|
3157
|
+
const classes = useStyles$N();
|
|
3151
3158
|
const [topSelectedTab, setTopSelectedTab] = useState();
|
|
3152
3159
|
const [bottomSelectedTab, setBottomSelectedTab] = useState();
|
|
3153
3160
|
const [collapsed, setCollapsed] = useState(initialCollapsed);
|
|
@@ -3249,7 +3256,7 @@ function usePane(location, defaultWidth, minWidth, sidePanes, onSelectSidePane,
|
|
|
3249
3256
|
setCollapsed(false);
|
|
3250
3257
|
}, children: paneComponents.map((entry, index) => {
|
|
3251
3258
|
const isSelected = selectedTab?.key === entry.key;
|
|
3252
|
-
return (jsx(SidePaneTab, { location: location, id: entry.key, title: entry.title, icon: entry.icon,
|
|
3259
|
+
return (jsx(SidePaneTab, { location: location, id: entry.key, title: entry.title, icon: entry.icon, teachingMoment: entry.teachingMoment, isSelected: isSelected && !collapsed, isFirst: index === 0, isLast: index === paneComponents.length - 1, dockOptions: dockOptions }, entry.key));
|
|
3253
3260
|
}) }) })), toolbarMode === "full" && (jsx(Collapse, { visible: !isChildWindowOpen, orientation: "horizontal", children: expandCollapseButton }))] })) }));
|
|
3254
3261
|
}, [location, collapsed, isChildWindowOpen, expandCollapseButton]);
|
|
3255
3262
|
// This memos the TabList to make it easy for the JSX to be inserted at the top of the pane (in "compact" mode) or returned to the caller to be used in the toolbar (in "full" mode).
|
|
@@ -3344,7 +3351,7 @@ function MakeShellServiceDefinition({ leftPaneDefaultWidth = 350, leftPaneMinWid
|
|
|
3344
3351
|
expand: () => onCollapseChanged.notifyObservers({ location: "right", collapsed: false }),
|
|
3345
3352
|
};
|
|
3346
3353
|
const rootComponent = () => {
|
|
3347
|
-
const classes = useStyles$
|
|
3354
|
+
const classes = useStyles$N();
|
|
3348
3355
|
const [sidePaneDockOverrides, setSidePaneDockOverrides] = useSetting(SidePaneDockOverridesSettingDescriptor);
|
|
3349
3356
|
// This function returns a promise that resolves after the dock change takes effect so that
|
|
3350
3357
|
// we can then select the re-docked pane.
|
|
@@ -3579,7 +3586,7 @@ const SettingsServiceDefinition = {
|
|
|
3579
3586
|
horizontalLocation: "right",
|
|
3580
3587
|
verticalLocation: "top",
|
|
3581
3588
|
order: 500,
|
|
3582
|
-
|
|
3589
|
+
teachingMoment: false,
|
|
3583
3590
|
content: () => {
|
|
3584
3591
|
const sections = useOrderedObservableCollection(sectionsCollection);
|
|
3585
3592
|
const sectionContent = useObservableCollection(sectionContentCollection);
|
|
@@ -3640,7 +3647,7 @@ const SelectionServiceDefinition = {
|
|
|
3640
3647
|
selectedEntityObservable.notifyObservers();
|
|
3641
3648
|
if (item) {
|
|
3642
3649
|
const disposable = item;
|
|
3643
|
-
if (disposable.dispose) {
|
|
3650
|
+
if (typeof disposable.dispose === "function") {
|
|
3644
3651
|
disposedHook = InterceptFunction(disposable, "dispose", { afterCall: () => setSelectedItem(null) });
|
|
3645
3652
|
}
|
|
3646
3653
|
}
|
|
@@ -3685,7 +3692,7 @@ const PropertiesServiceDefinition = {
|
|
|
3685
3692
|
horizontalLocation: "right",
|
|
3686
3693
|
verticalLocation: "top",
|
|
3687
3694
|
order: 100,
|
|
3688
|
-
|
|
3695
|
+
teachingMoment: false,
|
|
3689
3696
|
keepMounted: true,
|
|
3690
3697
|
content: () => {
|
|
3691
3698
|
const sections = useOrderedObservableCollection(sectionsCollection);
|
|
@@ -4005,7 +4012,7 @@ function useSceneExplorerDragDrop(options) {
|
|
|
4005
4012
|
|
|
4006
4013
|
const SyntheticUniqueIds = new WeakMap();
|
|
4007
4014
|
function GetEntityId(entity) {
|
|
4008
|
-
if (entity.uniqueId
|
|
4015
|
+
if ("uniqueId" in entity && typeof entity.uniqueId === "number") {
|
|
4009
4016
|
return entity.uniqueId;
|
|
4010
4017
|
}
|
|
4011
4018
|
let id = SyntheticUniqueIds.get(entity);
|
|
@@ -4014,6 +4021,13 @@ function GetEntityId(entity) {
|
|
|
4014
4021
|
}
|
|
4015
4022
|
return id;
|
|
4016
4023
|
}
|
|
4024
|
+
function IsEntityHidden(entity) {
|
|
4025
|
+
return ("reservedDataStore" in entity &&
|
|
4026
|
+
typeof entity.reservedDataStore === "object" &&
|
|
4027
|
+
entity.reservedDataStore &&
|
|
4028
|
+
"hidden" in entity.reservedDataStore &&
|
|
4029
|
+
entity.reservedDataStore.hidden === true);
|
|
4030
|
+
}
|
|
4017
4031
|
function GetEntitySection(entityItem) {
|
|
4018
4032
|
let current = entityItem;
|
|
4019
4033
|
while (current.type === "entity") {
|
|
@@ -4082,7 +4096,7 @@ function CoerceEntityArray(entities, sort) {
|
|
|
4082
4096
|
}
|
|
4083
4097
|
return entities;
|
|
4084
4098
|
}
|
|
4085
|
-
const useStyles$
|
|
4099
|
+
const useStyles$M = makeStyles({
|
|
4086
4100
|
rootDiv: {
|
|
4087
4101
|
flex: 1,
|
|
4088
4102
|
overflow: "hidden",
|
|
@@ -4190,14 +4204,14 @@ function MakeInlineCommandElement(command, isPlaceholder) {
|
|
|
4190
4204
|
}
|
|
4191
4205
|
const SceneTreeItem = (props) => {
|
|
4192
4206
|
const { isSelected, select } = props;
|
|
4193
|
-
const classes = useStyles$
|
|
4207
|
+
const classes = useStyles$M();
|
|
4194
4208
|
const [compactMode] = useSetting(CompactModeSettingDescriptor);
|
|
4195
4209
|
const treeItemLayoutClass = mergeClasses(classes.sceneTreeItemLayout, compactMode ? classes.treeItemLayoutCompact : undefined);
|
|
4196
4210
|
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"));
|
|
4197
4211
|
};
|
|
4198
4212
|
const SectionTreeItem = (props) => {
|
|
4199
4213
|
const { section, isFiltering, commandProviders, expandAll, collapseAll, isDropTarget, ...dropProps } = props;
|
|
4200
|
-
const classes = useStyles$
|
|
4214
|
+
const classes = useStyles$M();
|
|
4201
4215
|
const [compactMode] = useSetting(CompactModeSettingDescriptor);
|
|
4202
4216
|
// Get the commands that apply to this section.
|
|
4203
4217
|
const commands = useResource(useCallback(() => {
|
|
@@ -4214,7 +4228,7 @@ const SectionTreeItem = (props) => {
|
|
|
4214
4228
|
};
|
|
4215
4229
|
const EntityTreeItem = (props) => {
|
|
4216
4230
|
const { entityItem, isSelected, select, isFiltering, commandProviders, expandAll, collapseAll, isDragging, isDropTarget, ...dragProps } = props;
|
|
4217
|
-
const classes = useStyles$
|
|
4231
|
+
const classes = useStyles$M();
|
|
4218
4232
|
const [compactMode] = useSetting(CompactModeSettingDescriptor);
|
|
4219
4233
|
const hasChildren = !!entityItem.children?.length;
|
|
4220
4234
|
const displayInfo = useResource(useCallback(() => {
|
|
@@ -4330,8 +4344,8 @@ const EntityTreeItem = (props) => {
|
|
|
4330
4344
|
}, children: jsx(Body1, { wrap: false, truncate: true, children: name }) }) }, GetEntityId(entityItem.entity)) }), jsx(MenuPopover, { hidden: !hasChildren && contextMenuCommands.length === 0, children: jsxs(MenuList, { children: [hasChildren && (jsxs(Fragment, { children: [jsx(MenuItem, { icon: jsx(ArrowExpandAllRegular, {}), onClick: expandAll, children: jsx(Body1, { children: "Expand All" }) }), jsx(MenuItem, { icon: jsx(ArrowCollapseAllRegular, {}), onClick: collapseAll, children: jsx(Body1, { children: "Collapse All" }) })] })), hasChildren && contextMenuCommands.length > 0 && jsx(MenuDivider, {}), contextMenuItems] }) })] }));
|
|
4331
4345
|
};
|
|
4332
4346
|
const SceneExplorer = (props) => {
|
|
4333
|
-
const classes = useStyles$
|
|
4334
|
-
const { sections, entityCommandProviders, sectionCommandProviders, scene, selectedEntity } = props;
|
|
4347
|
+
const classes = useStyles$M();
|
|
4348
|
+
const { sections, entityCommandProviders, sectionCommandProviders, scene, selectedEntity = null } = props;
|
|
4335
4349
|
const [openItems, setOpenItems] = useState(new Set());
|
|
4336
4350
|
const [sceneVersion, setSceneVersion] = useState(0);
|
|
4337
4351
|
const scrollViewRef = useRef(null);
|
|
@@ -4400,7 +4414,7 @@ const SceneExplorer = (props) => {
|
|
|
4400
4414
|
scene: scene,
|
|
4401
4415
|
};
|
|
4402
4416
|
for (const section of sections) {
|
|
4403
|
-
const rootEntities = section.getRootEntities().filter((entity) => !entity
|
|
4417
|
+
const rootEntities = section.getRootEntities().filter((entity) => !IsEntityHidden(entity));
|
|
4404
4418
|
const sectionTreeItem = {
|
|
4405
4419
|
type: "section",
|
|
4406
4420
|
sectionName: section.displayName,
|
|
@@ -4432,7 +4446,7 @@ const SceneExplorer = (props) => {
|
|
|
4432
4446
|
(treeItem) => {
|
|
4433
4447
|
if (section.getEntityChildren) {
|
|
4434
4448
|
const children = section.getEntityChildren(treeItem.entity);
|
|
4435
|
-
return children.filter((child) => !child
|
|
4449
|
+
return children.filter((child) => !IsEntityHidden(child)).map((child) => createEntityTreeItemData(child, treeItem));
|
|
4436
4450
|
}
|
|
4437
4451
|
return null;
|
|
4438
4452
|
},
|
|
@@ -4623,7 +4637,7 @@ const SceneExplorerServiceDefinition = {
|
|
|
4623
4637
|
icon: CubeTreeRegular,
|
|
4624
4638
|
horizontalLocation: "left",
|
|
4625
4639
|
verticalLocation: "top",
|
|
4626
|
-
|
|
4640
|
+
teachingMoment: false,
|
|
4627
4641
|
keepMounted: true,
|
|
4628
4642
|
content: () => {
|
|
4629
4643
|
const sections = useOrderedObservableCollection(sectionsCollection);
|
|
@@ -4784,7 +4798,7 @@ const DebugServiceDefinition = {
|
|
|
4784
4798
|
horizontalLocation: "right",
|
|
4785
4799
|
verticalLocation: "top",
|
|
4786
4800
|
order: 200,
|
|
4787
|
-
|
|
4801
|
+
teachingMoment: false,
|
|
4788
4802
|
content: () => {
|
|
4789
4803
|
const sections = useOrderedObservableCollection(sectionsCollection);
|
|
4790
4804
|
const sectionContent = useObservableCollection(sectionContentCollection);
|
|
@@ -5953,7 +5967,7 @@ class CanvasGraphService {
|
|
|
5953
5967
|
}
|
|
5954
5968
|
}
|
|
5955
5969
|
|
|
5956
|
-
const useStyles$
|
|
5970
|
+
const useStyles$L = makeStyles({
|
|
5957
5971
|
canvas: {
|
|
5958
5972
|
flexGrow: 1,
|
|
5959
5973
|
width: "100%",
|
|
@@ -5962,7 +5976,7 @@ const useStyles$K = makeStyles({
|
|
|
5962
5976
|
});
|
|
5963
5977
|
const CanvasGraph = (props) => {
|
|
5964
5978
|
const { collector, scene, layoutObservable, returnToPlayheadObservable, onVisibleRangeChangedObservable, initialGraphSize } = props;
|
|
5965
|
-
const classes = useStyles$
|
|
5979
|
+
const classes = useStyles$L();
|
|
5966
5980
|
const canvasRef = useRef(null);
|
|
5967
5981
|
useEffect(() => {
|
|
5968
5982
|
if (!canvasRef.current) {
|
|
@@ -6016,101 +6030,208 @@ const CanvasGraph = (props) => {
|
|
|
6016
6030
|
return jsx("canvas", { className: classes.canvas, ref: canvasRef });
|
|
6017
6031
|
};
|
|
6018
6032
|
|
|
6019
|
-
function CoerceStepValue(step,
|
|
6020
|
-
// When the
|
|
6021
|
-
if (
|
|
6033
|
+
function CoerceStepValue(step, isFineKeyPressed, isCourseKeyPressed) {
|
|
6034
|
+
// When the fine key is pressed, decrease step by a factor of 10.
|
|
6035
|
+
if (isFineKeyPressed) {
|
|
6022
6036
|
return step * 0.1;
|
|
6023
6037
|
}
|
|
6024
|
-
// When the
|
|
6025
|
-
if (
|
|
6038
|
+
// When the course key is pressed, increase step by a factor of 10.
|
|
6039
|
+
if (isCourseKeyPressed) {
|
|
6026
6040
|
return step * 10;
|
|
6027
6041
|
}
|
|
6028
6042
|
return step;
|
|
6029
6043
|
}
|
|
6044
|
+
// Allow arbitrary expressions, primarily for math operations (e.g. 10*60 for 10 minutes in seconds).
|
|
6045
|
+
// Use Function constructor to safely evaluate the expression without allowing access to scope.
|
|
6046
|
+
// If the expression is invalid, fallback to NaN which will be caught by validateValue and prevent committing.
|
|
6047
|
+
function EvaluateExpression(rawValue) {
|
|
6048
|
+
const val = rawValue.trim();
|
|
6049
|
+
try {
|
|
6050
|
+
return Number(Function(`"use strict";return (${val})`)());
|
|
6051
|
+
}
|
|
6052
|
+
catch {
|
|
6053
|
+
return NaN;
|
|
6054
|
+
}
|
|
6055
|
+
}
|
|
6056
|
+
const useStyles$K = makeStyles({
|
|
6057
|
+
icon: {
|
|
6058
|
+
"&:hover": {
|
|
6059
|
+
color: tokens.colorBrandForeground1,
|
|
6060
|
+
},
|
|
6061
|
+
},
|
|
6062
|
+
});
|
|
6063
|
+
/**
|
|
6064
|
+
* A numeric input with a vertical drag-to-scrub icon (ArrowsBidirectionalRegular rotated 90°).
|
|
6065
|
+
* Click-and-drag up/down on the icon to increment/decrement the value.
|
|
6066
|
+
*/
|
|
6030
6067
|
const SpinButton = forwardRef((props, ref) => {
|
|
6031
|
-
SpinButton.displayName = "
|
|
6032
|
-
const
|
|
6068
|
+
SpinButton.displayName = "SpinButton2";
|
|
6069
|
+
const inputClasses = useInputStyles$1();
|
|
6070
|
+
const classes = useStyles$K();
|
|
6033
6071
|
const { size } = useContext(ToolContext);
|
|
6034
6072
|
const { min, max } = props;
|
|
6035
|
-
const
|
|
6073
|
+
const baseStep = props.step ?? 1;
|
|
6074
|
+
// Local ref for the input element so we can blur it programmatically (e.g. when a drag starts while editing).
|
|
6075
|
+
const inputRef = useRef(null);
|
|
6076
|
+
const mergedRef = useMergedRefs(ref, inputRef);
|
|
6077
|
+
// Modifier keys for step coercion.
|
|
6078
|
+
const isAltKeyPressed = useKeyState("Alt", { preventDefault: true });
|
|
6079
|
+
const isShiftKeyPressed = useKeyState("Shift");
|
|
6080
|
+
const step = CoerceStepValue(baseStep, isAltKeyPressed, isShiftKeyPressed);
|
|
6081
|
+
const stepPrecision = Math.max(0, CalculatePrecision(step));
|
|
6082
|
+
const [value, setValue] = useState(props.value ?? 0);
|
|
6036
6083
|
const lastCommittedValue = useRef(props.value);
|
|
6037
|
-
|
|
6038
|
-
const
|
|
6039
|
-
const
|
|
6040
|
-
|
|
6041
|
-
const [
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
const
|
|
6045
|
-
const
|
|
6084
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
6085
|
+
const scrubStartYRef = useRef(0);
|
|
6086
|
+
const scrubStartValueRef = useRef(0);
|
|
6087
|
+
const lastPointerYRef = useRef(0);
|
|
6088
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
6089
|
+
// Editing state: when the user is typing, we show their raw text rather than the formatted value.
|
|
6090
|
+
const [isEditing, setIsEditing] = useState(false);
|
|
6091
|
+
const [editText, setEditText] = useState("");
|
|
6092
|
+
const valuePrecision = Math.max(0, CalculatePrecision(value));
|
|
6093
|
+
// Display precision: controls how many decimals are shown in the formatted displayValue. Cap at 4 to avoid wild numbers.
|
|
6094
|
+
// If a fixed precision prop is provided, use it instead.
|
|
6095
|
+
const displayPrecision = props.precision ?? Math.min(4, Math.max(stepPrecision, valuePrecision));
|
|
6096
|
+
// Format a number for display: toFixed, then trim trailing zeros and period unless a fixed precision is specified.
|
|
6097
|
+
const formatValue = useCallback((v) => {
|
|
6098
|
+
const fixed = v.toFixed(displayPrecision);
|
|
6099
|
+
if (props.precision !== undefined) {
|
|
6100
|
+
return fixed;
|
|
6101
|
+
}
|
|
6102
|
+
return fixed.replace(/(\.\d*?)0+$/, "$1").replace(/\.$/, "");
|
|
6103
|
+
}, [displayPrecision, props.precision]);
|
|
6046
6104
|
useEffect(() => {
|
|
6047
|
-
if (props.value !== lastCommittedValue.current) {
|
|
6105
|
+
if (!isDragging && props.value !== lastCommittedValue.current) {
|
|
6048
6106
|
lastCommittedValue.current = props.value;
|
|
6049
|
-
setValue(props.value);
|
|
6107
|
+
setValue(props.value ?? 0);
|
|
6050
6108
|
}
|
|
6051
|
-
}, [props.value]);
|
|
6052
|
-
const validateValue = (numericValue) => {
|
|
6109
|
+
}, [props.value, isDragging]);
|
|
6110
|
+
const validateValue = useCallback((numericValue) => {
|
|
6053
6111
|
const outOfBounds = (min !== undefined && numericValue < min) || (max !== undefined && numericValue > max);
|
|
6054
6112
|
const failsValidator = props.validator && !props.validator(numericValue);
|
|
6055
6113
|
const failsIntCheck = props.forceInt ? !Number.isInteger(numericValue) : false;
|
|
6056
6114
|
const invalid = !!outOfBounds || !!failsValidator || isNaN(numericValue) || !!failsIntCheck;
|
|
6057
6115
|
return !invalid;
|
|
6058
|
-
};
|
|
6059
|
-
|
|
6060
|
-
|
|
6116
|
+
}, [min, max, props.validator, props.forceInt]);
|
|
6117
|
+
// Constrain a value to the valid range by clamping to [min, max].
|
|
6118
|
+
const constrainValue = useCallback((v) => Clamp(v, min ?? -Infinity, max ?? Infinity), [min, max]);
|
|
6119
|
+
const tryCommitValue = useCallback((currVal) => {
|
|
6061
6120
|
if (validateValue(currVal) && currVal !== lastCommittedValue.current) {
|
|
6062
6121
|
lastCommittedValue.current = currVal;
|
|
6063
6122
|
props.onChange(currVal);
|
|
6064
6123
|
}
|
|
6065
|
-
};
|
|
6066
|
-
const
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6124
|
+
}, [validateValue, props.onChange]);
|
|
6125
|
+
const handleInputChange = useCallback((_, data) => {
|
|
6126
|
+
// Just update the raw text — no evaluation or commit until Enter/blur.
|
|
6127
|
+
setEditText(data.value);
|
|
6128
|
+
}, []);
|
|
6129
|
+
// Evaluate the current edit text and commit the value. Returns the clamped value if valid, or undefined.
|
|
6130
|
+
const commitEditText = useCallback((text) => {
|
|
6131
|
+
const numericValue = EvaluateExpression(text);
|
|
6132
|
+
if (!isNaN(numericValue) && validateValue(numericValue)) {
|
|
6133
|
+
const constrained = constrainValue(numericValue);
|
|
6134
|
+
setValue(constrained);
|
|
6135
|
+
tryCommitValue(constrained);
|
|
6136
|
+
return constrained;
|
|
6071
6137
|
}
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6138
|
+
return undefined;
|
|
6139
|
+
}, [validateValue, constrainValue, tryCommitValue]);
|
|
6140
|
+
const handleIconPointerDown = useCallback((e) => {
|
|
6141
|
+
e.preventDefault();
|
|
6142
|
+
e.stopPropagation();
|
|
6143
|
+
// If the input was being edited, commit the current text and blur the input
|
|
6144
|
+
// so the focus state stays consistent after the drag ends.
|
|
6145
|
+
let startValue = value;
|
|
6146
|
+
if (isEditing) {
|
|
6147
|
+
const committed = commitEditText(editText);
|
|
6148
|
+
if (committed !== undefined) {
|
|
6149
|
+
startValue = committed;
|
|
6150
|
+
}
|
|
6151
|
+
setIsEditing(false);
|
|
6152
|
+
}
|
|
6153
|
+
// Blur the active element to ensure we can observe document level modifier keys.
|
|
6154
|
+
inputRef.current?.ownerDocument.activeElement?.blur?.();
|
|
6155
|
+
setIsDragging(true);
|
|
6156
|
+
scrubStartYRef.current = e.clientY;
|
|
6157
|
+
scrubStartValueRef.current = startValue;
|
|
6158
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
6159
|
+
}, [value, isEditing, editText, commitEditText]);
|
|
6160
|
+
// When the step size changes during a drag (e.g. Shift/Alt pressed or released), reset the scrub reference point
|
|
6161
|
+
// to the current value and pointer position so only future movement uses the new step.
|
|
6162
|
+
useEffect(() => {
|
|
6163
|
+
if (isDragging) {
|
|
6164
|
+
scrubStartValueRef.current = value;
|
|
6165
|
+
scrubStartYRef.current = lastPointerYRef.current;
|
|
6166
|
+
}
|
|
6167
|
+
}, [step]);
|
|
6168
|
+
const handleIconPointerMove = useCallback((e) => {
|
|
6169
|
+
if (!isDragging) {
|
|
6170
|
+
return;
|
|
6076
6171
|
}
|
|
6077
|
-
|
|
6078
|
-
|
|
6172
|
+
lastPointerYRef.current = e.clientY;
|
|
6173
|
+
// Dragging up (negative dy) should increment, dragging down should decrement.
|
|
6174
|
+
// Scale delta by step but round to display precision (not step) for smooth fine-grained control.
|
|
6175
|
+
const dy = scrubStartYRef.current - e.clientY;
|
|
6176
|
+
// 5 is just a number that "feels right" for the drag sensitivity — it determines how far the user needs to drag to change the value by 1 step.
|
|
6177
|
+
const delta = (dy * step) / 5;
|
|
6178
|
+
const raw = scrubStartValueRef.current + delta;
|
|
6179
|
+
const precisionFactor = Math.pow(10, displayPrecision);
|
|
6180
|
+
const rounded = Math.round(raw * precisionFactor) / precisionFactor;
|
|
6181
|
+
const constrained = constrainValue(rounded);
|
|
6182
|
+
setValue(constrained);
|
|
6183
|
+
tryCommitValue(constrained);
|
|
6184
|
+
}, [isDragging, step, displayPrecision, constrainValue, tryCommitValue]);
|
|
6185
|
+
const handleIconPointerUp = useCallback((e) => {
|
|
6186
|
+
setIsDragging(false);
|
|
6187
|
+
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
6188
|
+
}, []);
|
|
6189
|
+
const handleKeyDown = useCallback((event) => {
|
|
6190
|
+
// Commit on Enter and blur the input if the value is valid.
|
|
6191
|
+
if (event.key === "Enter") {
|
|
6192
|
+
const committed = commitEditText(event.currentTarget.value);
|
|
6193
|
+
if (committed !== undefined) {
|
|
6194
|
+
inputRef.current?.blur();
|
|
6195
|
+
}
|
|
6196
|
+
}
|
|
6197
|
+
if (event.key === "ArrowUp" || event.key === "ArrowDown") {
|
|
6198
|
+
event.preventDefault();
|
|
6199
|
+
const direction = event.key === "ArrowUp" ? 1 : -1;
|
|
6200
|
+
const newValue = constrainValue(Math.round((value + direction * step) / step) * step);
|
|
6201
|
+
setValue(newValue);
|
|
6202
|
+
tryCommitValue(newValue);
|
|
6203
|
+
// Update edit text to reflect the new value so the user sees the change
|
|
6204
|
+
setEditText(formatValue(newValue));
|
|
6079
6205
|
}
|
|
6080
6206
|
HandleKeyDown(event);
|
|
6081
|
-
};
|
|
6082
|
-
const
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
}
|
|
6098
|
-
catch {
|
|
6099
|
-
return NaN;
|
|
6100
|
-
}
|
|
6101
|
-
})(event.target.value);
|
|
6102
|
-
setValue(currVal);
|
|
6103
|
-
tryCommitValue(currVal);
|
|
6207
|
+
}, [value, step, constrainValue, tryCommitValue, commitEditText, formatValue]);
|
|
6208
|
+
const id = useId("spin-button2");
|
|
6209
|
+
// Real-time validation: when editing, validate the expression; otherwise validate the committed value.
|
|
6210
|
+
// (validateValue already handles NaN, so no separate isNaN check needed.)
|
|
6211
|
+
const isInputInvalid = !validateValue(isEditing ? EvaluateExpression(editText) : value);
|
|
6212
|
+
const mergedClassName = mergeClasses(inputClasses.input, isInputInvalid ? inputClasses.invalid : "", props.className);
|
|
6213
|
+
const inputSlotClassName = mergeClasses(inputClasses.inputSlot, props.inputClassName);
|
|
6214
|
+
const formattedValue = formatValue(value);
|
|
6215
|
+
const handleFocus = useCallback(() => {
|
|
6216
|
+
setIsEditing(true);
|
|
6217
|
+
setEditText(formattedValue);
|
|
6218
|
+
}, [formattedValue]);
|
|
6219
|
+
const handleBlur = useCallback((event) => {
|
|
6220
|
+
// Skip blur handling if a drag just started (icon pointerDown already committed).
|
|
6221
|
+
if (isDragging) {
|
|
6222
|
+
return;
|
|
6104
6223
|
}
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
const
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6224
|
+
commitEditText(event.target.value);
|
|
6225
|
+
setIsEditing(false);
|
|
6226
|
+
HandleOnBlur(event);
|
|
6227
|
+
}, [commitEditText, isDragging]);
|
|
6228
|
+
const contentBefore = !props.disableDragButton && (isHovered || isDragging) && !isInputInvalid ? (jsx(ArrowBidirectionalUpDownFilled, { className: classes.icon, style: { cursor: isDragging ? "ns-resize" : "pointer" }, onPointerDown: handleIconPointerDown, onPointerMove: handleIconPointerMove, onPointerUp: handleIconPointerUp })) : undefined;
|
|
6229
|
+
const input = (jsx("div", { onMouseEnter: () => setIsHovered(true), onMouseLeave: () => {
|
|
6230
|
+
if (!isDragging) {
|
|
6231
|
+
setIsHovered(false);
|
|
6232
|
+
}
|
|
6233
|
+
}, children: jsx(Input, { ref: mergedRef, id: id, appearance: "outline", size: size, className: mergedClassName, input: { className: inputSlotClassName }, value: isEditing ? editText : formattedValue, onChange: handleInputChange, onFocus: handleFocus, onKeyDown: handleKeyDown, onBlur: handleBlur, contentBefore: contentBefore, contentAfter: props.unit }) }));
|
|
6234
|
+
return props.infoLabel ? (jsxs("div", { className: inputClasses.container, children: [jsx(InfoLabel, { ...props.infoLabel, htmlFor: id }), input] })) : (input);
|
|
6114
6235
|
});
|
|
6115
6236
|
|
|
6116
6237
|
const TextInput = (props) => {
|
|
@@ -6792,7 +6913,7 @@ const StatsServiceDefinition = {
|
|
|
6792
6913
|
horizontalLocation: "right",
|
|
6793
6914
|
verticalLocation: "top",
|
|
6794
6915
|
order: 300,
|
|
6795
|
-
|
|
6916
|
+
teachingMoment: false,
|
|
6796
6917
|
content: () => {
|
|
6797
6918
|
const sections = useOrderedObservableCollection(sectionsCollection);
|
|
6798
6919
|
const sectionContent = useObservableCollection(sectionContentCollection);
|
|
@@ -6881,7 +7002,7 @@ const ToolsServiceDefinition = {
|
|
|
6881
7002
|
horizontalLocation: "right",
|
|
6882
7003
|
verticalLocation: "top",
|
|
6883
7004
|
order: 400,
|
|
6884
|
-
|
|
7005
|
+
teachingMoment: false,
|
|
6885
7006
|
content: () => {
|
|
6886
7007
|
const sections = useOrderedObservableCollection(sectionsCollection);
|
|
6887
7008
|
const sectionContent = useObservableCollection(sectionContentCollection);
|
|
@@ -6899,12 +7020,337 @@ const ToolsServiceDefinition = {
|
|
|
6899
7020
|
},
|
|
6900
7021
|
};
|
|
6901
7022
|
|
|
7023
|
+
const useStyles$F = makeStyles({
|
|
7024
|
+
dropdown: {
|
|
7025
|
+
...UniformWidthStyling,
|
|
7026
|
+
},
|
|
7027
|
+
});
|
|
7028
|
+
/**
|
|
7029
|
+
* Wraps a dropdown in a property line
|
|
7030
|
+
* @param props - PropertyLineProps and DropdownProps
|
|
7031
|
+
* @returns property-line wrapped dropdown
|
|
7032
|
+
*/
|
|
7033
|
+
const DropdownPropertyLine = forwardRef((props, ref) => {
|
|
7034
|
+
DropdownPropertyLine.displayName = "DropdownPropertyLine";
|
|
7035
|
+
const classes = useStyles$F();
|
|
7036
|
+
return (jsx(PropertyLine, { ...props, ref: ref, children: jsx(Dropdown, { ...props, className: classes.dropdown }) }));
|
|
7037
|
+
});
|
|
7038
|
+
/**
|
|
7039
|
+
* Dropdown component for number values.
|
|
7040
|
+
*/
|
|
7041
|
+
const NumberDropdownPropertyLine = DropdownPropertyLine;
|
|
7042
|
+
/**
|
|
7043
|
+
* Dropdown component for string values
|
|
7044
|
+
*/
|
|
7045
|
+
const StringDropdownPropertyLine = DropdownPropertyLine;
|
|
7046
|
+
|
|
7047
|
+
/**
|
|
7048
|
+
* A slider primitive that wraps the Fluent UI Slider with step scaling, drag tracking, and optional notify-on-release behavior.
|
|
7049
|
+
* Follows the same pattern as other primitives (e.g. Switch) — no wrapper divs, just the Fluent component with logic.
|
|
7050
|
+
* @param props
|
|
7051
|
+
* @returns Slider component
|
|
7052
|
+
*/
|
|
7053
|
+
const Slider = (props) => {
|
|
7054
|
+
Slider.displayName = "Slider";
|
|
7055
|
+
const { size } = useContext(ToolContext);
|
|
7056
|
+
const [value, setValue] = useState(props.value ?? 0);
|
|
7057
|
+
const pendingValueRef = useRef(undefined);
|
|
7058
|
+
const isDraggingRef = useRef(false);
|
|
7059
|
+
// NOTE: The Fluent slider will add tick marks if the step prop is anything other than undefined.
|
|
7060
|
+
// To avoid this, we scale the min/max based on the step so we can always make step undefined.
|
|
7061
|
+
// The actual step size in the Fluent slider is 1 when it is set to undefined.
|
|
7062
|
+
const min = props.min ?? 0;
|
|
7063
|
+
const max = props.max ?? 100;
|
|
7064
|
+
const step = props.step ?? 1;
|
|
7065
|
+
useEffect(() => {
|
|
7066
|
+
!isDraggingRef.current && setValue(props.value ?? 0); // Update local state when props.value changes as long as user is not actively dragging
|
|
7067
|
+
}, [props.value]);
|
|
7068
|
+
const handleSliderChange = (_, data) => {
|
|
7069
|
+
const newValue = data.value * step;
|
|
7070
|
+
setValue(newValue);
|
|
7071
|
+
if (props.notifyOnlyOnRelease) {
|
|
7072
|
+
// Store the value but don't notify parent yet
|
|
7073
|
+
pendingValueRef.current = newValue;
|
|
7074
|
+
}
|
|
7075
|
+
else {
|
|
7076
|
+
// Notify parent as slider changes
|
|
7077
|
+
props.onChange(newValue);
|
|
7078
|
+
}
|
|
7079
|
+
};
|
|
7080
|
+
const handleSliderPointerDown = () => {
|
|
7081
|
+
isDraggingRef.current = true;
|
|
7082
|
+
};
|
|
7083
|
+
const handleSliderPointerUp = () => {
|
|
7084
|
+
if (props.notifyOnlyOnRelease && isDraggingRef.current && pendingValueRef.current !== undefined) {
|
|
7085
|
+
props.onChange(pendingValueRef.current);
|
|
7086
|
+
pendingValueRef.current = undefined;
|
|
7087
|
+
}
|
|
7088
|
+
isDraggingRef.current = false;
|
|
7089
|
+
};
|
|
7090
|
+
return (jsx(Slider$1, { className: props.className, size: size, min: min / step, max: max / step, step: undefined, value: value / step, disabled: props.disabled, onChange: handleSliderChange, onPointerDown: () => {
|
|
7091
|
+
handleSliderPointerDown();
|
|
7092
|
+
props.onPointerDown?.();
|
|
7093
|
+
}, onPointerUp: () => {
|
|
7094
|
+
handleSliderPointerUp();
|
|
7095
|
+
props.onPointerUp?.();
|
|
7096
|
+
} }));
|
|
7097
|
+
};
|
|
7098
|
+
|
|
7099
|
+
const useSyncedSliderStyles = makeStyles({
|
|
7100
|
+
container: { display: "flex", minWidth: 0 },
|
|
7101
|
+
syncedSlider: {
|
|
7102
|
+
flex: "1 1 0",
|
|
7103
|
+
flexDirection: "row",
|
|
7104
|
+
display: "flex",
|
|
7105
|
+
alignItems: "center",
|
|
7106
|
+
minWidth: 0,
|
|
7107
|
+
},
|
|
7108
|
+
slider: {
|
|
7109
|
+
flex: "1 1 auto",
|
|
7110
|
+
minWidth: "75px",
|
|
7111
|
+
maxWidth: "75px",
|
|
7112
|
+
},
|
|
7113
|
+
compactSlider: {
|
|
7114
|
+
flex: "1 1 auto",
|
|
7115
|
+
minWidth: "50px", // Allow shrinking for compact mode
|
|
7116
|
+
maxWidth: "75px",
|
|
7117
|
+
},
|
|
7118
|
+
growSlider: {
|
|
7119
|
+
flex: "1 1 auto",
|
|
7120
|
+
minWidth: "50px",
|
|
7121
|
+
// No maxWidth - slider grows to fill available space
|
|
7122
|
+
},
|
|
7123
|
+
compactSpinButton: {
|
|
7124
|
+
width: "65px",
|
|
7125
|
+
minWidth: "65px",
|
|
7126
|
+
maxWidth: "65px",
|
|
7127
|
+
},
|
|
7128
|
+
compactSpinButtonInput: {
|
|
7129
|
+
minWidth: "0",
|
|
7130
|
+
},
|
|
7131
|
+
});
|
|
7132
|
+
/**
|
|
7133
|
+
* Component which synchronizes a slider and an input field, allowing the user to change the value using either control
|
|
7134
|
+
* @param props
|
|
7135
|
+
* @returns SyncedSlider component
|
|
7136
|
+
*/
|
|
7137
|
+
const SyncedSliderInput = (props) => {
|
|
7138
|
+
SyncedSliderInput.displayName = "SyncedSliderInput";
|
|
7139
|
+
const { infoLabel, ...passthroughProps } = props;
|
|
7140
|
+
const classes = useSyncedSliderStyles();
|
|
7141
|
+
const [value, setValue] = useState(props.value ?? 0);
|
|
7142
|
+
const pendingValueRef = useRef(undefined);
|
|
7143
|
+
const isDraggingRef = useRef(false);
|
|
7144
|
+
useEffect(() => {
|
|
7145
|
+
!isDraggingRef.current && setValue(props.value ?? 0); // Update local state when props.value changes as long as user is not actively dragging
|
|
7146
|
+
}, [props.value]);
|
|
7147
|
+
const handleSliderChange = (newValue) => {
|
|
7148
|
+
setValue(newValue);
|
|
7149
|
+
if (props.notifyOnlyOnRelease) {
|
|
7150
|
+
// Store the value but don't notify parent yet
|
|
7151
|
+
pendingValueRef.current = newValue;
|
|
7152
|
+
}
|
|
7153
|
+
else {
|
|
7154
|
+
// Notify parent as slider changes
|
|
7155
|
+
props.onChange(newValue);
|
|
7156
|
+
}
|
|
7157
|
+
};
|
|
7158
|
+
const handleSliderPointerDown = () => {
|
|
7159
|
+
isDraggingRef.current = true;
|
|
7160
|
+
};
|
|
7161
|
+
const handleSliderPointerUp = () => {
|
|
7162
|
+
if (props.notifyOnlyOnRelease && isDraggingRef.current && pendingValueRef.current !== undefined) {
|
|
7163
|
+
props.onChange(pendingValueRef.current);
|
|
7164
|
+
pendingValueRef.current = undefined;
|
|
7165
|
+
}
|
|
7166
|
+
isDraggingRef.current = false;
|
|
7167
|
+
};
|
|
7168
|
+
const handleInputChange = (value) => {
|
|
7169
|
+
setValue(value);
|
|
7170
|
+
props.onChange(value); // Input always updates immediately
|
|
7171
|
+
};
|
|
7172
|
+
const hasSlider = props.min !== undefined && props.max !== undefined;
|
|
7173
|
+
// Determine Slider className based on props
|
|
7174
|
+
const getSliderClassName = () => {
|
|
7175
|
+
if (props.growSlider) {
|
|
7176
|
+
return classes.growSlider;
|
|
7177
|
+
}
|
|
7178
|
+
if (props.compact) {
|
|
7179
|
+
return classes.compactSlider;
|
|
7180
|
+
}
|
|
7181
|
+
return classes.slider;
|
|
7182
|
+
};
|
|
7183
|
+
return (jsxs("div", { className: classes.container, 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: hasSlider || props.compact ? classes.compactSpinButton : undefined, inputClassName: hasSlider || props.compact ? classes.compactSpinButtonInput : undefined, value: value, onChange: handleInputChange, step: props.step, disableDragButton: true })] })] }));
|
|
7184
|
+
};
|
|
7185
|
+
|
|
7186
|
+
/**
|
|
7187
|
+
* Renders a simple wrapper around the SyncedSliderInput
|
|
7188
|
+
* @param props
|
|
7189
|
+
* @returns
|
|
7190
|
+
*/
|
|
7191
|
+
const SyncedSliderPropertyLine = forwardRef((props, ref) => {
|
|
7192
|
+
SyncedSliderPropertyLine.displayName = "SyncedSliderPropertyLine";
|
|
7193
|
+
const { label, description, ...sliderProps } = props;
|
|
7194
|
+
return (jsx(PropertyLine, { ref: ref, ...props, children: jsx(SyncedSliderInput, { ...sliderProps }) }));
|
|
7195
|
+
});
|
|
7196
|
+
|
|
7197
|
+
const WatcherSettingDescriptor = {
|
|
7198
|
+
key: "WatcherSettings",
|
|
7199
|
+
defaultValue: {
|
|
7200
|
+
mode: "intercept",
|
|
7201
|
+
},
|
|
7202
|
+
};
|
|
7203
|
+
const WatcherServiceIdentity = Symbol("WatcherService");
|
|
7204
|
+
const WatcherServiceDefinition = {
|
|
7205
|
+
friendlyName: "Watcher Service",
|
|
7206
|
+
produces: [WatcherServiceIdentity],
|
|
7207
|
+
consumes: [SettingsStoreIdentity],
|
|
7208
|
+
factory: (settingsStore) => {
|
|
7209
|
+
let refreshObservable = null;
|
|
7210
|
+
let pollingHandle = null;
|
|
7211
|
+
const applySettings = () => {
|
|
7212
|
+
const settings = settingsStore.readSetting(WatcherSettingDescriptor);
|
|
7213
|
+
if (pollingHandle !== null) {
|
|
7214
|
+
clearInterval(pollingHandle);
|
|
7215
|
+
pollingHandle = null;
|
|
7216
|
+
}
|
|
7217
|
+
if (settings.mode === "intercept") {
|
|
7218
|
+
if (refreshObservable) {
|
|
7219
|
+
refreshObservable.clear();
|
|
7220
|
+
refreshObservable = null;
|
|
7221
|
+
}
|
|
7222
|
+
}
|
|
7223
|
+
else {
|
|
7224
|
+
const pollingObservable = refreshObservable ?? (refreshObservable = new Observable());
|
|
7225
|
+
if (settings.mode === "polling") {
|
|
7226
|
+
pollingHandle = window.setInterval(() => {
|
|
7227
|
+
pollingObservable.notifyObservers();
|
|
7228
|
+
}, settings.interval);
|
|
7229
|
+
}
|
|
7230
|
+
}
|
|
7231
|
+
};
|
|
7232
|
+
const settingsStoreObserver = settingsStore.onChanged.add((key) => {
|
|
7233
|
+
if (key === WatcherSettingDescriptor.key) {
|
|
7234
|
+
applySettings();
|
|
7235
|
+
}
|
|
7236
|
+
});
|
|
7237
|
+
applySettings();
|
|
7238
|
+
return {
|
|
7239
|
+
watchProperty(target, propertyKey, onChanged) {
|
|
7240
|
+
if (refreshObservable) {
|
|
7241
|
+
let previousValue = target[propertyKey];
|
|
7242
|
+
const observer = refreshObservable.add(() => {
|
|
7243
|
+
const currentValue = target[propertyKey];
|
|
7244
|
+
if (!Object.is(previousValue, currentValue)) {
|
|
7245
|
+
previousValue = currentValue;
|
|
7246
|
+
onChanged(currentValue);
|
|
7247
|
+
}
|
|
7248
|
+
});
|
|
7249
|
+
return {
|
|
7250
|
+
dispose: () => observer.remove(),
|
|
7251
|
+
};
|
|
7252
|
+
}
|
|
7253
|
+
else {
|
|
7254
|
+
return InterceptProperty(target, propertyKey, {
|
|
7255
|
+
afterSet: (value) => onChanged(value),
|
|
7256
|
+
});
|
|
7257
|
+
}
|
|
7258
|
+
},
|
|
7259
|
+
refresh: () => {
|
|
7260
|
+
refreshObservable?.notifyObservers();
|
|
7261
|
+
},
|
|
7262
|
+
dispose: () => {
|
|
7263
|
+
if (pollingHandle !== null) {
|
|
7264
|
+
clearInterval(pollingHandle);
|
|
7265
|
+
pollingHandle = null;
|
|
7266
|
+
}
|
|
7267
|
+
refreshObservable?.clear();
|
|
7268
|
+
refreshObservable = null;
|
|
7269
|
+
settingsStoreObserver.remove();
|
|
7270
|
+
},
|
|
7271
|
+
};
|
|
7272
|
+
},
|
|
7273
|
+
};
|
|
7274
|
+
const WatchModes = [
|
|
7275
|
+
{ label: "Interception", value: "intercept" },
|
|
7276
|
+
{ label: "Polling", value: "polling" },
|
|
7277
|
+
{ label: "Manual", value: "manual" },
|
|
7278
|
+
];
|
|
7279
|
+
const WatcherSettingsServiceDefinition = {
|
|
7280
|
+
friendlyName: "Watcher Settings Service",
|
|
7281
|
+
consumes: [SettingsServiceIdentity],
|
|
7282
|
+
factory: (settingsService) => {
|
|
7283
|
+
const settingsRegistration = settingsService.addSectionContent({
|
|
7284
|
+
key: "watcherSettings",
|
|
7285
|
+
section: "UI",
|
|
7286
|
+
component: () => {
|
|
7287
|
+
const [watcherSettings, setWatcherSettings] = useSetting(WatcherSettingDescriptor);
|
|
7288
|
+
return (jsxs(Fragment, { children: [jsx(DropdownPropertyLine, { label: "Property Watch Mode", description: `Specifies how Inspector watches entity properties for changes. "Interception" sees changes instantly, but for complex scenes can impact performance. "Polling" has less performance impact on complex scenes, but changes are only detected at the specified interval. "Manual" requires the "Refresh" button in the toolbar to be pressed.`, options: WatchModes, value: watcherSettings.mode, onChange: (value) => setWatcherSettings((prev) => {
|
|
7289
|
+
return { interval: 250, ...prev, mode: value };
|
|
7290
|
+
}) }), jsx(Collapse, { visible: watcherSettings.mode === "polling", children: jsx(SyncedSliderPropertyLine, { label: "Polling Interval", description: "A smaller polling interval will detect changes faster but may impact performance more.", min: 30, max: 1000, step: 10, unit: "ms", value: watcherSettings.mode === "polling" ? watcherSettings.interval : NaN, onChange: (value) => setWatcherSettings((prev) => {
|
|
7291
|
+
return { ...prev, interval: value };
|
|
7292
|
+
}) }) })] }));
|
|
7293
|
+
},
|
|
7294
|
+
});
|
|
7295
|
+
return {
|
|
7296
|
+
dispose: () => {
|
|
7297
|
+
settingsRegistration.dispose();
|
|
7298
|
+
},
|
|
7299
|
+
};
|
|
7300
|
+
},
|
|
7301
|
+
};
|
|
7302
|
+
const WatcherRefreshToolbarServiceDefinition = {
|
|
7303
|
+
friendlyName: "Watcher Refresh Toolbar Service",
|
|
7304
|
+
consumes: [WatcherServiceIdentity, SettingsStoreIdentity, ShellServiceIdentity],
|
|
7305
|
+
factory: (watcherService, settingsStore, shellService) => {
|
|
7306
|
+
let toolbarItemRegistration = null;
|
|
7307
|
+
const updateToolbar = () => {
|
|
7308
|
+
const settings = settingsStore.readSetting(WatcherSettingDescriptor);
|
|
7309
|
+
if (settings.mode === "manual") {
|
|
7310
|
+
if (!toolbarItemRegistration) {
|
|
7311
|
+
toolbarItemRegistration = shellService.addToolbarItem({
|
|
7312
|
+
key: "Watcher Refresh",
|
|
7313
|
+
displayName: "Refresh Properties",
|
|
7314
|
+
verticalLocation: "bottom",
|
|
7315
|
+
horizontalLocation: "right",
|
|
7316
|
+
order: 200 /* DefaultToolbarItemOrder.RefreshProperties */,
|
|
7317
|
+
teachingMoment: {
|
|
7318
|
+
title: "Refresh Properties",
|
|
7319
|
+
description: "Press this button to manually refresh all UI bound to scene state. This is only available when Property Watch Mode is set to Manual in the settings pane.",
|
|
7320
|
+
},
|
|
7321
|
+
component: () => {
|
|
7322
|
+
return (jsx(Button, { appearance: "subtle", icon: ArrowClockwiseRegular, title: "Update all UI (e.g. Scene Explorer, Properties, etc.) bound to properties of entities (Meshes, Materials, etc.)", onClick: () => watcherService.refresh() }));
|
|
7323
|
+
},
|
|
7324
|
+
});
|
|
7325
|
+
}
|
|
7326
|
+
}
|
|
7327
|
+
else {
|
|
7328
|
+
toolbarItemRegistration?.dispose();
|
|
7329
|
+
toolbarItemRegistration = null;
|
|
7330
|
+
}
|
|
7331
|
+
};
|
|
7332
|
+
updateToolbar();
|
|
7333
|
+
const settingsStoreObserver = settingsStore.onChanged.add((key) => {
|
|
7334
|
+
if (key === WatcherSettingDescriptor.key) {
|
|
7335
|
+
updateToolbar();
|
|
7336
|
+
}
|
|
7337
|
+
});
|
|
7338
|
+
return {
|
|
7339
|
+
dispose: () => {
|
|
7340
|
+
toolbarItemRegistration?.dispose();
|
|
7341
|
+
toolbarItemRegistration = null;
|
|
7342
|
+
settingsStoreObserver.remove();
|
|
7343
|
+
},
|
|
7344
|
+
};
|
|
7345
|
+
},
|
|
7346
|
+
};
|
|
7347
|
+
|
|
6902
7348
|
const GizmoServiceIdentity = Symbol("GizmoService");
|
|
6903
7349
|
const GizmoServiceDefinition = {
|
|
6904
7350
|
friendlyName: "Gizmo Service",
|
|
6905
7351
|
produces: [GizmoServiceIdentity],
|
|
6906
|
-
consumes: [SceneContextIdentity, SelectionServiceIdentity],
|
|
6907
|
-
factory: (sceneContext, selectionService) => {
|
|
7352
|
+
consumes: [SceneContextIdentity, SelectionServiceIdentity, WatcherServiceIdentity],
|
|
7353
|
+
factory: (sceneContext, selectionService, watcherService) => {
|
|
6908
7354
|
// Ref-counted utility layers, shared across consumers.
|
|
6909
7355
|
const utilityLayers = new WeakMap();
|
|
6910
7356
|
const getUtilityLayer = (scene, layer = "default") => {
|
|
@@ -7003,13 +7449,11 @@ const GizmoServiceDefinition = {
|
|
|
7003
7449
|
currentKeepDepthUtilityLayerRef = null;
|
|
7004
7450
|
};
|
|
7005
7451
|
gm.coordinatesMode = coordinatesModeState;
|
|
7006
|
-
coordinatesModeInterceptToken =
|
|
7007
|
-
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
}
|
|
7012
|
-
},
|
|
7452
|
+
coordinatesModeInterceptToken = watcherService.watchProperty(gm, "coordinatesMode", (value) => {
|
|
7453
|
+
if (value !== coordinatesModeState) {
|
|
7454
|
+
coordinatesModeState = value;
|
|
7455
|
+
coordinatesModeObservable.notifyObservers();
|
|
7456
|
+
}
|
|
7013
7457
|
});
|
|
7014
7458
|
currentGizmoManager = gm;
|
|
7015
7459
|
}
|
|
@@ -7159,146 +7603,40 @@ const GizmoServiceDefinition = {
|
|
|
7159
7603
|
sceneObserver.remove();
|
|
7160
7604
|
selectionObserver.remove();
|
|
7161
7605
|
destroyGizmoManager();
|
|
7162
|
-
gizmoModeObservable.clear();
|
|
7163
|
-
coordinatesModeObservable.clear();
|
|
7164
|
-
cameraGizmoObservable.clear();
|
|
7165
|
-
},
|
|
7166
|
-
};
|
|
7167
|
-
},
|
|
7168
|
-
};
|
|
7169
|
-
|
|
7170
|
-
const BabylonWebResources = {
|
|
7171
|
-
homepage: "https://www.babylonjs.com",
|
|
7172
|
-
repository: "https://github.com/BabylonJS/Babylon.js",
|
|
7173
|
-
bugs: "https://github.com/BabylonJS/Babylon.js/issues",
|
|
7174
|
-
};
|
|
7175
|
-
/**
|
|
7176
|
-
* Well-known default built in extensions for the Inspector.
|
|
7177
|
-
*/
|
|
7178
|
-
const DefaultInspectorExtensionFeed = new BuiltInsExtensionFeed("Inspector", [
|
|
7179
|
-
{
|
|
7180
|
-
name: "Quick Creation Tools (Preview)",
|
|
7181
|
-
description: "Adds a new panel for easy creation of various Babylon assets. This is a WIP extension...expect changes!",
|
|
7182
|
-
keywords: ["creation", "tools"],
|
|
7183
|
-
...BabylonWebResources,
|
|
7184
|
-
author: { name: "Babylon.js", forumUserName: "" },
|
|
7185
|
-
getExtensionModuleAsync: async () => await import('./quickCreateToolsService-BuEys230.js'),
|
|
7186
|
-
},
|
|
7187
|
-
{
|
|
7188
|
-
name: "Reflector",
|
|
7189
|
-
description: "Connects to the Reflector Bridge for real-time scene synchronization with the Babylon.js Sandbox.",
|
|
7190
|
-
keywords: ["reflector", "bridge", "sync", "sandbox", "tools"],
|
|
7191
|
-
...BabylonWebResources,
|
|
7192
|
-
author: { name: "Babylon.js", forumUserName: "" },
|
|
7193
|
-
getExtensionModuleAsync: async () => await import('./reflectorService-Dwsb8ijf.js'),
|
|
7194
|
-
},
|
|
7195
|
-
]);
|
|
7196
|
-
|
|
7197
|
-
const useSyncedSliderStyles = makeStyles({
|
|
7198
|
-
container: { display: "flex", minWidth: 0 },
|
|
7199
|
-
syncedSlider: {
|
|
7200
|
-
flex: "1 1 0",
|
|
7201
|
-
flexDirection: "row",
|
|
7202
|
-
display: "flex",
|
|
7203
|
-
alignItems: "center",
|
|
7204
|
-
minWidth: 0,
|
|
7205
|
-
},
|
|
7206
|
-
slider: {
|
|
7207
|
-
flex: "1 1 auto",
|
|
7208
|
-
minWidth: "75px",
|
|
7209
|
-
maxWidth: "75px",
|
|
7210
|
-
},
|
|
7211
|
-
compactSlider: {
|
|
7212
|
-
flex: "1 1 auto",
|
|
7213
|
-
minWidth: "50px", // Allow shrinking for compact mode
|
|
7214
|
-
maxWidth: "75px",
|
|
7215
|
-
},
|
|
7216
|
-
growSlider: {
|
|
7217
|
-
flex: "1 1 auto",
|
|
7218
|
-
minWidth: "50px",
|
|
7219
|
-
// No maxWidth - slider grows to fill available space
|
|
7220
|
-
},
|
|
7221
|
-
compactSpinButton: {
|
|
7222
|
-
width: "65px",
|
|
7223
|
-
minWidth: "65px",
|
|
7224
|
-
maxWidth: "65px",
|
|
7225
|
-
},
|
|
7226
|
-
compactSpinButtonInput: {
|
|
7227
|
-
minWidth: "0",
|
|
7228
|
-
},
|
|
7229
|
-
});
|
|
7230
|
-
/**
|
|
7231
|
-
* Component which synchronizes a slider and an input field, allowing the user to change the value using either control
|
|
7232
|
-
* @param props
|
|
7233
|
-
* @returns SyncedSlider component
|
|
7234
|
-
*/
|
|
7235
|
-
const SyncedSliderInput = (props) => {
|
|
7236
|
-
SyncedSliderInput.displayName = "SyncedSliderInput";
|
|
7237
|
-
const { infoLabel, ...passthroughProps } = props;
|
|
7238
|
-
const classes = useSyncedSliderStyles();
|
|
7239
|
-
const { size } = useContext(ToolContext);
|
|
7240
|
-
const [value, setValue] = useState(props.value ?? 0);
|
|
7241
|
-
const pendingValueRef = useRef(undefined);
|
|
7242
|
-
const isDraggingRef = useRef(false);
|
|
7243
|
-
// NOTE: The Fluent slider will add tick marks if the step prop is anything other than undefined.
|
|
7244
|
-
// To avoid this, we scale the min/max based on the step so we can always make step undefined.
|
|
7245
|
-
// The actual step size in the Fluent slider is 1 when it is ste to undefined.
|
|
7246
|
-
const min = props.min ?? 0;
|
|
7247
|
-
const max = props.max ?? 100;
|
|
7248
|
-
const step = props.step ?? 1;
|
|
7249
|
-
useEffect(() => {
|
|
7250
|
-
!isDraggingRef.current && setValue(props.value ?? 0); // Update local state when props.value changes as long as user is not actively dragging
|
|
7251
|
-
}, [props.value]);
|
|
7252
|
-
const handleSliderChange = (_, data) => {
|
|
7253
|
-
const newValue = data.value * step;
|
|
7254
|
-
setValue(newValue);
|
|
7255
|
-
if (props.notifyOnlyOnRelease) {
|
|
7256
|
-
// Store the value but don't notify parent yet
|
|
7257
|
-
pendingValueRef.current = newValue;
|
|
7258
|
-
}
|
|
7259
|
-
else {
|
|
7260
|
-
// Notify parent as slider changes
|
|
7261
|
-
props.onChange(newValue);
|
|
7262
|
-
}
|
|
7263
|
-
};
|
|
7264
|
-
const handleSliderPointerDown = () => {
|
|
7265
|
-
isDraggingRef.current = true;
|
|
7266
|
-
};
|
|
7267
|
-
const handleSliderPointerUp = () => {
|
|
7268
|
-
if (props.notifyOnlyOnRelease && isDraggingRef.current && pendingValueRef.current !== undefined) {
|
|
7269
|
-
props.onChange(pendingValueRef.current);
|
|
7270
|
-
pendingValueRef.current = undefined;
|
|
7271
|
-
}
|
|
7272
|
-
isDraggingRef.current = false;
|
|
7273
|
-
};
|
|
7274
|
-
const handleInputChange = (value) => {
|
|
7275
|
-
setValue(value);
|
|
7276
|
-
props.onChange(value); // Input always updates immediately
|
|
7277
|
-
};
|
|
7278
|
-
const hasSlider = props.min !== undefined && props.max !== undefined;
|
|
7279
|
-
// Determine Slider className based on props
|
|
7280
|
-
const getSliderClassName = () => {
|
|
7281
|
-
if (props.growSlider) {
|
|
7282
|
-
return classes.growSlider;
|
|
7283
|
-
}
|
|
7284
|
-
if (props.compact) {
|
|
7285
|
-
return classes.compactSlider;
|
|
7286
|
-
}
|
|
7287
|
-
return classes.slider;
|
|
7288
|
-
};
|
|
7289
|
-
return (jsxs("div", { className: classes.container, children: [infoLabel && jsx(InfoLabel, { ...infoLabel, htmlFor: "syncedSlider" }), jsxs("div", { id: "syncedSlider", className: classes.syncedSlider, children: [hasSlider && (jsx(Slider, { ...passthroughProps, className: getSliderClassName(), size: size, min: min / step, max: max / step, step: undefined, value: value / step, onChange: handleSliderChange, onPointerDown: handleSliderPointerDown, onPointerUp: handleSliderPointerUp })), jsx(SpinButton, { ...passthroughProps, className: hasSlider || props.compact ? classes.compactSpinButton : undefined, inputClassName: hasSlider || props.compact ? classes.compactSpinButtonInput : undefined, value: value, onChange: handleInputChange, step: props.step })] })] }));
|
|
7606
|
+
gizmoModeObservable.clear();
|
|
7607
|
+
coordinatesModeObservable.clear();
|
|
7608
|
+
cameraGizmoObservable.clear();
|
|
7609
|
+
},
|
|
7610
|
+
};
|
|
7611
|
+
},
|
|
7290
7612
|
};
|
|
7291
7613
|
|
|
7614
|
+
const BabylonWebResources = {
|
|
7615
|
+
homepage: "https://www.babylonjs.com",
|
|
7616
|
+
repository: "https://github.com/BabylonJS/Babylon.js",
|
|
7617
|
+
bugs: "https://github.com/BabylonJS/Babylon.js/issues",
|
|
7618
|
+
};
|
|
7292
7619
|
/**
|
|
7293
|
-
*
|
|
7294
|
-
* @param props
|
|
7295
|
-
* @returns
|
|
7620
|
+
* Well-known default built in extensions for the Inspector.
|
|
7296
7621
|
*/
|
|
7297
|
-
const
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
|
|
7301
|
-
|
|
7622
|
+
const DefaultInspectorExtensionFeed = new BuiltInsExtensionFeed("Inspector", [
|
|
7623
|
+
{
|
|
7624
|
+
name: "Quick Creation Tools (Preview)",
|
|
7625
|
+
description: "Adds a new panel for easy creation of various Babylon assets. This is a WIP extension...expect changes!",
|
|
7626
|
+
keywords: ["creation", "tools"],
|
|
7627
|
+
...BabylonWebResources,
|
|
7628
|
+
author: { name: "Babylon.js", forumUserName: "" },
|
|
7629
|
+
getExtensionModuleAsync: async () => await import('./quickCreateToolsService-DHfs_EZ6.js'),
|
|
7630
|
+
},
|
|
7631
|
+
{
|
|
7632
|
+
name: "Reflector",
|
|
7633
|
+
description: "Connects to the Reflector Bridge for real-time scene synchronization with the Babylon.js Sandbox.",
|
|
7634
|
+
keywords: ["reflector", "bridge", "sync", "sandbox", "tools"],
|
|
7635
|
+
...BabylonWebResources,
|
|
7636
|
+
author: { name: "Babylon.js", forumUserName: "" },
|
|
7637
|
+
getExtensionModuleAsync: async () => await import('./reflectorService-DEPuGTAZ.js'),
|
|
7638
|
+
},
|
|
7639
|
+
]);
|
|
7302
7640
|
|
|
7303
7641
|
/**
|
|
7304
7642
|
* Reusable component which renders a color property line containing a label, colorPicker popout, and expandable RGBA values
|
|
@@ -7334,30 +7672,6 @@ const ColorSliders = ({ color, onSliderChange }) => (jsxs(Fragment, { children:
|
|
|
7334
7672
|
const Color3PropertyLine = ColorPropertyLine;
|
|
7335
7673
|
const Color4PropertyLine = ColorPropertyLine;
|
|
7336
7674
|
|
|
7337
|
-
const useStyles$F = makeStyles({
|
|
7338
|
-
dropdown: {
|
|
7339
|
-
...UniformWidthStyling,
|
|
7340
|
-
},
|
|
7341
|
-
});
|
|
7342
|
-
/**
|
|
7343
|
-
* Wraps a dropdown in a property line
|
|
7344
|
-
* @param props - PropertyLineProps and DropdownProps
|
|
7345
|
-
* @returns property-line wrapped dropdown
|
|
7346
|
-
*/
|
|
7347
|
-
const DropdownPropertyLine = forwardRef((props, ref) => {
|
|
7348
|
-
DropdownPropertyLine.displayName = "DropdownPropertyLine";
|
|
7349
|
-
const classes = useStyles$F();
|
|
7350
|
-
return (jsx(PropertyLine, { ...props, ref: ref, children: jsx(Dropdown, { ...props, className: classes.dropdown }) }));
|
|
7351
|
-
});
|
|
7352
|
-
/**
|
|
7353
|
-
* Dropdown component for number values.
|
|
7354
|
-
*/
|
|
7355
|
-
const NumberDropdownPropertyLine = DropdownPropertyLine;
|
|
7356
|
-
/**
|
|
7357
|
-
* Dropdown component for string values
|
|
7358
|
-
*/
|
|
7359
|
-
const StringDropdownPropertyLine = DropdownPropertyLine;
|
|
7360
|
-
|
|
7361
7675
|
/**
|
|
7362
7676
|
* Wraps a text input in a property line
|
|
7363
7677
|
* @param props - PropertyLineProps and InputProps
|
|
@@ -7402,21 +7716,21 @@ const TensorPropertyLine = (props) => {
|
|
|
7402
7716
|
useEffect(() => {
|
|
7403
7717
|
setVector(props.value);
|
|
7404
7718
|
}, [props.value, props.expandedContent]);
|
|
7405
|
-
return (jsx(PropertyLine, { ...props, expandedContent: vector ? jsx(VectorSliders, { vector: vector, min: min, max: max, unit: props.unit, step: props.step, converted: converted, onChange: onChange }) : undefined, children: jsx(Body1, { children: `[${formatted(props.value.x)}, ${formatted(props.value.y)}${HasZ(props.value) ? `, ${formatted(props.value.z)}` : ""}${HasW(props.value) ? `, ${formatted(props.value.w)}` : ""}]` }) }));
|
|
7719
|
+
return (jsx(PropertyLine, { ...props, expandedContent: jsxs(Fragment, { children: [props.expandedContent, vector ? (jsx(VectorSliders, { vector: vector, min: min, max: max, unit: props.unit, step: props.step, precision: props.precision, converted: converted, onChange: onChange })) : undefined] }), children: jsx(Body1, { children: `[${formatted(props.value.x)}, ${formatted(props.value.y)}${HasZ(props.value) ? `, ${formatted(props.value.z)}` : ""}${HasW(props.value) ? `, ${formatted(props.value.w)}` : ""}]` }) }));
|
|
7406
7720
|
};
|
|
7407
|
-
const VectorSliders = ({ vector, min, max, unit, step, converted, onChange }) => (jsxs(Fragment, { children: [jsx(
|
|
7721
|
+
const VectorSliders = ({ vector, min, max, unit, step, precision, converted, onChange }) => (jsxs(Fragment, { children: [jsx(NumberInputPropertyLine, { label: "X", value: converted(vector.x), min: min, max: max, onChange: (val) => onChange(val, "x"), unit: unit, step: step, precision: precision }), jsx(NumberInputPropertyLine, { label: "Y", value: converted(vector.y), min: min, max: max, onChange: (val) => onChange(val, "y"), unit: unit, step: step, precision: precision }), HasZ(vector) && (jsx(NumberInputPropertyLine, { label: "Z", value: converted(vector.z), min: min, max: max, onChange: (val) => onChange(val, "z"), unit: unit, step: step, precision: precision })), HasW(vector) && (jsx(NumberInputPropertyLine, { label: "W", value: converted(vector.w), min: min, max: max, onChange: (val) => onChange(val, "w"), unit: unit, step: step, precision: precision }))] }));
|
|
7408
7722
|
const ToDegreesConverter = { from: Tools.ToDegrees, to: Tools.ToRadians };
|
|
7409
7723
|
const RotationVectorPropertyLine = (props) => {
|
|
7410
7724
|
RotationVectorPropertyLine.displayName = "RotationVectorPropertyLine";
|
|
7411
|
-
const
|
|
7412
|
-
const
|
|
7413
|
-
return (jsx(Vector3PropertyLine, { ...props, unit: props.useDegrees ? "
|
|
7725
|
+
const step = props.useDegrees ? 1 : 0.01;
|
|
7726
|
+
const precision = props.useDegrees ? 1 : 2;
|
|
7727
|
+
return (jsx(Vector3PropertyLine, { ...props, unit: props.useDegrees ? "°" : "rad", valueConverter: props.useDegrees ? ToDegreesConverter : undefined, step: step, precision: precision }));
|
|
7414
7728
|
};
|
|
7415
7729
|
const QuaternionPropertyLineInternal = TensorPropertyLine;
|
|
7416
7730
|
const QuaternionPropertyLine = (props) => {
|
|
7417
7731
|
QuaternionPropertyLine.displayName = "QuaternionPropertyLine";
|
|
7418
|
-
const
|
|
7419
|
-
const
|
|
7732
|
+
const step = props.useDegrees ? 1 : 0.01;
|
|
7733
|
+
const precision = props.useDegrees ? 1 : 2;
|
|
7420
7734
|
const [quat, setQuat] = useState(props.value);
|
|
7421
7735
|
useEffect(() => {
|
|
7422
7736
|
setQuat(props.value);
|
|
@@ -7431,7 +7745,7 @@ const QuaternionPropertyLine = (props) => {
|
|
|
7431
7745
|
const quat = Quaternion.FromEulerAngles(val.x, val.y, val.z);
|
|
7432
7746
|
onQuatChange(quat);
|
|
7433
7747
|
};
|
|
7434
|
-
return useEuler ? (jsx(Vector3PropertyLine, { ...restProps, nullable: false, ignoreNullable: false, value: quat.toEulerAngles(), valueConverter: ToDegreesConverter,
|
|
7748
|
+
return useEuler ? (jsx(Vector3PropertyLine, { ...restProps, nullable: false, ignoreNullable: false, value: quat.toEulerAngles(), valueConverter: ToDegreesConverter, onChange: onEulerChange, unit: props.useDegrees ? "°" : "rad", step: step, precision: precision, expandedContent: jsx(TextPropertyLine, { label: "Quaternion", value: `[${quat.x.toFixed(4)}, ${quat.y.toFixed(4)}, ${quat.z.toFixed(4)}, ${quat.w.toFixed(4)}]` }) })) : (jsx(QuaternionPropertyLineInternal, { ...props, nullable: false, value: quat, onChange: onQuatChange, unit: props.useDegrees ? "°" : "rad", step: step, precision: precision }));
|
|
7435
7749
|
};
|
|
7436
7750
|
const Vector2PropertyLine = TensorPropertyLine;
|
|
7437
7751
|
const Vector3PropertyLine = TensorPropertyLine;
|
|
@@ -7946,7 +8260,7 @@ const ThemeSelectorServiceDefinition = {
|
|
|
7946
8260
|
key: "ThemeSelector",
|
|
7947
8261
|
horizontalLocation: "right",
|
|
7948
8262
|
verticalLocation: "top",
|
|
7949
|
-
|
|
8263
|
+
teachingMoment: false,
|
|
7950
8264
|
order: -300,
|
|
7951
8265
|
component: () => {
|
|
7952
8266
|
const classes = useStyles$E();
|
|
@@ -8011,7 +8325,7 @@ function MakeModularTool(options) {
|
|
|
8011
8325
|
const [requiredExtensions, setRequiredExtensions] = useState();
|
|
8012
8326
|
const [requiredExtensionsDeferred, setRequiredExtensionsDeferred] = useState();
|
|
8013
8327
|
const [extensionInstallError, setExtensionInstallError] = useState();
|
|
8014
|
-
const [
|
|
8328
|
+
const [bootstrapServices, setBootstrapServices] = useState();
|
|
8015
8329
|
// This is the main async initialization.
|
|
8016
8330
|
useEffect(() => {
|
|
8017
8331
|
const initializeExtensionManagerAsync = async () => {
|
|
@@ -8022,17 +8336,22 @@ function MakeModularTool(options) {
|
|
|
8022
8336
|
produces: [SettingsStoreIdentity],
|
|
8023
8337
|
factory: () => settingsStore,
|
|
8024
8338
|
});
|
|
8339
|
+
// Register watcher service early since many other services will rely on it.
|
|
8340
|
+
// TODO: Really this should be in the Inspector layer, but we would need a way
|
|
8341
|
+
// to setup the WatcherContext.Provider before the root component is rendered
|
|
8342
|
+
// for that to work, since components will use the WatcherContext.
|
|
8343
|
+
await serviceContainer.addServiceAsync(WatcherServiceDefinition);
|
|
8025
8344
|
// Register the shell service (top level toolbar/side pane UI layout).
|
|
8026
8345
|
await serviceContainer.addServiceAsync(MakeShellServiceDefinition(options));
|
|
8027
|
-
// Register a service that simply consumes the
|
|
8346
|
+
// Register a service that simply consumes the services we need before first render.
|
|
8028
8347
|
await serviceContainer.addServiceAsync({
|
|
8029
|
-
friendlyName: "
|
|
8030
|
-
consumes: [RootComponentServiceIdentity],
|
|
8031
|
-
factory: (rootComponentService) => {
|
|
8348
|
+
friendlyName: "Service Bootstrapper",
|
|
8349
|
+
consumes: [RootComponentServiceIdentity, WatcherServiceIdentity],
|
|
8350
|
+
factory: (rootComponentService, watcherService) => {
|
|
8032
8351
|
// Use function syntax for the state setter since the root component may be a function component.
|
|
8033
|
-
|
|
8352
|
+
setBootstrapServices({ rootComponentService, watcherService });
|
|
8034
8353
|
return {
|
|
8035
|
-
dispose: () =>
|
|
8354
|
+
dispose: () => setBootstrapServices(undefined),
|
|
8036
8355
|
};
|
|
8037
8356
|
},
|
|
8038
8357
|
});
|
|
@@ -8044,7 +8363,7 @@ function MakeModularTool(options) {
|
|
|
8044
8363
|
}
|
|
8045
8364
|
// Register the extension list service (for browsing/installing extensions) if extension feeds are provided.
|
|
8046
8365
|
if (extensionFeeds.length > 0) {
|
|
8047
|
-
const { ExtensionListServiceDefinition } = await import('./extensionsListService-
|
|
8366
|
+
const { ExtensionListServiceDefinition } = await import('./extensionsListService-Duej2zkq.js');
|
|
8048
8367
|
await serviceContainer.addServiceAsync(ExtensionListServiceDefinition);
|
|
8049
8368
|
}
|
|
8050
8369
|
// Register all external services (that make up a unique tool).
|
|
@@ -8111,12 +8430,17 @@ function MakeModularTool(options) {
|
|
|
8111
8430
|
setExtensionInstallError(undefined);
|
|
8112
8431
|
}, [setExtensionInstallError]);
|
|
8113
8432
|
// Show a spinner until a main view has been set.
|
|
8114
|
-
|
|
8115
|
-
|
|
8116
|
-
|
|
8117
|
-
|
|
8118
|
-
|
|
8119
|
-
|
|
8433
|
+
if (!bootstrapServices) {
|
|
8434
|
+
return (jsx(SettingsStoreContext.Provider, { value: settingsStore, children: jsx(Theme, { className: classes.app, children: jsx(Spinner, { className: classes.spinner }) }) }));
|
|
8435
|
+
}
|
|
8436
|
+
else {
|
|
8437
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
8438
|
+
const Content = bootstrapServices.rootComponentService.rootComponent;
|
|
8439
|
+
return (
|
|
8440
|
+
// Expose the settings store as a React context so that UI components can read/write
|
|
8441
|
+
// settings without the ISettingsService needing to be explicitly passed around.
|
|
8442
|
+
jsx(SettingsStoreContext.Provider, { value: settingsStore, children: jsx(WatcherContext.Provider, { value: bootstrapServices.watcherService, children: jsx(ExtensionManagerContext.Provider, { value: extensionManagerContext, children: jsx(Theme, { className: classes.app, children: jsxs(ToastProvider, { children: [jsx(Dialog, { open: !!requiredExtensions, modalType: "alert", children: jsx(DialogSurface, { children: jsxs(DialogBody, { children: [jsx(DialogTitle, { children: "Required Extensions" }), jsxs(DialogContent, { children: ["Opening this URL requires the following extensions to be installed and enabled:", jsx("ul", { children: requiredExtensions?.map((name) => jsx("li", { children: name }, name)) })] }), jsxs(DialogActions, { children: [jsx(Button$1, { appearance: "primary", onClick: onAcceptRequiredExtensions, children: "Install & Enable" }), jsx(Button$1, { appearance: "secondary", onClick: onRejectRequiredExtensions, children: "No Thanks" })] })] }) }) }), jsx(Dialog, { open: !!extensionInstallError, modalType: "alert", children: jsx(DialogSurface, { children: jsxs(DialogBody, { children: [jsx(DialogTitle, { children: jsxs("div", { className: classes.extensionErrorTitleDiv, children: ["Extension Install Error", jsx(ErrorCircleRegular, { className: classes.extensionErrorIcon })] }) }), jsx(DialogContent, { children: jsxs(List$1, { children: [jsx(ListItem, { children: jsx(Body1, { children: `Extension "${extensionInstallError?.extension.name}" failed to install and was removed.` }) }), jsx(ListItem, { children: jsx(Body1, { children: `${extensionInstallError?.error}` }) })] }) }), jsx(DialogActions, { children: jsx(Button$1, { appearance: "primary", onClick: onAcknowledgedExtensionInstallError, children: "Close" }) })] }) }) }), jsx(Suspense, { fallback: jsx(Spinner, { className: classes.spinner }), children: jsx(Content, {}) })] }) }) }) }) }));
|
|
8443
|
+
}
|
|
8120
8444
|
};
|
|
8121
8445
|
// Set the container element to be a flex container so that the tool can be displayed properly.
|
|
8122
8446
|
const originalContainerElementDisplay = containerElement.style.display;
|
|
@@ -8195,12 +8519,103 @@ const GizmoToolbarServiceDefinition = {
|
|
|
8195
8519
|
key: "Gizmo Toolbar",
|
|
8196
8520
|
verticalLocation: "top",
|
|
8197
8521
|
horizontalLocation: "left",
|
|
8198
|
-
|
|
8522
|
+
teachingMoment: false,
|
|
8199
8523
|
component: () => jsx(GizmoToolbar, { gizmoService: gizmoService, sceneContext: sceneContext }),
|
|
8200
8524
|
});
|
|
8201
8525
|
},
|
|
8202
8526
|
};
|
|
8203
8527
|
|
|
8528
|
+
const HighlightSelectedEntitySettingDescriptor = {
|
|
8529
|
+
key: "HighlightSelectedEntity",
|
|
8530
|
+
defaultValue: true,
|
|
8531
|
+
};
|
|
8532
|
+
const HighlightServiceDefinition = {
|
|
8533
|
+
friendlyName: "Highlight Service",
|
|
8534
|
+
consumes: [SelectionServiceIdentity, SceneContextIdentity, SettingsStoreIdentity, ThemeServiceIdentity, GizmoServiceIdentity],
|
|
8535
|
+
factory: (selectionService, sceneContext, settingsStore, themeService, gizmoService) => {
|
|
8536
|
+
let outlineLayer = null;
|
|
8537
|
+
let utilityLayer = null;
|
|
8538
|
+
let currentScene = null;
|
|
8539
|
+
let activeCameraObserver = null;
|
|
8540
|
+
function disposeOutlineLayer() {
|
|
8541
|
+
outlineLayer?.dispose();
|
|
8542
|
+
outlineLayer = null;
|
|
8543
|
+
utilityLayer?.dispose();
|
|
8544
|
+
utilityLayer = null;
|
|
8545
|
+
currentScene = null;
|
|
8546
|
+
}
|
|
8547
|
+
function getOrCreateOutlineLayer(scene) {
|
|
8548
|
+
if (!outlineLayer || currentScene !== scene) {
|
|
8549
|
+
disposeOutlineLayer();
|
|
8550
|
+
utilityLayer = gizmoService.getUtilityLayer(scene);
|
|
8551
|
+
outlineLayer = new SelectionOutlineLayer("InspectorSelectionOutline", utilityLayer.value.utilityLayerScene);
|
|
8552
|
+
updateColor(outlineLayer);
|
|
8553
|
+
currentScene = scene;
|
|
8554
|
+
}
|
|
8555
|
+
return outlineLayer;
|
|
8556
|
+
}
|
|
8557
|
+
function updateColor(outlineLayer) {
|
|
8558
|
+
outlineLayer.outlineColor = Color3.FromHexString(themeService.theme.colorBrandForeground1);
|
|
8559
|
+
}
|
|
8560
|
+
function updateHighlight() {
|
|
8561
|
+
const scene = sceneContext.currentScene;
|
|
8562
|
+
const entity = selectionService.selectedEntity instanceof AbstractMesh && !(selectionService.selectedEntity instanceof GaussianSplattingMesh)
|
|
8563
|
+
? selectionService.selectedEntity
|
|
8564
|
+
: null;
|
|
8565
|
+
if (!entity || !settingsStore.readSetting(HighlightSelectedEntitySettingDescriptor) || !scene || !scene.activeCamera) {
|
|
8566
|
+
disposeOutlineLayer();
|
|
8567
|
+
return;
|
|
8568
|
+
}
|
|
8569
|
+
const layer = getOrCreateOutlineLayer(scene);
|
|
8570
|
+
layer.clearSelection();
|
|
8571
|
+
layer.addSelection(entity);
|
|
8572
|
+
}
|
|
8573
|
+
function watchActiveCamera(scene) {
|
|
8574
|
+
activeCameraObserver?.remove();
|
|
8575
|
+
activeCameraObserver = null;
|
|
8576
|
+
if (scene) {
|
|
8577
|
+
activeCameraObserver = scene.onActiveCameraChanged.add(updateHighlight);
|
|
8578
|
+
}
|
|
8579
|
+
}
|
|
8580
|
+
// React to theme changes.
|
|
8581
|
+
const themeObserver = themeService.onChanged.add(() => {
|
|
8582
|
+
if (outlineLayer) {
|
|
8583
|
+
updateColor(outlineLayer);
|
|
8584
|
+
}
|
|
8585
|
+
});
|
|
8586
|
+
// React to selection changes.
|
|
8587
|
+
const selectionObserver = selectionService.onSelectedEntityChanged.add(updateHighlight);
|
|
8588
|
+
// React to scene changes.
|
|
8589
|
+
const sceneObserver = sceneContext.currentSceneObservable.add(() => {
|
|
8590
|
+
// Dispose the old layer when the scene changes.
|
|
8591
|
+
disposeOutlineLayer();
|
|
8592
|
+
watchActiveCamera(sceneContext.currentScene);
|
|
8593
|
+
updateHighlight();
|
|
8594
|
+
});
|
|
8595
|
+
// React to setting changes.
|
|
8596
|
+
const settingObserver = settingsStore.onChanged.add((setting) => {
|
|
8597
|
+
if (setting === HighlightSelectedEntitySettingDescriptor.key) {
|
|
8598
|
+
updateHighlight();
|
|
8599
|
+
}
|
|
8600
|
+
});
|
|
8601
|
+
// Watch active camera on the initial scene.
|
|
8602
|
+
watchActiveCamera(sceneContext.currentScene);
|
|
8603
|
+
// Initial update.
|
|
8604
|
+
updateHighlight();
|
|
8605
|
+
return {
|
|
8606
|
+
dispose: () => {
|
|
8607
|
+
themeObserver.remove();
|
|
8608
|
+
selectionObserver.remove();
|
|
8609
|
+
sceneObserver.remove();
|
|
8610
|
+
settingObserver.remove();
|
|
8611
|
+
activeCameraObserver?.remove();
|
|
8612
|
+
activeCameraObserver = null;
|
|
8613
|
+
disposeOutlineLayer();
|
|
8614
|
+
},
|
|
8615
|
+
};
|
|
8616
|
+
},
|
|
8617
|
+
};
|
|
8618
|
+
|
|
8204
8619
|
const useStyles$B = makeStyles({
|
|
8205
8620
|
badge: {
|
|
8206
8621
|
margin: tokens.spacingHorizontalXXS,
|
|
@@ -8215,7 +8630,8 @@ const MiniStatsServiceDefinition = {
|
|
|
8215
8630
|
key: "Mini Stats",
|
|
8216
8631
|
verticalLocation: "bottom",
|
|
8217
8632
|
horizontalLocation: "right",
|
|
8218
|
-
|
|
8633
|
+
order: 300 /* DefaultToolbarItemOrder.FrameRate */,
|
|
8634
|
+
teachingMoment: false,
|
|
8219
8635
|
component: () => {
|
|
8220
8636
|
const classes = useStyles$B();
|
|
8221
8637
|
const scene = useObservableState(useCallback(() => sceneContext.currentScene, [sceneContext.currentScene]), sceneContext.currentSceneObservable);
|
|
@@ -8245,8 +8661,7 @@ const CurveEditorProvider = (props) => {
|
|
|
8245
8661
|
const [activeAnimations, setActiveAnimations] = useState([]);
|
|
8246
8662
|
const [activeChannels, setActiveChannels] = useState({});
|
|
8247
8663
|
const [activeKeyPoints, setActiveKeyPoints] = useState(null);
|
|
8248
|
-
|
|
8249
|
-
const [mainKeyPoint, _setMainKeyPoint] = useState(null);
|
|
8664
|
+
const [mainKeyPoint, setMainKeyPoint] = useState(null);
|
|
8250
8665
|
const [activeFrame, setActiveFrame] = useState(0);
|
|
8251
8666
|
const [fromKey, setFromKey] = useState(0);
|
|
8252
8667
|
const [toKey, setToKey] = useState(100);
|
|
@@ -8498,6 +8913,7 @@ const CurveEditorProvider = (props) => {
|
|
|
8498
8913
|
setReferenceMaxFrame,
|
|
8499
8914
|
setFocusedInput,
|
|
8500
8915
|
setActiveKeyPoints,
|
|
8916
|
+
setMainKeyPoint,
|
|
8501
8917
|
setActiveChannels,
|
|
8502
8918
|
play,
|
|
8503
8919
|
stop,
|
|
@@ -8616,12 +9032,16 @@ const TopBar = () => {
|
|
|
8616
9032
|
});
|
|
8617
9033
|
const onActiveKeyPointChangedObserver = observables.onActiveKeyPointChanged.add(() => {
|
|
8618
9034
|
const numKeys = state.activeKeyPoints?.length || 0;
|
|
8619
|
-
|
|
8620
|
-
const
|
|
8621
|
-
const frameEnabled = (numKeys === 1 && numAnims === 1) || (numKeys > 1 && numAnims > 1);
|
|
9035
|
+
const numAnims = state.activeKeyPoints ? new Set(state.activeKeyPoints.map((kp) => kp.curve.animation.uniqueId)).size : 0;
|
|
9036
|
+
const hasActiveQuaternion = state.activeKeyPoints?.some((kp) => kp.curve.animation.dataType === Animation.ANIMATIONTYPE_QUATERNION) ?? false;
|
|
9037
|
+
const frameEnabled = ((numKeys === 1 && numAnims === 1) || (numKeys > 1 && numAnims > 1)) && !hasActiveQuaternion;
|
|
8622
9038
|
setFrameControlEnabled(frameEnabled);
|
|
8623
|
-
setValueControlEnabled(numKeys > 0);
|
|
8624
|
-
//
|
|
9039
|
+
setValueControlEnabled(numKeys > 0 && !hasActiveQuaternion);
|
|
9040
|
+
// Reset values when no keys are selected
|
|
9041
|
+
if (numKeys === 0) {
|
|
9042
|
+
setKeyFrameValue(null);
|
|
9043
|
+
setKeyValue(null);
|
|
9044
|
+
}
|
|
8625
9045
|
});
|
|
8626
9046
|
return () => {
|
|
8627
9047
|
observables.onFrameSet.remove(onFrameSetObserver);
|
|
@@ -8938,6 +9358,7 @@ const useStyles$x = makeStyles({
|
|
|
8938
9358
|
display: "flex",
|
|
8939
9359
|
flexDirection: "column",
|
|
8940
9360
|
gap: tokens.spacingVerticalM,
|
|
9361
|
+
minWidth: "150px",
|
|
8941
9362
|
},
|
|
8942
9363
|
header: {
|
|
8943
9364
|
display: "flex",
|
|
@@ -8973,13 +9394,16 @@ const useStyles$x = makeStyles({
|
|
|
8973
9394
|
const ANIMATION_TYPES = ["Float", "Vector2", "Vector3", "Quaternion", "Color3", "Color4"];
|
|
8974
9395
|
const LOOP_MODES = ["Cycle", "Relative", "Relative from current", "Constant"];
|
|
8975
9396
|
const MODES = ["List", "Custom"];
|
|
9397
|
+
const ModeOptions = MODES.map((m) => ({ label: m, value: m }));
|
|
9398
|
+
const AnimationTypeOptions = ANIMATION_TYPES.map((t) => ({ label: t, value: t }));
|
|
9399
|
+
const LoopModeOptions = LOOP_MODES.map((lm) => ({ label: lm, value: lm }));
|
|
8976
9400
|
/**
|
|
8977
9401
|
* Panel for adding new animations
|
|
8978
9402
|
* @returns The add animation panel component
|
|
8979
9403
|
*/
|
|
8980
9404
|
const AddAnimationPanel = ({ onClose }) => {
|
|
8981
9405
|
const styles = useStyles$x();
|
|
8982
|
-
const { state, observables } = useCurveEditor();
|
|
9406
|
+
const { state, actions, observables } = useCurveEditor();
|
|
8983
9407
|
const [name, setName] = useState("");
|
|
8984
9408
|
const [mode, setMode] = useState("List");
|
|
8985
9409
|
const [customProperty, setCustomProperty] = useState("");
|
|
@@ -9184,11 +9608,15 @@ const AddAnimationPanel = ({ onClose }) => {
|
|
|
9184
9608
|
state.target.animations = [...state.target.animations, animation];
|
|
9185
9609
|
}
|
|
9186
9610
|
}
|
|
9187
|
-
//
|
|
9611
|
+
// Auto-select the newly created animation
|
|
9612
|
+
actions.setActiveAnimations([animation]);
|
|
9613
|
+
actions.resetAllActiveChannels();
|
|
9614
|
+
// Close panel, then notify so listeners (e.g. AnimationList) can react
|
|
9188
9615
|
onClose();
|
|
9189
9616
|
observables.onAnimationsLoaded.notifyObservers();
|
|
9190
|
-
|
|
9191
|
-
|
|
9617
|
+
observables.onActiveAnimationChanged.notifyObservers({});
|
|
9618
|
+
}, [name, currentProperty, currentType, loopMode, fps, minFrame, maxFrame, state.target, actions, observables, onClose]);
|
|
9619
|
+
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" })] })] }));
|
|
9192
9620
|
};
|
|
9193
9621
|
|
|
9194
9622
|
const useStyles$w = makeStyles({
|
|
@@ -9616,6 +10044,7 @@ class CurveData {
|
|
|
9616
10044
|
constructor(color, animation, property, tangentBuilder, setDefaultInTangent, setDefaultOutTangent) {
|
|
9617
10045
|
this.keys = [];
|
|
9618
10046
|
this.onDataUpdatedObservable = new Observable();
|
|
10047
|
+
this.siblings = [];
|
|
9619
10048
|
this.color = color;
|
|
9620
10049
|
this.animation = animation;
|
|
9621
10050
|
this.property = property;
|
|
@@ -9809,6 +10238,13 @@ class CurveData {
|
|
|
9809
10238
|
const originalKey = this.animation.getKeys()[keyId];
|
|
9810
10239
|
originalKey.frame = frame;
|
|
9811
10240
|
this.keys[keyId].frame = frame;
|
|
10241
|
+
// Sync frame to all sibling curves (same animation, different property)
|
|
10242
|
+
for (const sibling of this.siblings) {
|
|
10243
|
+
if (sibling !== this && sibling.keys[keyId]) {
|
|
10244
|
+
sibling.keys[keyId].frame = frame;
|
|
10245
|
+
sibling.onDataUpdatedObservable.notifyObservers();
|
|
10246
|
+
}
|
|
10247
|
+
}
|
|
9812
10248
|
this.onDataUpdatedObservable.notifyObservers();
|
|
9813
10249
|
}
|
|
9814
10250
|
updateKeyValue(keyId, value) {
|
|
@@ -9833,6 +10269,8 @@ CurveData.TangentLength = 50;
|
|
|
9833
10269
|
*/
|
|
9834
10270
|
const Curve = ({ curve, convertX, convertY }) => {
|
|
9835
10271
|
const isQuaternion = curve.animation.dataType === Animation.ANIMATIONTYPE_QUATERNION;
|
|
10272
|
+
// Derive path data, recomputing whenever the curve's key data changes
|
|
10273
|
+
const pathData = useObservableState(useCallback(() => curve.getPathData(convertX, convertY), [curve, convertX, convertY]), curve.onDataUpdatedObservable);
|
|
9836
10274
|
// Path style - same as v1
|
|
9837
10275
|
const pathStyle = {
|
|
9838
10276
|
stroke: curve.color,
|
|
@@ -9843,7 +10281,7 @@ const Curve = ({ curve, convertX, convertY }) => {
|
|
|
9843
10281
|
pathStyle["strokeDasharray"] = "5";
|
|
9844
10282
|
pathStyle["strokeOpacity"] = "0.5";
|
|
9845
10283
|
}
|
|
9846
|
-
return (jsx("svg", { style: { cursor: "pointer", overflow: "auto" }, children: jsx("path", { d:
|
|
10284
|
+
return (jsx("svg", { style: { cursor: "pointer", overflow: "auto" }, children: jsx("path", { d: pathData, style: pathStyle }) }));
|
|
9847
10285
|
};
|
|
9848
10286
|
|
|
9849
10287
|
// Inline SVG data URIs for key point icons
|
|
@@ -9921,6 +10359,9 @@ const KeyPointComponent = (props) => {
|
|
|
9921
10359
|
if (isSelected()) {
|
|
9922
10360
|
// This keypoint is directly selected
|
|
9923
10361
|
setSelectedState(SelectionState.Selected);
|
|
10362
|
+
// Notify frame/value observers so the top bar displays correct values (like v1's _onActiveKeyPointChanged)
|
|
10363
|
+
observables.onFrameSet.notifyObservers(invertX(currentXRef.current));
|
|
10364
|
+
observables.onValueSet.notifyObservers(invertY(currentYRef.current));
|
|
9924
10365
|
}
|
|
9925
10366
|
else if (state.activeKeyPoints) {
|
|
9926
10367
|
// Check if a sibling (same keyId, different curve, same animation) is selected
|
|
@@ -9943,8 +10384,8 @@ const KeyPointComponent = (props) => {
|
|
|
9943
10384
|
setSelectedState(SelectionState.None);
|
|
9944
10385
|
setTangentSelectedIndex(-1);
|
|
9945
10386
|
}
|
|
9946
|
-
}, [state.activeKeyPoints, state.mainKeyPoint, curve, keyId, isSelected, curvesMatch]);
|
|
9947
|
-
// Extract slope from a tangent vector
|
|
10387
|
+
}, [state.activeKeyPoints, state.mainKeyPoint, curve, keyId, isSelected, curvesMatch, observables, invertX, invertY]);
|
|
10388
|
+
// Extract slope from a tangent vector
|
|
9948
10389
|
const extractSlope = useCallback((vec, storedLength, isIn) => {
|
|
9949
10390
|
const keys = curve.keys;
|
|
9950
10391
|
const keyValue = keys[keyId].value;
|
|
@@ -9960,10 +10401,13 @@ const KeyPointComponent = (props) => {
|
|
|
9960
10401
|
const currentPosition = vec.clone();
|
|
9961
10402
|
currentPosition.normalize();
|
|
9962
10403
|
currentPosition.scaleInPlace(storedLength);
|
|
9963
|
-
|
|
9964
|
-
const
|
|
10404
|
+
// Use refs for current position to avoid stale closure during drag
|
|
10405
|
+
const cx = currentXRef.current;
|
|
10406
|
+
const cy = currentYRef.current;
|
|
10407
|
+
const value = isIn ? keyValue - invertY(currentPosition.y + cy) : invertY(currentPosition.y + cy) - keyValue;
|
|
10408
|
+
const frame = isIn ? keyFrame - invertX(currentPosition.x + cx) : invertX(currentPosition.x + cx) - keyFrame;
|
|
9965
10409
|
return value / frame;
|
|
9966
|
-
}, [curve, keyId, invertX, invertY
|
|
10410
|
+
}, [curve, keyId, invertX, invertY]);
|
|
9967
10411
|
// Tangent operations
|
|
9968
10412
|
const flattenTangent = useCallback(() => {
|
|
9969
10413
|
// First update the interpolation mode to NONE without triggering observers
|
|
@@ -9982,6 +10426,8 @@ const KeyPointComponent = (props) => {
|
|
|
9982
10426
|
curve.updateOutTangentFromControlPoint(keyId, 0);
|
|
9983
10427
|
}
|
|
9984
10428
|
}
|
|
10429
|
+
// Notify curve to re-render path
|
|
10430
|
+
curve.onDataUpdatedObservable.notifyObservers();
|
|
9985
10431
|
setForceUpdate((v) => v + 1);
|
|
9986
10432
|
}, [keyId, tangentSelectedIndex, curve]);
|
|
9987
10433
|
const linearTangent = useCallback(() => {
|
|
@@ -10095,6 +10541,46 @@ const KeyPointComponent = (props) => {
|
|
|
10095
10541
|
observables.onSelectionRectangleMoved.remove(observer);
|
|
10096
10542
|
};
|
|
10097
10543
|
}, [observables, curve, keyId, actions]);
|
|
10544
|
+
// Handle frame manually entered from top bar
|
|
10545
|
+
useEffect(() => {
|
|
10546
|
+
const observer = observables.onFrameManuallyEntered.add((newValue) => {
|
|
10547
|
+
if (selectedState === SelectionState.None) {
|
|
10548
|
+
return;
|
|
10549
|
+
}
|
|
10550
|
+
let newX = convertX(newValue);
|
|
10551
|
+
// Clamp to neighbors
|
|
10552
|
+
const previousX = getPreviousX();
|
|
10553
|
+
const nextX = getNextX();
|
|
10554
|
+
if (previousX !== null) {
|
|
10555
|
+
newX = Math.max(previousX, newX);
|
|
10556
|
+
}
|
|
10557
|
+
if (nextX !== null) {
|
|
10558
|
+
newX = Math.min(nextX, newX);
|
|
10559
|
+
}
|
|
10560
|
+
const frame = invertX(newX);
|
|
10561
|
+
currentXRef.current = newX;
|
|
10562
|
+
setCurrentX(newX);
|
|
10563
|
+
onFrameValueChanged(frame);
|
|
10564
|
+
});
|
|
10565
|
+
return () => {
|
|
10566
|
+
observables.onFrameManuallyEntered.remove(observer);
|
|
10567
|
+
};
|
|
10568
|
+
}, [observables, selectedState, convertX, invertX, getPreviousX, getNextX, onFrameValueChanged]);
|
|
10569
|
+
// Handle value manually entered from top bar
|
|
10570
|
+
useEffect(() => {
|
|
10571
|
+
const observer = observables.onValueManuallyEntered.add((newValue) => {
|
|
10572
|
+
if (selectedState !== SelectionState.Selected) {
|
|
10573
|
+
return;
|
|
10574
|
+
}
|
|
10575
|
+
const newY = convertY(newValue);
|
|
10576
|
+
currentYRef.current = newY;
|
|
10577
|
+
setCurrentY(newY);
|
|
10578
|
+
onKeyValueChanged(newValue);
|
|
10579
|
+
});
|
|
10580
|
+
return () => {
|
|
10581
|
+
observables.onValueManuallyEntered.remove(observer);
|
|
10582
|
+
};
|
|
10583
|
+
}, [observables, selectedState, convertY, onKeyValueChanged]);
|
|
10098
10584
|
// Handle select all keys
|
|
10099
10585
|
useEffect(() => {
|
|
10100
10586
|
const observer = observables.onSelectAllKeys.add(() => {
|
|
@@ -10112,6 +10598,60 @@ const KeyPointComponent = (props) => {
|
|
|
10112
10598
|
observables.onSelectAllKeys.remove(observer);
|
|
10113
10599
|
};
|
|
10114
10600
|
}, [observables, actions, curve, keyId]);
|
|
10601
|
+
// Track active key points count in a ref to avoid stale closure in handlePointerMove
|
|
10602
|
+
const activeKeyPointsRef = useRef(state.activeKeyPoints);
|
|
10603
|
+
activeKeyPointsRef.current = state.activeKeyPoints;
|
|
10604
|
+
// Multi-point movement: store offset from main key point
|
|
10605
|
+
const offsetToMain = useRef({ x: 0, y: 0 });
|
|
10606
|
+
const isMainKeyPoint = useRef(false);
|
|
10607
|
+
// When a main key point is set (multi-selection), store offset from it
|
|
10608
|
+
useEffect(() => {
|
|
10609
|
+
const observer = observables.onMainKeyPointSet.add((info) => {
|
|
10610
|
+
// Check if WE are the main key point
|
|
10611
|
+
if (info.curve === curve && info.keyId === keyId) {
|
|
10612
|
+
isMainKeyPoint.current = true;
|
|
10613
|
+
return;
|
|
10614
|
+
}
|
|
10615
|
+
isMainKeyPoint.current = false;
|
|
10616
|
+
// Store offset from the main key point position
|
|
10617
|
+
offsetToMain.current = {
|
|
10618
|
+
x: currentXRef.current - info.x,
|
|
10619
|
+
y: currentYRef.current - info.y,
|
|
10620
|
+
};
|
|
10621
|
+
});
|
|
10622
|
+
return () => {
|
|
10623
|
+
observables.onMainKeyPointSet.remove(observer);
|
|
10624
|
+
};
|
|
10625
|
+
}, [observables, curve, keyId]);
|
|
10626
|
+
// When the main key point moves, follow it with offset
|
|
10627
|
+
useEffect(() => {
|
|
10628
|
+
const observer = observables.onMainKeyPointMoved.add((pos) => {
|
|
10629
|
+
// Skip if we ARE the main key point
|
|
10630
|
+
if (isMainKeyPoint.current) {
|
|
10631
|
+
return;
|
|
10632
|
+
}
|
|
10633
|
+
if (selectedState === SelectionState.None) {
|
|
10634
|
+
return;
|
|
10635
|
+
}
|
|
10636
|
+
// Move frame for selected + siblings (but not first key)
|
|
10637
|
+
if (keyId !== 0) {
|
|
10638
|
+
const newX = pos.x + offsetToMain.current.x;
|
|
10639
|
+
currentXRef.current = newX;
|
|
10640
|
+
setCurrentX(newX);
|
|
10641
|
+
onFrameValueChanged(invertX(newX));
|
|
10642
|
+
}
|
|
10643
|
+
// Move value only for directly selected points
|
|
10644
|
+
if (selectedState === SelectionState.Selected) {
|
|
10645
|
+
const newY = pos.y + offsetToMain.current.y;
|
|
10646
|
+
currentYRef.current = newY;
|
|
10647
|
+
setCurrentY(newY);
|
|
10648
|
+
onKeyValueChanged(invertY(newY));
|
|
10649
|
+
}
|
|
10650
|
+
});
|
|
10651
|
+
return () => {
|
|
10652
|
+
observables.onMainKeyPointMoved.remove(observer);
|
|
10653
|
+
};
|
|
10654
|
+
}, [observables, curve, keyId, selectedState, invertX, invertY, onFrameValueChanged, onKeyValueChanged]);
|
|
10115
10655
|
// Mouse/pointer handlers
|
|
10116
10656
|
const handlePointerDown = useCallback((evt) => {
|
|
10117
10657
|
const animationType = curve.animation.dataType;
|
|
@@ -10143,29 +10683,64 @@ const KeyPointComponent = (props) => {
|
|
|
10143
10683
|
lockY.current = false;
|
|
10144
10684
|
accumulatedX.current = 0;
|
|
10145
10685
|
accumulatedY.current = 0;
|
|
10146
|
-
// Handle selection
|
|
10686
|
+
// Handle selection (matches v1's _select logic)
|
|
10147
10687
|
if (!evt.ctrlKey) {
|
|
10148
10688
|
if (!isSelected()) {
|
|
10689
|
+
// Not in list, not multi-select: clear and add self
|
|
10149
10690
|
actions.setActiveKeyPoints([{ curve, keyId }]);
|
|
10691
|
+
// Single selection → no mainKeyPoint (like v1)
|
|
10692
|
+
actions.setMainKeyPoint(null);
|
|
10693
|
+
}
|
|
10694
|
+
else {
|
|
10695
|
+
// Already in list, not multi-select:
|
|
10696
|
+
// If >1 selected, promote this to mainKeyPoint (DON'T clear others — v1 behavior)
|
|
10697
|
+
// If only 1, mainKeyPoint = null
|
|
10698
|
+
if (state.activeKeyPoints && state.activeKeyPoints.length > 1) {
|
|
10699
|
+
const info = { x: currentXRef.current, y: currentYRef.current, curve, keyId };
|
|
10700
|
+
actions.setMainKeyPoint({ curve, keyId });
|
|
10701
|
+
queueMicrotask(() => {
|
|
10702
|
+
observables.onMainKeyPointSet.notifyObservers(info);
|
|
10703
|
+
});
|
|
10704
|
+
}
|
|
10705
|
+
else {
|
|
10706
|
+
actions.setMainKeyPoint(null);
|
|
10707
|
+
}
|
|
10150
10708
|
}
|
|
10151
10709
|
}
|
|
10152
10710
|
else {
|
|
10153
|
-
|
|
10154
|
-
|
|
10155
|
-
|
|
10156
|
-
|
|
10157
|
-
|
|
10711
|
+
// Ctrl-click: toggle selection
|
|
10712
|
+
if (isSelected()) {
|
|
10713
|
+
// Remove from list
|
|
10714
|
+
actions.setActiveKeyPoints((prev) => {
|
|
10715
|
+
const current = prev || [];
|
|
10716
|
+
const matchesCurve = (kp) => kp.curve.animation.uniqueId === curve.animation.uniqueId && kp.curve.property === curve.property && kp.keyId === keyId;
|
|
10158
10717
|
return current.filter((kp) => !matchesCurve(kp));
|
|
10718
|
+
});
|
|
10719
|
+
actions.setMainKeyPoint(null);
|
|
10720
|
+
}
|
|
10721
|
+
else {
|
|
10722
|
+
// Add to list
|
|
10723
|
+
actions.setActiveKeyPoints((prev) => {
|
|
10724
|
+
const current = prev || [];
|
|
10725
|
+
return [...current, { curve, keyId }];
|
|
10726
|
+
});
|
|
10727
|
+
// Multi selection is now engaged
|
|
10728
|
+
if ((state.activeKeyPoints?.length ?? 0) + 1 > 1) {
|
|
10729
|
+
const info = { x: currentXRef.current, y: currentYRef.current, curve, keyId };
|
|
10730
|
+
actions.setMainKeyPoint({ curve, keyId });
|
|
10731
|
+
queueMicrotask(() => {
|
|
10732
|
+
observables.onMainKeyPointSet.notifyObservers(info);
|
|
10733
|
+
});
|
|
10159
10734
|
}
|
|
10160
10735
|
else {
|
|
10161
|
-
|
|
10736
|
+
actions.setMainKeyPoint(null);
|
|
10162
10737
|
}
|
|
10163
|
-
}
|
|
10738
|
+
}
|
|
10164
10739
|
}
|
|
10165
10740
|
observables.onActiveKeyPointChanged.notifyObservers();
|
|
10166
10741
|
// Capture pointer for drag
|
|
10167
10742
|
evt.target.setPointerCapture(evt.pointerId);
|
|
10168
|
-
}, [curve, keyId, actions, observables, isSelected]);
|
|
10743
|
+
}, [curve, keyId, actions, observables, isSelected, state.activeKeyPoints]);
|
|
10169
10744
|
const handlePointerMove = useCallback((evt) => {
|
|
10170
10745
|
if (!pointerIsDown.current || selectedState !== SelectionState.Selected) {
|
|
10171
10746
|
return;
|
|
@@ -10226,6 +10801,13 @@ const KeyPointComponent = (props) => {
|
|
|
10226
10801
|
currentYRef.current = newY;
|
|
10227
10802
|
setCurrentX(newX);
|
|
10228
10803
|
setCurrentY(newY);
|
|
10804
|
+
// Notify other selected key points to follow (multi-point movement)
|
|
10805
|
+
const activeKeyPoints = activeKeyPointsRef.current;
|
|
10806
|
+
if (activeKeyPoints && activeKeyPoints.length > 1) {
|
|
10807
|
+
requestAnimationFrame(() => {
|
|
10808
|
+
observables.onMainKeyPointMoved.notifyObservers({ x: newX, y: newY });
|
|
10809
|
+
});
|
|
10810
|
+
}
|
|
10229
10811
|
}
|
|
10230
10812
|
else {
|
|
10231
10813
|
// Tangent manipulation
|
|
@@ -10435,6 +11017,129 @@ const useStyles$t = makeStyles({
|
|
|
10435
11017
|
pointerEvents: "none",
|
|
10436
11018
|
},
|
|
10437
11019
|
});
|
|
11020
|
+
/**
|
|
11021
|
+
* Evaluates active animations and produces CurveData instances with extracted key values.
|
|
11022
|
+
* @param activeAnimations - The currently active animations to evaluate
|
|
11023
|
+
* @param activeChannels - The currently active channels to determine which curves to show for multi-channel animations
|
|
11024
|
+
* @returns An array of CurveData instances representing the curves to display in the graph
|
|
11025
|
+
*/
|
|
11026
|
+
function EvaluateKeys(activeAnimations, activeChannels) {
|
|
11027
|
+
const result = [];
|
|
11028
|
+
// Helper to set default tangents across all curves
|
|
11029
|
+
const setDefaultInTangent = (keyId) => {
|
|
11030
|
+
for (const curve of result) {
|
|
11031
|
+
curve.storeDefaultInTangent(keyId);
|
|
11032
|
+
}
|
|
11033
|
+
};
|
|
11034
|
+
const setDefaultOutTangent = (keyId) => {
|
|
11035
|
+
for (const curve of result) {
|
|
11036
|
+
curve.storeDefaultOutTangent(keyId);
|
|
11037
|
+
}
|
|
11038
|
+
};
|
|
11039
|
+
for (const animation of activeAnimations) {
|
|
11040
|
+
const keys = animation.getKeys();
|
|
11041
|
+
if (keys.length === 0) {
|
|
11042
|
+
continue;
|
|
11043
|
+
}
|
|
11044
|
+
const channelColor = activeChannels[animation.uniqueId];
|
|
11045
|
+
const curvesToAdd = [];
|
|
11046
|
+
// Create curves based on data type
|
|
11047
|
+
switch (animation.dataType) {
|
|
11048
|
+
case Animation.ANIMATIONTYPE_FLOAT:
|
|
11049
|
+
curvesToAdd.push(new CurveData(channelColor || DefaultCurveColor, animation));
|
|
11050
|
+
break;
|
|
11051
|
+
case Animation.ANIMATIONTYPE_VECTOR2:
|
|
11052
|
+
if (!channelColor || channelColor === ChannelColors.X) {
|
|
11053
|
+
curvesToAdd.push(new CurveData(ChannelColors.X, animation, "x", () => Vector2.Zero(), setDefaultInTangent, setDefaultOutTangent));
|
|
11054
|
+
}
|
|
11055
|
+
if (!channelColor || channelColor === ChannelColors.Y) {
|
|
11056
|
+
curvesToAdd.push(new CurveData(ChannelColors.Y, animation, "y", () => Vector2.Zero(), setDefaultInTangent, setDefaultOutTangent));
|
|
11057
|
+
}
|
|
11058
|
+
break;
|
|
11059
|
+
case Animation.ANIMATIONTYPE_VECTOR3:
|
|
11060
|
+
if (!channelColor || channelColor === ChannelColors.X) {
|
|
11061
|
+
curvesToAdd.push(new CurveData(ChannelColors.X, animation, "x", () => Vector3.Zero(), setDefaultInTangent, setDefaultOutTangent));
|
|
11062
|
+
}
|
|
11063
|
+
if (!channelColor || channelColor === ChannelColors.Y) {
|
|
11064
|
+
curvesToAdd.push(new CurveData(ChannelColors.Y, animation, "y", () => Vector3.Zero(), setDefaultInTangent, setDefaultOutTangent));
|
|
11065
|
+
}
|
|
11066
|
+
if (!channelColor || channelColor === ChannelColors.Z) {
|
|
11067
|
+
curvesToAdd.push(new CurveData(ChannelColors.Z, animation, "z", () => Vector3.Zero(), setDefaultInTangent, setDefaultOutTangent));
|
|
11068
|
+
}
|
|
11069
|
+
break;
|
|
11070
|
+
case Animation.ANIMATIONTYPE_COLOR3:
|
|
11071
|
+
if (!channelColor || channelColor === ColorChannelColors.R) {
|
|
11072
|
+
curvesToAdd.push(new CurveData(ColorChannelColors.R, animation, "r", () => Color3.Black(), setDefaultInTangent, setDefaultOutTangent));
|
|
11073
|
+
}
|
|
11074
|
+
if (!channelColor || channelColor === ColorChannelColors.G) {
|
|
11075
|
+
curvesToAdd.push(new CurveData(ColorChannelColors.G, animation, "g", () => Color3.Black(), setDefaultInTangent, setDefaultOutTangent));
|
|
11076
|
+
}
|
|
11077
|
+
if (!channelColor || channelColor === ColorChannelColors.B) {
|
|
11078
|
+
curvesToAdd.push(new CurveData(ColorChannelColors.B, animation, "b", () => Color3.Black(), setDefaultInTangent, setDefaultOutTangent));
|
|
11079
|
+
}
|
|
11080
|
+
break;
|
|
11081
|
+
case Animation.ANIMATIONTYPE_COLOR4:
|
|
11082
|
+
if (!channelColor || channelColor === ColorChannelColors.R) {
|
|
11083
|
+
curvesToAdd.push(new CurveData(ColorChannelColors.R, animation, "r", () => new Color4(), setDefaultInTangent, setDefaultOutTangent));
|
|
11084
|
+
}
|
|
11085
|
+
if (!channelColor || channelColor === ColorChannelColors.G) {
|
|
11086
|
+
curvesToAdd.push(new CurveData(ColorChannelColors.G, animation, "g", () => new Color4(), setDefaultInTangent, setDefaultOutTangent));
|
|
11087
|
+
}
|
|
11088
|
+
if (!channelColor || channelColor === ColorChannelColors.B) {
|
|
11089
|
+
curvesToAdd.push(new CurveData(ColorChannelColors.B, animation, "b", () => new Color4(), setDefaultInTangent, setDefaultOutTangent));
|
|
11090
|
+
}
|
|
11091
|
+
if (!channelColor || channelColor === ColorChannelColors.A) {
|
|
11092
|
+
curvesToAdd.push(new CurveData(ColorChannelColors.A, animation, "a", () => new Color4(), setDefaultInTangent, setDefaultOutTangent));
|
|
11093
|
+
}
|
|
11094
|
+
break;
|
|
11095
|
+
case Animation.ANIMATIONTYPE_QUATERNION:
|
|
11096
|
+
if (!channelColor || channelColor === ChannelColors.X) {
|
|
11097
|
+
curvesToAdd.push(new CurveData(ChannelColors.X, animation, "x", () => new Quaternion(), setDefaultInTangent, setDefaultOutTangent));
|
|
11098
|
+
}
|
|
11099
|
+
if (!channelColor || channelColor === ChannelColors.Y) {
|
|
11100
|
+
curvesToAdd.push(new CurveData(ChannelColors.Y, animation, "y", () => new Quaternion(), setDefaultInTangent, setDefaultOutTangent));
|
|
11101
|
+
}
|
|
11102
|
+
if (!channelColor || channelColor === ChannelColors.Z) {
|
|
11103
|
+
curvesToAdd.push(new CurveData(ChannelColors.Z, animation, "z", () => new Quaternion(), setDefaultInTangent, setDefaultOutTangent));
|
|
11104
|
+
}
|
|
11105
|
+
if (!channelColor || channelColor === ChannelColors.W) {
|
|
11106
|
+
curvesToAdd.push(new CurveData(ChannelColors.W, animation, "w", () => new Quaternion(), setDefaultInTangent, setDefaultOutTangent));
|
|
11107
|
+
}
|
|
11108
|
+
break;
|
|
11109
|
+
}
|
|
11110
|
+
ExtractValuesFromKeys(keys, curvesToAdd);
|
|
11111
|
+
// Wire up siblings so frame changes sync across all curves for the same animation
|
|
11112
|
+
for (const curve of curvesToAdd) {
|
|
11113
|
+
curve.siblings = curvesToAdd;
|
|
11114
|
+
}
|
|
11115
|
+
result.push(...curvesToAdd);
|
|
11116
|
+
}
|
|
11117
|
+
return result;
|
|
11118
|
+
}
|
|
11119
|
+
/**
|
|
11120
|
+
* Extracts key values, tangents, and interpolation data from animation keys into CurveData instances.
|
|
11121
|
+
* @param keys - The animation keys to extract data from
|
|
11122
|
+
* @param curves - CurveData to push changes to
|
|
11123
|
+
*/
|
|
11124
|
+
function ExtractValuesFromKeys(keys, curves) {
|
|
11125
|
+
for (const key of keys) {
|
|
11126
|
+
const lockedTangent = key.lockedTangent ?? true;
|
|
11127
|
+
for (const curve of curves) {
|
|
11128
|
+
const prop = curve.property;
|
|
11129
|
+
const value = prop ? key.value[prop] : key.value;
|
|
11130
|
+
const inTangent = prop ? key.inTangent?.[prop] : key.inTangent;
|
|
11131
|
+
const outTangent = prop ? key.outTangent?.[prop] : key.outTangent;
|
|
11132
|
+
curve.keys.push({
|
|
11133
|
+
frame: key.frame,
|
|
11134
|
+
value,
|
|
11135
|
+
inTangent,
|
|
11136
|
+
outTangent,
|
|
11137
|
+
lockedTangent,
|
|
11138
|
+
interpolation: key.interpolation,
|
|
11139
|
+
});
|
|
11140
|
+
}
|
|
11141
|
+
}
|
|
11142
|
+
}
|
|
10438
11143
|
/**
|
|
10439
11144
|
* Main graph area for displaying and editing animation curves
|
|
10440
11145
|
* @returns The graph component
|
|
@@ -10498,6 +11203,33 @@ const Graph = ({ width, height }) => {
|
|
|
10498
11203
|
value: value,
|
|
10499
11204
|
lockedTangent: true,
|
|
10500
11205
|
};
|
|
11206
|
+
// Compute Hermite 1st-derivative tangents so the curve shape is preserved (like v1)
|
|
11207
|
+
if (leftKey?.outTangent !== undefined && rightKey?.inTangent !== undefined) {
|
|
11208
|
+
const invFrameDelta = 1.0 / (rightKey.frame - leftKey.frame);
|
|
11209
|
+
const cutTime = (currentFrame - leftKey.frame) * invFrameDelta;
|
|
11210
|
+
let derivative = null;
|
|
11211
|
+
switch (currentAnimation.dataType) {
|
|
11212
|
+
case Animation.ANIMATIONTYPE_FLOAT:
|
|
11213
|
+
derivative = Scalar.Hermite1stDerivative(leftKey.value * invFrameDelta, leftKey.outTangent, rightKey.value * invFrameDelta, rightKey.inTangent, cutTime);
|
|
11214
|
+
break;
|
|
11215
|
+
case Animation.ANIMATIONTYPE_VECTOR2:
|
|
11216
|
+
derivative = Vector2.Hermite1stDerivative(leftKey.value.scale(invFrameDelta), leftKey.outTangent, rightKey.value.scale(invFrameDelta), rightKey.inTangent, cutTime);
|
|
11217
|
+
break;
|
|
11218
|
+
case Animation.ANIMATIONTYPE_VECTOR3:
|
|
11219
|
+
derivative = Vector3.Hermite1stDerivative(leftKey.value.scale(invFrameDelta), leftKey.outTangent, rightKey.value.scale(invFrameDelta), rightKey.inTangent, cutTime);
|
|
11220
|
+
break;
|
|
11221
|
+
case Animation.ANIMATIONTYPE_COLOR3:
|
|
11222
|
+
derivative = Color3.Hermite1stDerivative(leftKey.value.scale(invFrameDelta), leftKey.outTangent, rightKey.value.scale(invFrameDelta), rightKey.inTangent, cutTime);
|
|
11223
|
+
break;
|
|
11224
|
+
case Animation.ANIMATIONTYPE_COLOR4:
|
|
11225
|
+
derivative = Color4.Hermite1stDerivative(leftKey.value.scale(invFrameDelta), leftKey.outTangent, rightKey.value.scale(invFrameDelta), rightKey.inTangent, cutTime);
|
|
11226
|
+
break;
|
|
11227
|
+
}
|
|
11228
|
+
if (derivative !== null) {
|
|
11229
|
+
newKey.inTangent = derivative;
|
|
11230
|
+
newKey.outTangent = derivative.clone ? derivative.clone() : derivative;
|
|
11231
|
+
}
|
|
11232
|
+
}
|
|
10501
11233
|
keys.splice(indexToAdd + 1, 0, newKey);
|
|
10502
11234
|
}
|
|
10503
11235
|
currentAnimation.setKeys(keys);
|
|
@@ -10532,6 +11264,9 @@ const Graph = ({ width, height }) => {
|
|
|
10532
11264
|
const keys = animation.getKeys();
|
|
10533
11265
|
const sortedIndices = Array.from(keyIndices).sort((a, b) => b - a); // Sort descending
|
|
10534
11266
|
for (const index of sortedIndices) {
|
|
11267
|
+
if (index === 0 || index === keys.length - 1) {
|
|
11268
|
+
continue; // Cannot delete first or last key
|
|
11269
|
+
}
|
|
10535
11270
|
if (index >= 0 && index < keys.length) {
|
|
10536
11271
|
keys.splice(index, 1);
|
|
10537
11272
|
}
|
|
@@ -10544,119 +11279,31 @@ const Graph = ({ width, height }) => {
|
|
|
10544
11279
|
observables.onActiveAnimationChanged.notifyObservers({});
|
|
10545
11280
|
});
|
|
10546
11281
|
// Note: Tangent operations (flatten, linear, break, unify, step) are handled by KeyPointComponent
|
|
10547
|
-
// Each selected keypoint subscribes to the observables and handles its own tangent updates
|
|
11282
|
+
// Each selected keypoint subscribes to the observables and handles its own tangent updates
|
|
10548
11283
|
return () => {
|
|
10549
11284
|
observables.onCreateOrUpdateKeyPointRequired.remove(onCreateOrUpdateKeyPointRequired);
|
|
10550
11285
|
observables.onFrameRequired.remove(onFrameRequired);
|
|
10551
11286
|
observables.onDeleteKeyActiveKeyPoints.remove(onDeleteKeyActiveKeyPoints);
|
|
10552
11287
|
};
|
|
10553
11288
|
}, [observables, state.activeAnimations, state.activeFrame, state.activeKeyPoints, actions]);
|
|
10554
|
-
|
|
10555
|
-
|
|
10556
|
-
|
|
10557
|
-
const
|
|
10558
|
-
|
|
10559
|
-
|
|
10560
|
-
}
|
|
11289
|
+
// Invalidation counter — incremented when keys are added/deleted so curves recompute
|
|
11290
|
+
const [curveVersion, invalidateCurves] = useReducer((c) => c + 1, 0);
|
|
11291
|
+
useEffect(() => {
|
|
11292
|
+
const observer = observables.onActiveAnimationChanged.add(() => invalidateCurves());
|
|
11293
|
+
return () => {
|
|
11294
|
+
observables.onActiveAnimationChanged.remove(observer);
|
|
10561
11295
|
};
|
|
10562
|
-
|
|
10563
|
-
|
|
10564
|
-
|
|
10565
|
-
|
|
11296
|
+
}, [observables]);
|
|
11297
|
+
const curves = useMemo(() => EvaluateKeys(state.activeAnimations, state.activeChannels), [state.activeAnimations, state.activeChannels, curveVersion]);
|
|
11298
|
+
// Re-render when any curve's key data is mutated (e.g. sibling frame sync)
|
|
11299
|
+
// so key point positions stay in sync with their curve paths
|
|
11300
|
+
const [, invalidateKeyPoints] = useReducer((c) => c + 1, 0);
|
|
11301
|
+
useEffect(() => {
|
|
11302
|
+
const observers = curves.map((curve) => curve.onDataUpdatedObservable.add(invalidateKeyPoints));
|
|
11303
|
+
return () => {
|
|
11304
|
+
curves.forEach((curve, i) => curve.onDataUpdatedObservable.remove(observers[i]));
|
|
10566
11305
|
};
|
|
10567
|
-
|
|
10568
|
-
const keys = animation.getKeys();
|
|
10569
|
-
if (keys.length === 0) {
|
|
10570
|
-
continue;
|
|
10571
|
-
}
|
|
10572
|
-
const channelColor = state.activeChannels[animation.uniqueId];
|
|
10573
|
-
const curvesToAdd = [];
|
|
10574
|
-
// Create curves based on data type (like v1's _evaluateKeys)
|
|
10575
|
-
switch (animation.dataType) {
|
|
10576
|
-
case Animation.ANIMATIONTYPE_FLOAT:
|
|
10577
|
-
curvesToAdd.push(new CurveData(channelColor || DefaultCurveColor, animation));
|
|
10578
|
-
break;
|
|
10579
|
-
case Animation.ANIMATIONTYPE_VECTOR2:
|
|
10580
|
-
if (!channelColor || channelColor === ChannelColors.X) {
|
|
10581
|
-
curvesToAdd.push(new CurveData(ChannelColors.X, animation, "x", () => Vector2.Zero(), setDefaultInTangent, setDefaultOutTangent));
|
|
10582
|
-
}
|
|
10583
|
-
if (!channelColor || channelColor === ChannelColors.Y) {
|
|
10584
|
-
curvesToAdd.push(new CurveData(ChannelColors.Y, animation, "y", () => Vector2.Zero(), setDefaultInTangent, setDefaultOutTangent));
|
|
10585
|
-
}
|
|
10586
|
-
break;
|
|
10587
|
-
case Animation.ANIMATIONTYPE_VECTOR3:
|
|
10588
|
-
if (!channelColor || channelColor === ChannelColors.X) {
|
|
10589
|
-
curvesToAdd.push(new CurveData(ChannelColors.X, animation, "x", () => Vector3.Zero(), setDefaultInTangent, setDefaultOutTangent));
|
|
10590
|
-
}
|
|
10591
|
-
if (!channelColor || channelColor === ChannelColors.Y) {
|
|
10592
|
-
curvesToAdd.push(new CurveData(ChannelColors.Y, animation, "y", () => Vector3.Zero(), setDefaultInTangent, setDefaultOutTangent));
|
|
10593
|
-
}
|
|
10594
|
-
if (!channelColor || channelColor === ChannelColors.Z) {
|
|
10595
|
-
curvesToAdd.push(new CurveData(ChannelColors.Z, animation, "z", () => Vector3.Zero(), setDefaultInTangent, setDefaultOutTangent));
|
|
10596
|
-
}
|
|
10597
|
-
break;
|
|
10598
|
-
case Animation.ANIMATIONTYPE_COLOR3:
|
|
10599
|
-
if (!channelColor || channelColor === ColorChannelColors.R) {
|
|
10600
|
-
curvesToAdd.push(new CurveData(ColorChannelColors.R, animation, "r", () => Color3.Black(), setDefaultInTangent, setDefaultOutTangent));
|
|
10601
|
-
}
|
|
10602
|
-
if (!channelColor || channelColor === ColorChannelColors.G) {
|
|
10603
|
-
curvesToAdd.push(new CurveData(ColorChannelColors.G, animation, "g", () => Color3.Black(), setDefaultInTangent, setDefaultOutTangent));
|
|
10604
|
-
}
|
|
10605
|
-
if (!channelColor || channelColor === ColorChannelColors.B) {
|
|
10606
|
-
curvesToAdd.push(new CurveData(ColorChannelColors.B, animation, "b", () => Color3.Black(), setDefaultInTangent, setDefaultOutTangent));
|
|
10607
|
-
}
|
|
10608
|
-
break;
|
|
10609
|
-
case Animation.ANIMATIONTYPE_COLOR4:
|
|
10610
|
-
if (!channelColor || channelColor === ColorChannelColors.R) {
|
|
10611
|
-
curvesToAdd.push(new CurveData(ColorChannelColors.R, animation, "r", () => new Color4(), setDefaultInTangent, setDefaultOutTangent));
|
|
10612
|
-
}
|
|
10613
|
-
if (!channelColor || channelColor === ColorChannelColors.G) {
|
|
10614
|
-
curvesToAdd.push(new CurveData(ColorChannelColors.G, animation, "g", () => new Color4(), setDefaultInTangent, setDefaultOutTangent));
|
|
10615
|
-
}
|
|
10616
|
-
if (!channelColor || channelColor === ColorChannelColors.B) {
|
|
10617
|
-
curvesToAdd.push(new CurveData(ColorChannelColors.B, animation, "b", () => new Color4(), setDefaultInTangent, setDefaultOutTangent));
|
|
10618
|
-
}
|
|
10619
|
-
if (!channelColor || channelColor === ColorChannelColors.A) {
|
|
10620
|
-
curvesToAdd.push(new CurveData(ColorChannelColors.A, animation, "a", () => new Color4(), setDefaultInTangent, setDefaultOutTangent));
|
|
10621
|
-
}
|
|
10622
|
-
break;
|
|
10623
|
-
case Animation.ANIMATIONTYPE_QUATERNION:
|
|
10624
|
-
if (!channelColor || channelColor === ChannelColors.X) {
|
|
10625
|
-
curvesToAdd.push(new CurveData(ChannelColors.X, animation, "x", () => new Quaternion(), setDefaultInTangent, setDefaultOutTangent));
|
|
10626
|
-
}
|
|
10627
|
-
if (!channelColor || channelColor === ChannelColors.Y) {
|
|
10628
|
-
curvesToAdd.push(new CurveData(ChannelColors.Y, animation, "y", () => new Quaternion(), setDefaultInTangent, setDefaultOutTangent));
|
|
10629
|
-
}
|
|
10630
|
-
if (!channelColor || channelColor === ChannelColors.Z) {
|
|
10631
|
-
curvesToAdd.push(new CurveData(ChannelColors.Z, animation, "z", () => new Quaternion(), setDefaultInTangent, setDefaultOutTangent));
|
|
10632
|
-
}
|
|
10633
|
-
if (!channelColor || channelColor === ChannelColors.W) {
|
|
10634
|
-
curvesToAdd.push(new CurveData(ChannelColors.W, animation, "w", () => new Quaternion(), setDefaultInTangent, setDefaultOutTangent));
|
|
10635
|
-
}
|
|
10636
|
-
break;
|
|
10637
|
-
}
|
|
10638
|
-
// Populate keys for each curve (like v1's _extractValuesFromKeys)
|
|
10639
|
-
for (const key of keys) {
|
|
10640
|
-
const lockedTangent = key.lockedTangent ?? true;
|
|
10641
|
-
for (const curve of curvesToAdd) {
|
|
10642
|
-
const prop = curve.property;
|
|
10643
|
-
const value = prop ? key.value[prop] : key.value;
|
|
10644
|
-
const inTangent = prop ? key.inTangent?.[prop] : key.inTangent;
|
|
10645
|
-
const outTangent = prop ? key.outTangent?.[prop] : key.outTangent;
|
|
10646
|
-
curve.keys.push({
|
|
10647
|
-
frame: key.frame,
|
|
10648
|
-
value,
|
|
10649
|
-
inTangent,
|
|
10650
|
-
outTangent,
|
|
10651
|
-
lockedTangent,
|
|
10652
|
-
interpolation: key.interpolation,
|
|
10653
|
-
});
|
|
10654
|
-
}
|
|
10655
|
-
}
|
|
10656
|
-
result.push(...curvesToAdd);
|
|
10657
|
-
}
|
|
10658
|
-
return result;
|
|
10659
|
-
}, [state.activeAnimations, state.activeChannels]);
|
|
11306
|
+
}, [curves, invalidateKeyPoints]);
|
|
10660
11307
|
// Calculate value range
|
|
10661
11308
|
const valueRange = useMemo(() => {
|
|
10662
11309
|
let minValue = 0;
|
|
@@ -11153,7 +11800,8 @@ const useStyles$q = makeStyles({
|
|
|
11153
11800
|
backgroundColor: tokens.colorNeutralBackground2,
|
|
11154
11801
|
overflow: "hidden",
|
|
11155
11802
|
userSelect: "none",
|
|
11156
|
-
|
|
11803
|
+
cursor: "pointer",
|
|
11804
|
+
touchAction: "none",
|
|
11157
11805
|
},
|
|
11158
11806
|
svg: {
|
|
11159
11807
|
width: "100%",
|
|
@@ -11186,10 +11834,11 @@ const OFFSET_X = 10;
|
|
|
11186
11834
|
*/
|
|
11187
11835
|
const RangeFrameBar = ({ width }) => {
|
|
11188
11836
|
const styles = useStyles$q();
|
|
11189
|
-
const { state, observables } = useCurveEditor();
|
|
11837
|
+
const { state, actions, observables } = useCurveEditor();
|
|
11190
11838
|
const svgRef = useRef(null);
|
|
11191
11839
|
const [viewWidth, setViewWidth] = useState(width);
|
|
11192
11840
|
const [displayFrame, setDisplayFrame] = useState(state.activeFrame);
|
|
11841
|
+
const pointerIsDown = useRef(false);
|
|
11193
11842
|
// Re-render when range updates
|
|
11194
11843
|
// useCallback stabilizes the accessor to prevent infinite re-render loops
|
|
11195
11844
|
useObservableState(useCallback(() => ({}), []), observables.onRangeUpdated);
|
|
@@ -11215,6 +11864,26 @@ const RangeFrameBar = ({ width }) => {
|
|
|
11215
11864
|
setDisplayFrame(state.activeFrame);
|
|
11216
11865
|
}
|
|
11217
11866
|
}, [state.activeFrame, state.isPlaying]);
|
|
11867
|
+
// Playhead scrubbing — linear interpolation from pointer position to frame
|
|
11868
|
+
const pointerToFrame = useCallback((offsetX) => {
|
|
11869
|
+
const { fromKey, toKey } = state;
|
|
11870
|
+
return Math.round(Math.max(fromKey, Math.min(toKey, (offsetX / viewWidth) * (toKey - fromKey) + fromKey)));
|
|
11871
|
+
}, [state.fromKey, state.toKey, viewWidth]);
|
|
11872
|
+
const handlePointerDown = useCallback((evt) => {
|
|
11873
|
+
pointerIsDown.current = true;
|
|
11874
|
+
evt.currentTarget.setPointerCapture(evt.pointerId);
|
|
11875
|
+
actions.moveToFrame(pointerToFrame(evt.nativeEvent.offsetX));
|
|
11876
|
+
}, [actions, pointerToFrame]);
|
|
11877
|
+
const handlePointerMove = useCallback((evt) => {
|
|
11878
|
+
if (!pointerIsDown.current) {
|
|
11879
|
+
return;
|
|
11880
|
+
}
|
|
11881
|
+
actions.moveToFrame(pointerToFrame(evt.nativeEvent.offsetX));
|
|
11882
|
+
}, [actions, pointerToFrame]);
|
|
11883
|
+
const handlePointerUp = useCallback((evt) => {
|
|
11884
|
+
pointerIsDown.current = false;
|
|
11885
|
+
evt.currentTarget.releasePointerCapture(evt.pointerId);
|
|
11886
|
+
}, []);
|
|
11218
11887
|
// Compute frame ticks
|
|
11219
11888
|
const frameTicks = useMemo(() => {
|
|
11220
11889
|
if (state.activeAnimations.length === 0) {
|
|
@@ -11269,7 +11938,7 @@ const RangeFrameBar = ({ width }) => {
|
|
|
11269
11938
|
return jsx("line", { className: styles.activeFrameLine, x1: x, y1: 0, x2: x, y2: 40 });
|
|
11270
11939
|
}, [displayFrame, frameToX, styles.activeFrameLine]);
|
|
11271
11940
|
const viewBox = `${ -10} 0 ${viewWidth + OFFSET_X * 4} 40`;
|
|
11272
|
-
return (jsx("div", { className: styles.root, children: jsxs("svg", { ref: svgRef, className: styles.svg, viewBox: viewBox, children: [frameTicks.map((frame, i) => {
|
|
11941
|
+
return (jsx("div", { className: styles.root, onPointerDown: handlePointerDown, onPointerMove: handlePointerMove, onPointerUp: handlePointerUp, onPointerCancel: handlePointerUp, children: jsxs("svg", { ref: svgRef, className: styles.svg, viewBox: viewBox, children: [frameTicks.map((frame, i) => {
|
|
11273
11942
|
const x = frameToX(frame);
|
|
11274
11943
|
return (jsxs("g", { children: [jsx("line", { className: styles.tickLine, x1: x, y1: 22, x2: x, y2: 40 }), jsx("text", { className: styles.tickLabel, x: x, y: 14, children: Math.round(frame) })] }, `tick-${frame}-${i}`));
|
|
11275
11944
|
}), renderKeyframes, renderActiveFrame] }) }));
|
|
@@ -11528,6 +12197,22 @@ const RangeSelector = () => {
|
|
|
11528
12197
|
}, onPointerDown: handlePointerDown, onPointerMove: handlePointerMove, onPointerUp: handlePointerUp, onPointerCancel: handlePointerCancel, children: [jsx("div", { id: "left-handle", className: styles.handle, children: jsxs("div", { className: styles.handleIcon, children: [jsx("div", {}), jsx("div", {}), jsx("div", {})] }) }), jsx("div", { className: styles.label, children: Math.floor(state.fromKey) }), jsx("div", { className: styles.label, children: Math.floor(state.toKey) }), jsx("div", { id: "right-handle", className: styles.handle, children: jsxs("div", { className: styles.handleIcon, children: [jsx("div", {}), jsx("div", {}), jsx("div", {})] }) })] }) }));
|
|
11529
12198
|
};
|
|
11530
12199
|
|
|
12200
|
+
/**
|
|
12201
|
+
* Checks whether any of the given animations has a key at the specified frame.
|
|
12202
|
+
* @param animations - The animations to check for keys
|
|
12203
|
+
* @param frame - The frame index to check for keys at
|
|
12204
|
+
* @returns True if a key exists at the frame, false otherwise.
|
|
12205
|
+
*/
|
|
12206
|
+
function GetKeyAtAnyFrameIndex(animations, frame) {
|
|
12207
|
+
for (const animation of animations) {
|
|
12208
|
+
for (const key of animation.getKeys()) {
|
|
12209
|
+
if (Math.floor(frame - key.frame) === 0) {
|
|
12210
|
+
return true;
|
|
12211
|
+
}
|
|
12212
|
+
}
|
|
12213
|
+
}
|
|
12214
|
+
return false;
|
|
12215
|
+
}
|
|
11531
12216
|
const useStyles$n = makeStyles({
|
|
11532
12217
|
root: {
|
|
11533
12218
|
display: "flex",
|
|
@@ -11621,21 +12306,34 @@ const BottomBar = () => {
|
|
|
11621
12306
|
setClipLength(newLength);
|
|
11622
12307
|
actions.setClipLength(newLength);
|
|
11623
12308
|
actions.setReferenceMaxFrame(newLength);
|
|
12309
|
+
// Move playhead to new clip end
|
|
12310
|
+
observables.onMoveToFrameRequired.notifyObservers(newLength);
|
|
12311
|
+
// Create a key at the boundary if one doesn't exist
|
|
12312
|
+
if (!GetKeyAtAnyFrameIndex(state.activeAnimations, newLength)) {
|
|
12313
|
+
observables.onCreateOrUpdateKeyPointRequired.notifyObservers();
|
|
12314
|
+
}
|
|
11624
12315
|
});
|
|
11625
12316
|
const onClipLengthDecreased = observables.onClipLengthDecreased.add((newLength) => {
|
|
11626
12317
|
setClipLength(newLength);
|
|
11627
12318
|
actions.setClipLength(newLength);
|
|
11628
12319
|
actions.setReferenceMaxFrame(newLength);
|
|
12320
|
+
// Move playhead to new clip end
|
|
12321
|
+
observables.onMoveToFrameRequired.notifyObservers(newLength);
|
|
12322
|
+
// Create a key at the boundary if one doesn't exist
|
|
12323
|
+
if (!GetKeyAtAnyFrameIndex(state.activeAnimations, newLength)) {
|
|
12324
|
+
observables.onCreateOrUpdateKeyPointRequired.notifyObservers();
|
|
12325
|
+
}
|
|
11629
12326
|
// Clamp toKey to new clip length
|
|
11630
12327
|
if (toKeyRef.current > newLength) {
|
|
11631
12328
|
actions.setToKey(newLength);
|
|
11632
12329
|
}
|
|
12330
|
+
observables.onRangeUpdated.notifyObservers();
|
|
11633
12331
|
});
|
|
11634
12332
|
return () => {
|
|
11635
12333
|
observables.onClipLengthIncreased.remove(onClipLengthIncreased);
|
|
11636
12334
|
observables.onClipLengthDecreased.remove(onClipLengthDecreased);
|
|
11637
12335
|
};
|
|
11638
|
-
}, [observables, actions]);
|
|
12336
|
+
}, [observables, actions, state.activeAnimations]);
|
|
11639
12337
|
const handlePlayForward = useCallback(() => {
|
|
11640
12338
|
actions.play(true);
|
|
11641
12339
|
}, [actions]);
|
|
@@ -12119,7 +12817,7 @@ const SoundCommandProperties = (props) => {
|
|
|
12119
12817
|
else {
|
|
12120
12818
|
sound.play();
|
|
12121
12819
|
}
|
|
12122
|
-
} }), jsx(Property, { component:
|
|
12820
|
+
} }), jsx(Property, { component: NumberInputPropertyLine, label: "Volume", functionPath: "setVolume", value: volume, min: 0, step: 0.1, onChange: (value) => {
|
|
12123
12821
|
sound.setVolume(value);
|
|
12124
12822
|
} }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Loop", target: sound, propertyKey: "loop" })] }));
|
|
12125
12823
|
};
|
|
@@ -12159,7 +12857,7 @@ const ArcRotateCameraTransformProperties = (props) => {
|
|
|
12159
12857
|
const upperBetaLimit = useProperty(camera, "upperBetaLimit") ?? Math.PI;
|
|
12160
12858
|
const lowerRadiusLimit = useProperty(camera, "lowerRadiusLimit");
|
|
12161
12859
|
const upperRadiusLimit = useProperty(camera, "upperRadiusLimit");
|
|
12162
|
-
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component:
|
|
12860
|
+
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Alpha", description: `Horizontal angle in ${useDegrees ? "degrees" : "radians"}`, target: camera, propertyKey: "alpha", min: toDisplayAngle(lowerAlphaLimit), max: toDisplayAngle(upperAlphaLimit), step: toDisplayAngle(0.01), unit: useDegrees ? "°" : "rad", convertTo: (value) => toDisplayAngle(value, true), convertFrom: fromDisplayAngle }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Beta", description: `Vertical angle in ${useDegrees ? "degrees" : "radians"}`, target: camera, propertyKey: "beta", min: toDisplayAngle(lowerBetaLimit), max: toDisplayAngle(upperBetaLimit), step: toDisplayAngle(0.01), unit: useDegrees ? "°" : "rad", convertTo: (value) => toDisplayAngle(value, true), convertFrom: fromDisplayAngle }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Radius", description: "Distance from the target point.", target: camera, propertyKey: "radius", min: lowerRadiusLimit ?? undefined, max: upperRadiusLimit ?? undefined, step: 0.01 })] }));
|
|
12163
12861
|
};
|
|
12164
12862
|
const ArcRotateCameraControlProperties = (props) => {
|
|
12165
12863
|
const { camera } = props;
|
|
@@ -12171,8 +12869,19 @@ const ArcRotateCameraCollisionProperties = (props) => {
|
|
|
12171
12869
|
};
|
|
12172
12870
|
const ArcRotateCameraLimitsProperties = (props) => {
|
|
12173
12871
|
const { camera } = props;
|
|
12872
|
+
const [toDisplayAngle, fromDisplayAngle, useDegrees] = useAngleConverters();
|
|
12873
|
+
const minAlphaLimit = 0;
|
|
12874
|
+
const maxAlphaLimit = Math.PI * 2;
|
|
12875
|
+
const minBetaLimit = -Math.PI;
|
|
12876
|
+
const maxBetaLimit = Math.PI;
|
|
12877
|
+
const lowerAlphaLimit = useProperty(camera, "lowerAlphaLimit") ?? minAlphaLimit;
|
|
12878
|
+
const upperAlphaLimit = useProperty(camera, "upperAlphaLimit") ?? maxAlphaLimit;
|
|
12879
|
+
const lowerBetaLimit = useProperty(camera, "lowerBetaLimit") ?? minBetaLimit;
|
|
12880
|
+
const upperBetaLimit = useProperty(camera, "upperBetaLimit") ?? maxBetaLimit;
|
|
12881
|
+
const lowerRadiusLimit = useProperty(camera, "lowerRadiusLimit");
|
|
12882
|
+
const upperRadiusLimit = useProperty(camera, "upperRadiusLimit");
|
|
12174
12883
|
// TODO-Iv2: Update defaultValues
|
|
12175
|
-
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Lower Alpha Limit", target: camera, propertyKey: "lowerAlphaLimit", nullable: true, defaultValue:
|
|
12884
|
+
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Lower Alpha Limit", target: camera, propertyKey: "lowerAlphaLimit", nullable: true, defaultValue: toDisplayAngle(minAlphaLimit), min: toDisplayAngle(minAlphaLimit), max: toDisplayAngle(upperAlphaLimit), unit: useDegrees ? "°" : "rad", convertTo: (value) => (value === null ? value : toDisplayAngle(value, true)), convertFrom: (value) => (value === null ? value : fromDisplayAngle(value)) }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Upper Alpha Limit", target: camera, propertyKey: "upperAlphaLimit", nullable: true, defaultValue: toDisplayAngle(maxAlphaLimit), min: toDisplayAngle(lowerAlphaLimit), max: toDisplayAngle(maxAlphaLimit), unit: useDegrees ? "°" : "rad", convertTo: (value) => (value === null ? value : toDisplayAngle(value, true)), convertFrom: (value) => (value === null ? value : fromDisplayAngle(value)) }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Lower Beta Limit", target: camera, propertyKey: "lowerBetaLimit", nullable: true, defaultValue: toDisplayAngle(minBetaLimit), min: toDisplayAngle(minBetaLimit), max: toDisplayAngle(upperBetaLimit), unit: useDegrees ? "°" : "rad", convertTo: (value) => (value === null ? value : toDisplayAngle(value, true)), convertFrom: (value) => (value === null ? value : fromDisplayAngle(value)) }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Upper Beta Limit", target: camera, propertyKey: "upperBetaLimit", nullable: true, defaultValue: toDisplayAngle(maxBetaLimit), min: toDisplayAngle(lowerBetaLimit), max: toDisplayAngle(maxBetaLimit), unit: useDegrees ? "°" : "rad", convertTo: (value) => (value === null ? value : toDisplayAngle(value, true)), convertFrom: (value) => (value === null ? value : fromDisplayAngle(value)) }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Lower Radius Limit", target: camera, propertyKey: "lowerRadiusLimit", nullable: true, defaultValue: 0, min: 0, max: upperRadiusLimit ?? undefined }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Upper Radius Limit", target: camera, propertyKey: "upperRadiusLimit", nullable: true, defaultValue: 100, min: lowerRadiusLimit ?? undefined }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Lower Target Y Limit", target: camera, propertyKey: "lowerTargetYLimit" })] }));
|
|
12176
12885
|
};
|
|
12177
12886
|
const ArcRotateCameraBehaviorsProperties = (props) => {
|
|
12178
12887
|
const { camera } = props;
|
|
@@ -12189,7 +12898,7 @@ const GeospatialCameraTransformProperties = (props) => {
|
|
|
12189
12898
|
const pitchMax = limits?.pitchMax ?? Math.PI / 2;
|
|
12190
12899
|
const radiusMin = limits?.radiusMin ?? 0;
|
|
12191
12900
|
const radiusMax = limits?.radiusMax ?? Infinity;
|
|
12192
|
-
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component:
|
|
12901
|
+
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Yaw", description: `Horizontal rotation in ${useDegrees ? "degrees" : "radians"} (0 = north)`, target: camera, propertyKey: "yaw", min: toDisplayAngle(yawMin), max: toDisplayAngle(yawMax), step: toDisplayAngle(0.01), unit: useDegrees ? "°" : "rad", convertTo: (value) => toDisplayAngle(value, true), convertFrom: fromDisplayAngle }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Pitch", description: `Vertical angle in ${useDegrees ? "degrees" : "radians"} (0 = looking down, π/2 = horizon)`, target: camera, propertyKey: "pitch", min: toDisplayAngle(pitchMin), max: toDisplayAngle(pitchMax), step: toDisplayAngle(0.01), unit: useDegrees ? "°" : "rad", convertTo: (value) => toDisplayAngle(value, true), convertFrom: fromDisplayAngle }), radiusMax !== Infinity ? (jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Radius", description: "Distance from the center point.", target: camera, propertyKey: "radius", min: radiusMin, max: radiusMax, step: 0.01 })) : (jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Radius", description: "Distance from the center point.", target: camera, propertyKey: "radius", min: 0, step: 0.01 })), jsx(BoundProperty, { component: Vector3PropertyLine, label: "Center", description: "The point on the globe the camera orbits around.", target: camera, propertyKey: "center" }), jsx(BoundProperty, { component: Vector3PropertyLine, label: "Position", description: "The camera's position.", target: camera, propertyKey: "position" })] }));
|
|
12193
12902
|
};
|
|
12194
12903
|
const GeospatialCameraCollisionProperties = (props) => {
|
|
12195
12904
|
const { camera } = props;
|
|
@@ -12266,7 +12975,7 @@ const CameraGeneralProperties = (props) => {
|
|
|
12266
12975
|
const { camera } = props;
|
|
12267
12976
|
const [toDisplayAngle, fromDisplayAngle, useDegrees] = useAngleConverters();
|
|
12268
12977
|
const mode = useProperty(camera, "mode");
|
|
12269
|
-
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Near Plane", description: "Anything closer than this will not be drawn.", target: camera, propertyKey: "minZ" }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Far Plane", description: "Anything further than this will not be drawn.", target: camera, propertyKey: "maxZ" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Inertia", target: camera, propertyKey: "inertia", min: 0, max: 1, step: 0.01 }), jsx(BoundProperty, { component: HexPropertyLine, label: "Layer Mask", target: camera, propertyKey: "layerMask" }), jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Mode", options: CameraModes, target: camera, propertyKey: "mode" }), jsx(Collapse, { visible: mode === Camera.PERSPECTIVE_CAMERA, children: jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "FOV", description: `Field of view in ${useDegrees ? "degrees" : "radians"}`, target: camera, propertyKey: "fov", min: toDisplayAngle(0.1), max: toDisplayAngle(Math.PI), step: toDisplayAngle(0.01), convertTo: toDisplayAngle, convertFrom: fromDisplayAngle }) }), jsx(Collapse, { visible: mode === Camera.ORTHOGRAPHIC_CAMERA, children: jsxs("div", { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Left", target: camera, step: 0.1, propertyKey: "orthoLeft", nullable: true, defaultValue: 0 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Right", target: camera, step: 0.1, propertyKey: "orthoRight", nullable: true, defaultValue: 0 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Top", target: camera, step: 0.1, propertyKey: "orthoTop", nullable: true, defaultValue: 0 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Bottom", target: camera, step: 0.1, propertyKey: "orthoBottom", nullable: true, defaultValue: 0 })] }) })] }));
|
|
12978
|
+
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Near Plane", description: "Anything closer than this will not be drawn.", target: camera, propertyKey: "minZ" }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Far Plane", description: "Anything further than this will not be drawn.", target: camera, propertyKey: "maxZ" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Inertia", target: camera, propertyKey: "inertia", min: 0, max: 1, step: 0.01 }), jsx(BoundProperty, { component: HexPropertyLine, label: "Layer Mask", target: camera, propertyKey: "layerMask" }), jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Mode", options: CameraModes, target: camera, propertyKey: "mode" }), jsx(Collapse, { visible: mode === Camera.PERSPECTIVE_CAMERA, children: jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "FOV", description: `Field of view in ${useDegrees ? "degrees" : "radians"}`, target: camera, propertyKey: "fov", min: toDisplayAngle(0.1), max: toDisplayAngle(Math.PI), step: toDisplayAngle(0.01), unit: useDegrees ? "°" : "rad", convertTo: toDisplayAngle, convertFrom: fromDisplayAngle }) }), jsx(Collapse, { visible: mode === Camera.ORTHOGRAPHIC_CAMERA, children: jsxs("div", { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Left", target: camera, step: 0.1, propertyKey: "orthoLeft", nullable: true, defaultValue: 0 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Right", target: camera, step: 0.1, propertyKey: "orthoRight", nullable: true, defaultValue: 0 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Top", target: camera, step: 0.1, propertyKey: "orthoTop", nullable: true, defaultValue: 0 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Bottom", target: camera, step: 0.1, propertyKey: "orthoBottom", nullable: true, defaultValue: 0 })] }) })] }));
|
|
12270
12979
|
};
|
|
12271
12980
|
|
|
12272
12981
|
const FollowCameraTransformProperties = (props) => {
|
|
@@ -12985,7 +13694,7 @@ const Gradient = (props) => {
|
|
|
12985
13694
|
// Only use compact mode when there are numeric values (spinbuttons) taking up space
|
|
12986
13695
|
const hasNumericValues = !(gradient.value1 instanceof Color3 || gradient.value1 instanceof Color4) ||
|
|
12987
13696
|
(gradient.value2 !== undefined && !(gradient.value2 instanceof Color3 || gradient.value2 instanceof Color4));
|
|
12988
|
-
return (jsxs("div", { id: "gradientContainer", className: classes.container, children: [jsx("div", { className: gradient.value1 instanceof Color3 || gradient.value1 instanceof Color4 ? classes.colorWrapper : classes.valueWrapper, children: gradient.value1 instanceof Color3 || gradient.value1 instanceof Color4 ? (jsx(ColorPickerPopup, { value: gradient.value1, onChange: (color) => gradientChange({ ...gradient, value1: color }) })) : (jsx(SyncedSliderInput, { step: 0.01, value: gradient.value1, onChange: (val) => gradientChange({ ...gradient, value1: val }), compact: true })) }), gradient.value2 !== undefined && (jsx("div", { className: gradient.value2 instanceof Color3 || gradient.value2 instanceof Color4 ? classes.colorWrapper : classes.valueWrapper, children: gradient.value2 instanceof Color3 || gradient.value2 instanceof Color4 ? (jsx(ColorPickerPopup, { value: gradient.value2, onChange: (color) => gradientChange({ ...gradient, value2: color }) })) : (jsx(SyncedSliderInput, { step: 0.01, value: gradient.value2, onChange: (val) => gradientChange({ ...gradient, value2: val }), compact: true })) })), jsx("div", { className: classes.stepSliderWrapper, children: jsx(SyncedSliderInput, { notifyOnlyOnRelease: true, min: 0, max: 1, step: 0.01, value: gradient.step, onChange: (val) => gradientChange({ ...gradient, step: val }), compact: hasNumericValues, growSlider: !hasNumericValues }) })] }));
|
|
13697
|
+
return (jsxs("div", { id: "gradientContainer", className: classes.container, children: [jsx("div", { className: gradient.value1 instanceof Color3 || gradient.value1 instanceof Color4 ? classes.colorWrapper : classes.valueWrapper, children: gradient.value1 instanceof Color3 || gradient.value1 instanceof Color4 ? (jsx(ColorPickerPopup, { value: gradient.value1, onChange: (color) => gradientChange({ ...gradient, value1: color }) })) : (jsx(SyncedSliderInput, { step: 0.01, precision: 2, value: gradient.value1, onChange: (val) => gradientChange({ ...gradient, value1: val }), compact: true })) }), gradient.value2 !== undefined && (jsx("div", { className: gradient.value2 instanceof Color3 || gradient.value2 instanceof Color4 ? classes.colorWrapper : classes.valueWrapper, children: gradient.value2 instanceof Color3 || gradient.value2 instanceof Color4 ? (jsx(ColorPickerPopup, { value: gradient.value2, onChange: (color) => gradientChange({ ...gradient, value2: color }) })) : (jsx(SyncedSliderInput, { step: 0.01, precision: 2, value: gradient.value2, onChange: (val) => gradientChange({ ...gradient, value2: val }), compact: true })) })), jsx("div", { className: classes.stepSliderWrapper, children: jsx(SyncedSliderInput, { notifyOnlyOnRelease: true, min: 0, max: 1, step: 0.01, precision: 2, value: gradient.step, onChange: (val) => gradientChange({ ...gradient, step: val }), compact: hasNumericValues, growSlider: !hasNumericValues }) })] }));
|
|
12989
13698
|
};
|
|
12990
13699
|
const FactorGradientCast = Gradient;
|
|
12991
13700
|
const Color3GradientCast = Gradient;
|
|
@@ -13856,7 +14565,7 @@ const PBRBaseMaterialDebugProperties = (props) => {
|
|
|
13856
14565
|
const SkyMaterialProperties = (props) => {
|
|
13857
14566
|
const { material } = props;
|
|
13858
14567
|
const [toDisplayAngle, fromDisplayAngle, useDegrees] = useAngleConverters();
|
|
13859
|
-
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component:
|
|
14568
|
+
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Azimuth", description: `Azimuth angle in ${useDegrees ? "degrees" : "radians"}`, target: material, propertyKey: "azimuth", step: toDisplayAngle(0.001), unit: useDegrees ? "°" : "rad", convertTo: toDisplayAngle, convertFrom: fromDisplayAngle, docLink: "https://doc.babylonjs.com/toolsAndResources/assetLibraries/materialsLibrary/skyMat/#configuring-the-sky-material" }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Inclination", description: `Inclination angle in ${useDegrees ? "degrees" : "radians"}`, target: material, propertyKey: "inclination", min: toDisplayAngle(0), max: toDisplayAngle(Math.PI / 2), step: toDisplayAngle(0.001), unit: useDegrees ? "°" : "rad", convertTo: toDisplayAngle, convertFrom: fromDisplayAngle, docLink: "https://doc.babylonjs.com/toolsAndResources/assetLibraries/materialsLibrary/skyMat/#configuring-the-sky-material" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Turbidity", description: "Atmospheric turbidity.", target: material, propertyKey: "turbidity", min: 0, max: 100, step: 0.1, docLink: "https://doc.babylonjs.com/toolsAndResources/assetLibraries/materialsLibrary/skyMat/#configuring-the-sky-material" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Luminance", description: "Brightness of the sky (0 to 1).", target: material, propertyKey: "luminance", min: 0, max: 1, step: 0.001, docLink: "https://doc.babylonjs.com/toolsAndResources/assetLibraries/materialsLibrary/skyMat/#configuring-the-sky-material" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Rayleigh", description: "Rayleigh scattering coefficient (0 to 4).", target: material, propertyKey: "rayleigh", min: 0, max: 4, step: 0.001, docLink: "https://doc.babylonjs.com/toolsAndResources/assetLibraries/materialsLibrary/skyMat/#configuring-the-sky-material" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Mie Directional G", description: "Mie directional scattering (0 to 1).", target: material, propertyKey: "mieDirectionalG", min: 0, max: 1, step: 0.001, docLink: "https://doc.babylonjs.com/toolsAndResources/assetLibraries/materialsLibrary/skyMat/#configuring-the-sky-material" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Mie Coefficient", description: "Mie scattering coefficient (0 to 1).", target: material, propertyKey: "mieCoefficient", min: 0, max: 1, step: 0.001, docLink: "https://doc.babylonjs.com/toolsAndResources/assetLibraries/materialsLibrary/skyMat/#configuring-the-sky-material" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Distance", description: "Distance to the sky dome (0 to 1000 units).", target: material, propertyKey: "distance", min: 0, max: 1000, step: 0.1, docLink: "https://doc.babylonjs.com/toolsAndResources/assetLibraries/materialsLibrary/skyMat/#configuring-the-sky-material" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Use Sun Pos", description: "Enable custom sun position.", target: material, propertyKey: "useSunPosition", docLink: "https://doc.babylonjs.com/toolsAndResources/assetLibraries/materialsLibrary/skyMat/#configuring-the-sky-material" }), jsx(BoundProperty, { component: Vector3PropertyLine, label: "Sun Position", description: "Custom sun position (Vector3).", target: material, propertyKey: "sunPosition", docLink: "https://doc.babylonjs.com/toolsAndResources/assetLibraries/materialsLibrary/skyMat/#configuring-the-sky-material" }), jsx(BoundProperty, { component: Vector3PropertyLine, label: "Camera Offset", description: "Offset for the camera (Vector3).", target: material, propertyKey: "cameraOffset", docLink: "https://doc.babylonjs.com/toolsAndResources/assetLibraries/materialsLibrary/skyMat/#keeping-the-horizon-relative-to-the-camera-elevation" })] }));
|
|
13860
14569
|
};
|
|
13861
14570
|
|
|
13862
14571
|
const StandardMaterialGeneralProperties = (props) => {
|
|
@@ -17026,7 +17735,7 @@ const SpriteGeneralProperties = (props) => {
|
|
|
17026
17735
|
const SpriteTransformProperties = (props) => {
|
|
17027
17736
|
const { sprite } = props;
|
|
17028
17737
|
const [toDisplayAngle, fromDisplayAngle, useDegrees] = useAngleConverters();
|
|
17029
|
-
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: Vector3PropertyLine, label: "Position", target: sprite, propertyKey: "position" }), jsx(BoundProperty, { component:
|
|
17738
|
+
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: Vector3PropertyLine, label: "Position", target: sprite, propertyKey: "position" }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Angle", description: `Rotation angle of the sprite in ${useDegrees ? "degrees" : "radians"}`, step: toDisplayAngle(0.01), unit: useDegrees ? "°" : "rad", target: sprite, propertyKey: "angle", convertTo: toDisplayAngle, convertFrom: fromDisplayAngle }, "Angle"), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Width", description: "Width of the sprite (in world space units)", target: sprite, propertyKey: "width" }, "Width"), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Height", description: "Height of the sprite (in world space units)", target: sprite, propertyKey: "height" }, "Height")] }));
|
|
17030
17739
|
};
|
|
17031
17740
|
const SpriteAnimationProperties = (props) => {
|
|
17032
17741
|
const { sprite } = props;
|
|
@@ -18620,7 +19329,7 @@ const Contrast = {
|
|
|
18620
19329
|
const handleExposureChange = (_, data) => {
|
|
18621
19330
|
setExposure(data.value);
|
|
18622
19331
|
};
|
|
18623
|
-
return (jsxs("div", { className: classes.settingsContainer, children: [jsxs("div", { className: classes.sliderRow, children: [jsxs(Label, { children: ["Contrast: ", contrast] }), jsx(Slider, { min: -100, max: 100, value: contrast, onChange: handleContrastChange })] }), jsxs("div", { className: classes.sliderRow, children: [jsxs(Label, { children: ["Exposure: ", exposure] }), jsx(Slider, { min: -100, max: 100, value: exposure, onChange: handleExposureChange })] })] }));
|
|
19332
|
+
return (jsxs("div", { className: classes.settingsContainer, children: [jsxs("div", { className: classes.sliderRow, children: [jsxs(Label, { children: ["Contrast: ", contrast] }), jsx(Slider$1, { min: -100, max: 100, value: contrast, onChange: handleContrastChange })] }), jsxs("div", { className: classes.sliderRow, children: [jsxs(Label, { children: ["Exposure: ", exposure] }), jsx(Slider$1, { min: -100, max: 100, value: exposure, onChange: handleExposureChange })] })] }));
|
|
18624
19333
|
},
|
|
18625
19334
|
};
|
|
18626
19335
|
},
|
|
@@ -18842,7 +19551,7 @@ const Paintbrush = {
|
|
|
18842
19551
|
const handleWidthChange = (_, data) => {
|
|
18843
19552
|
setWidth(data.value);
|
|
18844
19553
|
};
|
|
18845
|
-
return (jsx("div", { className: classes.settingsContainer, children: jsxs("div", { className: classes.sliderRow, children: [jsxs(Label, { children: ["Size: ", width] }), jsx(Slider, { min: 1, max: 100, value: width, onChange: handleWidthChange })] }) }));
|
|
19554
|
+
return (jsx("div", { className: classes.settingsContainer, children: jsxs("div", { className: classes.sliderRow, children: [jsxs(Label, { children: ["Size: ", width] }), jsx(Slider$1, { min: 1, max: 100, value: width, onChange: handleWidthChange })] }) }));
|
|
18846
19555
|
},
|
|
18847
19556
|
};
|
|
18848
19557
|
},
|
|
@@ -19087,7 +19796,7 @@ const TransformProperties = (props) => {
|
|
|
19087
19796
|
const quatRotation = useQuaternionProperty(transform, "rotationQuaternion");
|
|
19088
19797
|
const [useDegrees] = useSetting(UseDegreesSettingDescriptor);
|
|
19089
19798
|
const [useEuler] = useSetting(UseEulerSettingDescriptor);
|
|
19090
|
-
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: Vector3PropertyLine, label: "Position", target: transform, propertyKey: "position" }), quatRotation ? (jsx(Property, { component: QuaternionPropertyLine, label: "Rotation
|
|
19799
|
+
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: Vector3PropertyLine, label: "Position", target: transform, propertyKey: "position" }), quatRotation ? (jsx(Property, { component: QuaternionPropertyLine, label: "Rotation", propertyPath: "rotationQuaternion", value: quatRotation, onChange: (val) => (transform.rotationQuaternion = val), useDegrees: useDegrees, useEuler: useEuler })) : (jsx(BoundProperty, { component: RotationVectorPropertyLine, label: "Rotation", target: transform, propertyKey: "rotation", useDegrees: useDegrees })), jsx(BoundProperty, { component: Vector3PropertyLine, label: "Scaling", target: transform, propertyKey: "scaling", step: 0.1 })] }));
|
|
19091
19800
|
};
|
|
19092
19801
|
|
|
19093
19802
|
const TransformPropertiesServiceDefinition = {
|
|
@@ -19115,8 +19824,8 @@ const TransformPropertiesServiceDefinition = {
|
|
|
19115
19824
|
|
|
19116
19825
|
const AnimationGroupExplorerServiceDefinition = {
|
|
19117
19826
|
friendlyName: "Animation Group Explorer",
|
|
19118
|
-
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity],
|
|
19119
|
-
factory: (sceneExplorerService, sceneContext) => {
|
|
19827
|
+
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity, WatcherServiceIdentity],
|
|
19828
|
+
factory: (sceneExplorerService, sceneContext, watcherService) => {
|
|
19120
19829
|
const scene = sceneContext.currentScene;
|
|
19121
19830
|
if (!scene) {
|
|
19122
19831
|
return undefined;
|
|
@@ -19129,11 +19838,7 @@ const AnimationGroupExplorerServiceDefinition = {
|
|
|
19129
19838
|
getEntityDisplayInfo: (entity) => {
|
|
19130
19839
|
const namedEntity = entity instanceof AnimationGroup ? entity : entity.animation;
|
|
19131
19840
|
const onChangeObservable = new Observable();
|
|
19132
|
-
const nameHookToken =
|
|
19133
|
-
afterSet: () => {
|
|
19134
|
-
onChangeObservable.notifyObservers();
|
|
19135
|
-
},
|
|
19136
|
-
});
|
|
19841
|
+
const nameHookToken = watcherService.watchProperty(namedEntity, "name", () => onChangeObservable.notifyObservers());
|
|
19137
19842
|
return {
|
|
19138
19843
|
get name() {
|
|
19139
19844
|
return namedEntity.name;
|
|
@@ -19199,8 +19904,8 @@ const AnimationGroupExplorerServiceDefinition = {
|
|
|
19199
19904
|
|
|
19200
19905
|
const AtmosphereExplorerServiceDefinition = {
|
|
19201
19906
|
friendlyName: "Atmosphere Explorer",
|
|
19202
|
-
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity],
|
|
19203
|
-
factory: (sceneExplorerService, sceneContext) => {
|
|
19907
|
+
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity, WatcherServiceIdentity],
|
|
19908
|
+
factory: (sceneExplorerService, sceneContext, watcherService) => {
|
|
19204
19909
|
const scene = sceneContext.currentScene;
|
|
19205
19910
|
if (!scene) {
|
|
19206
19911
|
return undefined;
|
|
@@ -19211,11 +19916,7 @@ const AtmosphereExplorerServiceDefinition = {
|
|
|
19211
19916
|
getRootEntities: () => (scene.getExternalData("atmosphere") ? [scene.getExternalData("atmosphere")] : []),
|
|
19212
19917
|
getEntityDisplayInfo: (atmosphere) => {
|
|
19213
19918
|
const onChangeObservable = new Observable();
|
|
19214
|
-
const nameHookToken =
|
|
19215
|
-
afterSet: () => {
|
|
19216
|
-
onChangeObservable.notifyObservers();
|
|
19217
|
-
},
|
|
19218
|
-
});
|
|
19919
|
+
const nameHookToken = watcherService.watchProperty(atmosphere, "name", () => onChangeObservable.notifyObservers());
|
|
19219
19920
|
return {
|
|
19220
19921
|
get name() {
|
|
19221
19922
|
return atmosphere.name;
|
|
@@ -19274,8 +19975,8 @@ const DisposableCommandServiceDefinition = {
|
|
|
19274
19975
|
|
|
19275
19976
|
const EffectLayerExplorerServiceDefinition = {
|
|
19276
19977
|
friendlyName: "Effect Layer Explorer",
|
|
19277
|
-
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity],
|
|
19278
|
-
factory: (sceneExplorerService, sceneContext) => {
|
|
19978
|
+
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity, WatcherServiceIdentity],
|
|
19979
|
+
factory: (sceneExplorerService, sceneContext, watcherService) => {
|
|
19279
19980
|
const scene = sceneContext.currentScene;
|
|
19280
19981
|
if (!scene) {
|
|
19281
19982
|
return undefined;
|
|
@@ -19286,11 +19987,7 @@ const EffectLayerExplorerServiceDefinition = {
|
|
|
19286
19987
|
getRootEntities: () => scene.effectLayers,
|
|
19287
19988
|
getEntityDisplayInfo: (effectLayer) => {
|
|
19288
19989
|
const onChangeObservable = new Observable();
|
|
19289
|
-
const nameHookToken =
|
|
19290
|
-
afterSet: () => {
|
|
19291
|
-
onChangeObservable.notifyObservers();
|
|
19292
|
-
},
|
|
19293
|
-
});
|
|
19990
|
+
const nameHookToken = watcherService.watchProperty(effectLayer, "name", () => onChangeObservable.notifyObservers());
|
|
19294
19991
|
return {
|
|
19295
19992
|
get name() {
|
|
19296
19993
|
return effectLayer.name;
|
|
@@ -19316,8 +20013,8 @@ const EffectLayerExplorerServiceDefinition = {
|
|
|
19316
20013
|
|
|
19317
20014
|
const FrameGraphExplorerServiceDefinition = {
|
|
19318
20015
|
friendlyName: "Frame Graph Explorer",
|
|
19319
|
-
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity],
|
|
19320
|
-
factory: (sceneExplorerService, sceneContext) => {
|
|
20016
|
+
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity, WatcherServiceIdentity],
|
|
20017
|
+
factory: (sceneExplorerService, sceneContext, watcherService) => {
|
|
19321
20018
|
const scene = sceneContext.currentScene;
|
|
19322
20019
|
if (!scene) {
|
|
19323
20020
|
return undefined;
|
|
@@ -19328,11 +20025,7 @@ const FrameGraphExplorerServiceDefinition = {
|
|
|
19328
20025
|
getRootEntities: () => scene.frameGraphs,
|
|
19329
20026
|
getEntityDisplayInfo: (frameGraph) => {
|
|
19330
20027
|
const onChangeObservable = new Observable();
|
|
19331
|
-
const nameHookToken =
|
|
19332
|
-
afterSet: () => {
|
|
19333
|
-
onChangeObservable.notifyObservers();
|
|
19334
|
-
},
|
|
19335
|
-
});
|
|
20028
|
+
const nameHookToken = watcherService.watchProperty(frameGraph, "name", () => onChangeObservable.notifyObservers());
|
|
19336
20029
|
return {
|
|
19337
20030
|
get name() {
|
|
19338
20031
|
return frameGraph.name;
|
|
@@ -19353,9 +20046,7 @@ const FrameGraphExplorerServiceDefinition = {
|
|
|
19353
20046
|
order: 900 /* DefaultCommandsOrder.FrameGraphPlay */,
|
|
19354
20047
|
getCommand: (frameGraph) => {
|
|
19355
20048
|
const onChangeObservable = new Observable();
|
|
19356
|
-
const frameGraphHook =
|
|
19357
|
-
afterSet: () => onChangeObservable.notifyObservers(),
|
|
19358
|
-
});
|
|
20049
|
+
const frameGraphHook = watcherService.watchProperty(scene, "frameGraph", () => onChangeObservable.notifyObservers());
|
|
19359
20050
|
return {
|
|
19360
20051
|
type: "toggle",
|
|
19361
20052
|
displayName: "Make Active",
|
|
@@ -19418,8 +20109,8 @@ function IsControl(entity) {
|
|
|
19418
20109
|
}
|
|
19419
20110
|
const GuiExplorerServiceDefinition = {
|
|
19420
20111
|
friendlyName: "GUI Explorer",
|
|
19421
|
-
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity],
|
|
19422
|
-
factory: (sceneExplorerService, sceneContext) => {
|
|
20112
|
+
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity, WatcherServiceIdentity],
|
|
20113
|
+
factory: (sceneExplorerService, sceneContext, watcherService) => {
|
|
19423
20114
|
const scene = sceneContext.currentScene;
|
|
19424
20115
|
if (!scene) {
|
|
19425
20116
|
return undefined;
|
|
@@ -19445,10 +20136,8 @@ const GuiExplorerServiceDefinition = {
|
|
|
19445
20136
|
const disposeActions = [];
|
|
19446
20137
|
const onChangeObservable = new Observable();
|
|
19447
20138
|
disposeActions.push(() => onChangeObservable.clear());
|
|
19448
|
-
const nameHookToken =
|
|
19449
|
-
|
|
19450
|
-
onChangeObservable.notifyObservers();
|
|
19451
|
-
},
|
|
20139
|
+
const nameHookToken = watcherService.watchProperty(entity, "name", () => {
|
|
20140
|
+
onChangeObservable.notifyObservers();
|
|
19452
20141
|
});
|
|
19453
20142
|
disposeActions.push(() => nameHookToken.dispose());
|
|
19454
20143
|
if (!IsAdvancedDynamicTexture(entity) && IsContainer(entity)) {
|
|
@@ -19505,9 +20194,7 @@ const GuiExplorerServiceDefinition = {
|
|
|
19505
20194
|
order: 1000 /* DefaultCommandsOrder.GuiHighlight */,
|
|
19506
20195
|
getCommand: (control) => {
|
|
19507
20196
|
const onChangeObservable = new Observable();
|
|
19508
|
-
const showBoundingBoxHook =
|
|
19509
|
-
afterSet: () => onChangeObservable.notifyObservers(),
|
|
19510
|
-
});
|
|
20197
|
+
const showBoundingBoxHook = watcherService.watchProperty(control, "isHighlighted", () => onChangeObservable.notifyObservers());
|
|
19511
20198
|
return {
|
|
19512
20199
|
type: "toggle",
|
|
19513
20200
|
get displayName() {
|
|
@@ -19563,8 +20250,8 @@ const GuiExplorerServiceDefinition = {
|
|
|
19563
20250
|
|
|
19564
20251
|
const MaterialExplorerServiceDefinition = {
|
|
19565
20252
|
friendlyName: "Material Explorer",
|
|
19566
|
-
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity],
|
|
19567
|
-
factory: (sceneExplorerService, sceneContext) => {
|
|
20253
|
+
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity, WatcherServiceIdentity],
|
|
20254
|
+
factory: (sceneExplorerService, sceneContext, watcherService) => {
|
|
19568
20255
|
const scene = sceneContext.currentScene;
|
|
19569
20256
|
if (!scene) {
|
|
19570
20257
|
return undefined;
|
|
@@ -19575,11 +20262,7 @@ const MaterialExplorerServiceDefinition = {
|
|
|
19575
20262
|
getRootEntities: () => [...scene.materials, ...scene.multiMaterials],
|
|
19576
20263
|
getEntityDisplayInfo: (material) => {
|
|
19577
20264
|
const onChangeObservable = new Observable();
|
|
19578
|
-
const nameHookToken =
|
|
19579
|
-
afterSet: () => {
|
|
19580
|
-
onChangeObservable.notifyObservers();
|
|
19581
|
-
},
|
|
19582
|
-
});
|
|
20265
|
+
const nameHookToken = watcherService.watchProperty(material, "name", () => onChangeObservable.notifyObservers());
|
|
19583
20266
|
return {
|
|
19584
20267
|
get name() {
|
|
19585
20268
|
return material.name;
|
|
@@ -19619,8 +20302,8 @@ const MaterialExplorerServiceDefinition = {
|
|
|
19619
20302
|
|
|
19620
20303
|
const NodeExplorerServiceDefinition = {
|
|
19621
20304
|
friendlyName: "Node Explorer",
|
|
19622
|
-
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity, GizmoServiceIdentity],
|
|
19623
|
-
factory: (sceneExplorerService, sceneContext, gizmoService) => {
|
|
20305
|
+
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity, GizmoServiceIdentity, WatcherServiceIdentity],
|
|
20306
|
+
factory: (sceneExplorerService, sceneContext, gizmoService, watcherService) => {
|
|
19624
20307
|
const scene = sceneContext.currentScene;
|
|
19625
20308
|
if (!scene) {
|
|
19626
20309
|
return undefined;
|
|
@@ -19659,14 +20342,8 @@ const NodeExplorerServiceDefinition = {
|
|
|
19659
20342
|
getEntityChildren: (node) => node.getChildren(),
|
|
19660
20343
|
getEntityDisplayInfo: (node) => {
|
|
19661
20344
|
const onChangeObservable = new Observable();
|
|
19662
|
-
const nameHookToken =
|
|
19663
|
-
|
|
19664
|
-
});
|
|
19665
|
-
const parentHookToken = InterceptProperty(node, "parent", {
|
|
19666
|
-
afterSet: () => {
|
|
19667
|
-
nodeMovedObservable.notifyObservers(node);
|
|
19668
|
-
},
|
|
19669
|
-
});
|
|
20345
|
+
const nameHookToken = watcherService.watchProperty(node, "name", () => onChangeObservable.notifyObservers());
|
|
20346
|
+
const parentHookToken = watcherService.watchProperty(node, "parent", () => nodeMovedObservable.notifyObservers(node));
|
|
19670
20347
|
return {
|
|
19671
20348
|
get name() {
|
|
19672
20349
|
return node.name;
|
|
@@ -19729,9 +20406,7 @@ const NodeExplorerServiceDefinition = {
|
|
|
19729
20406
|
order: 1000 /* DefaultCommandsOrder.MeshBoundingBox */,
|
|
19730
20407
|
getCommand: (mesh) => {
|
|
19731
20408
|
const onChangeObservable = new Observable();
|
|
19732
|
-
const showBoundingBoxHook =
|
|
19733
|
-
afterSet: () => onChangeObservable.notifyObservers(),
|
|
19734
|
-
});
|
|
20409
|
+
const showBoundingBoxHook = watcherService.watchProperty(mesh, "showBoundingBox", () => onChangeObservable.notifyObservers());
|
|
19735
20410
|
return {
|
|
19736
20411
|
type: "toggle",
|
|
19737
20412
|
get displayName() {
|
|
@@ -19757,9 +20432,7 @@ const NodeExplorerServiceDefinition = {
|
|
|
19757
20432
|
order: 1100 /* DefaultCommandsOrder.MeshVisibility */,
|
|
19758
20433
|
getCommand: (mesh) => {
|
|
19759
20434
|
const onChangeObservable = new Observable();
|
|
19760
|
-
const isVisibleHook =
|
|
19761
|
-
afterSet: () => onChangeObservable.notifyObservers(),
|
|
19762
|
-
});
|
|
20435
|
+
const isVisibleHook = watcherService.watchProperty(mesh, "isVisible", () => onChangeObservable.notifyObservers());
|
|
19763
20436
|
return {
|
|
19764
20437
|
type: "toggle",
|
|
19765
20438
|
get displayName() {
|
|
@@ -19911,8 +20584,8 @@ const NodeExplorerServiceDefinition = {
|
|
|
19911
20584
|
|
|
19912
20585
|
const ParticleSystemExplorerServiceDefinition = {
|
|
19913
20586
|
friendlyName: "Particle System Explorer",
|
|
19914
|
-
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity],
|
|
19915
|
-
factory: (sceneExplorerService, sceneContext) => {
|
|
20587
|
+
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity, WatcherServiceIdentity],
|
|
20588
|
+
factory: (sceneExplorerService, sceneContext, watcherService) => {
|
|
19916
20589
|
const scene = sceneContext.currentScene;
|
|
19917
20590
|
if (!scene) {
|
|
19918
20591
|
return undefined;
|
|
@@ -19923,11 +20596,7 @@ const ParticleSystemExplorerServiceDefinition = {
|
|
|
19923
20596
|
getRootEntities: () => scene.particleSystems,
|
|
19924
20597
|
getEntityDisplayInfo: (particleSystem) => {
|
|
19925
20598
|
const onChangeObservable = new Observable();
|
|
19926
|
-
const nameHookToken =
|
|
19927
|
-
afterSet: () => {
|
|
19928
|
-
onChangeObservable.notifyObservers();
|
|
19929
|
-
},
|
|
19930
|
-
});
|
|
20599
|
+
const nameHookToken = watcherService.watchProperty(particleSystem, "name", () => onChangeObservable.notifyObservers());
|
|
19931
20600
|
return {
|
|
19932
20601
|
get name() {
|
|
19933
20602
|
return particleSystem.name;
|
|
@@ -19967,8 +20636,8 @@ const ParticleSystemExplorerServiceDefinition = {
|
|
|
19967
20636
|
|
|
19968
20637
|
const PostProcessExplorerServiceDefinition = {
|
|
19969
20638
|
friendlyName: "Post Process Explorer",
|
|
19970
|
-
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity],
|
|
19971
|
-
factory: (sceneExplorerService, sceneContext) => {
|
|
20639
|
+
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity, WatcherServiceIdentity],
|
|
20640
|
+
factory: (sceneExplorerService, sceneContext, watcherService) => {
|
|
19972
20641
|
const scene = sceneContext.currentScene;
|
|
19973
20642
|
if (!scene) {
|
|
19974
20643
|
return undefined;
|
|
@@ -19979,11 +20648,7 @@ const PostProcessExplorerServiceDefinition = {
|
|
|
19979
20648
|
getRootEntities: () => scene.postProcesses,
|
|
19980
20649
|
getEntityDisplayInfo: (postProcess) => {
|
|
19981
20650
|
const onChangeObservable = new Observable();
|
|
19982
|
-
const nameHookToken =
|
|
19983
|
-
afterSet: () => {
|
|
19984
|
-
onChangeObservable.notifyObservers();
|
|
19985
|
-
},
|
|
19986
|
-
});
|
|
20651
|
+
const nameHookToken = watcherService.watchProperty(postProcess, "name", () => onChangeObservable.notifyObservers());
|
|
19987
20652
|
return {
|
|
19988
20653
|
get name() {
|
|
19989
20654
|
return postProcess.name;
|
|
@@ -20041,8 +20706,8 @@ const RenderingPipelineExplorerServiceDefinition = {
|
|
|
20041
20706
|
|
|
20042
20707
|
const SkeletonExplorerServiceDefinition = {
|
|
20043
20708
|
friendlyName: "Skeleton Explorer",
|
|
20044
|
-
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity],
|
|
20045
|
-
factory: (sceneExplorerService, sceneContext) => {
|
|
20709
|
+
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity, WatcherServiceIdentity],
|
|
20710
|
+
factory: (sceneExplorerService, sceneContext, watcherService) => {
|
|
20046
20711
|
const scene = sceneContext.currentScene;
|
|
20047
20712
|
if (!scene) {
|
|
20048
20713
|
return undefined;
|
|
@@ -20055,16 +20720,8 @@ const SkeletonExplorerServiceDefinition = {
|
|
|
20055
20720
|
getEntityChildren: (skeletonOrBone) => skeletonOrBone.getChildren(),
|
|
20056
20721
|
getEntityDisplayInfo: (skeletonOrBone) => {
|
|
20057
20722
|
const onChangeObservable = new Observable();
|
|
20058
|
-
const nameHookToken =
|
|
20059
|
-
|
|
20060
|
-
});
|
|
20061
|
-
const parentHookToken = skeletonOrBone instanceof Skeleton
|
|
20062
|
-
? null
|
|
20063
|
-
: InterceptProperty(skeletonOrBone, "parent", {
|
|
20064
|
-
afterSet: () => {
|
|
20065
|
-
boneMovedObservable.notifyObservers(skeletonOrBone);
|
|
20066
|
-
},
|
|
20067
|
-
});
|
|
20723
|
+
const nameHookToken = watcherService.watchProperty(skeletonOrBone, "name", () => onChangeObservable.notifyObservers());
|
|
20724
|
+
const parentHookToken = skeletonOrBone instanceof Skeleton ? null : watcherService.watchProperty(skeletonOrBone, "parent", () => boneMovedObservable.notifyObservers(skeletonOrBone));
|
|
20068
20725
|
return {
|
|
20069
20726
|
get name() {
|
|
20070
20727
|
return skeletonOrBone.name;
|
|
@@ -20092,8 +20749,8 @@ const SkeletonExplorerServiceDefinition = {
|
|
|
20092
20749
|
|
|
20093
20750
|
const SoundExplorerServiceDefinition = {
|
|
20094
20751
|
friendlyName: "Sound Explorer",
|
|
20095
|
-
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity],
|
|
20096
|
-
factory: (sceneExplorerService, sceneContext) => {
|
|
20752
|
+
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity, WatcherServiceIdentity],
|
|
20753
|
+
factory: (sceneExplorerService, sceneContext, watcherService) => {
|
|
20097
20754
|
const scene = sceneContext.currentScene;
|
|
20098
20755
|
if (!scene) {
|
|
20099
20756
|
return undefined;
|
|
@@ -20119,25 +20776,14 @@ const SoundExplorerServiceDefinition = {
|
|
|
20119
20776
|
// If _mainSoundTrack is already defined, set up hooks immediately.
|
|
20120
20777
|
hookMainSoundTrack(scene.mainSoundTrack);
|
|
20121
20778
|
// Watch for _mainSoundTrack being set (it is lazily created by the mainSoundTrack getter in audioSceneComponent.ts).
|
|
20122
|
-
const mainSoundTrackHook =
|
|
20123
|
-
afterSet: () => hookMainSoundTrack(scene._mainSoundTrack),
|
|
20124
|
-
});
|
|
20779
|
+
const mainSoundTrackHook = watcherService.watchProperty(scene, "_mainSoundTrack", () => hookMainSoundTrack(scene._mainSoundTrack));
|
|
20125
20780
|
const sectionRegistration = sceneExplorerService.addSection({
|
|
20126
20781
|
displayName: "Sounds",
|
|
20127
20782
|
order: 1400 /* DefaultSectionsOrder.Sounds */,
|
|
20128
20783
|
getRootEntities: () => scene.mainSoundTrack?.soundCollection ?? [],
|
|
20129
20784
|
getEntityDisplayInfo: (sound) => {
|
|
20130
20785
|
const onChangeObservable = new Observable();
|
|
20131
|
-
const
|
|
20132
|
-
afterSet: () => {
|
|
20133
|
-
onChangeObservable.notifyObservers();
|
|
20134
|
-
},
|
|
20135
|
-
});
|
|
20136
|
-
const nameHookToken = InterceptProperty(sound, "name", {
|
|
20137
|
-
afterSet: () => {
|
|
20138
|
-
onChangeObservable.notifyObservers();
|
|
20139
|
-
},
|
|
20140
|
-
});
|
|
20786
|
+
const nameHookToken = watcherService.watchProperty(sound, "name", () => onChangeObservable.notifyObservers());
|
|
20141
20787
|
return {
|
|
20142
20788
|
get name() {
|
|
20143
20789
|
return sound.name;
|
|
@@ -20145,7 +20791,6 @@ const SoundExplorerServiceDefinition = {
|
|
|
20145
20791
|
onChange: onChangeObservable,
|
|
20146
20792
|
dispose: () => {
|
|
20147
20793
|
nameHookToken.dispose();
|
|
20148
|
-
displayNameHookToken.dispose();
|
|
20149
20794
|
onChangeObservable.clear();
|
|
20150
20795
|
},
|
|
20151
20796
|
};
|
|
@@ -20169,8 +20814,8 @@ const SoundExplorerServiceDefinition = {
|
|
|
20169
20814
|
|
|
20170
20815
|
const SpriteManagerExplorerServiceDefinition = {
|
|
20171
20816
|
friendlyName: "Sprite Manager Explorer",
|
|
20172
|
-
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity],
|
|
20173
|
-
factory: (sceneExplorerService, sceneContext) => {
|
|
20817
|
+
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity, WatcherServiceIdentity],
|
|
20818
|
+
factory: (sceneExplorerService, sceneContext, watcherService) => {
|
|
20174
20819
|
const scene = sceneContext.currentScene;
|
|
20175
20820
|
if (!scene) {
|
|
20176
20821
|
return undefined;
|
|
@@ -20182,9 +20827,7 @@ const SpriteManagerExplorerServiceDefinition = {
|
|
|
20182
20827
|
getEntityChildren: (spriteEntity) => (spriteEntity instanceof Sprite ? [] : spriteEntity.sprites),
|
|
20183
20828
|
getEntityDisplayInfo: (spriteEntity) => {
|
|
20184
20829
|
const onChangeObservable = new Observable();
|
|
20185
|
-
const nameHookToken =
|
|
20186
|
-
afterSet: () => onChangeObservable.notifyObservers(),
|
|
20187
|
-
});
|
|
20830
|
+
const nameHookToken = watcherService.watchProperty(spriteEntity, "name", () => onChangeObservable.notifyObservers());
|
|
20188
20831
|
return {
|
|
20189
20832
|
get name() {
|
|
20190
20833
|
return spriteEntity.name;
|
|
@@ -20242,8 +20885,8 @@ const SpriteManagerExplorerServiceDefinition = {
|
|
|
20242
20885
|
|
|
20243
20886
|
const TextureExplorerServiceDefinition = {
|
|
20244
20887
|
friendlyName: "Texture Explorer",
|
|
20245
|
-
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity],
|
|
20246
|
-
factory: (sceneExplorerService, sceneContext) => {
|
|
20888
|
+
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity, WatcherServiceIdentity],
|
|
20889
|
+
factory: (sceneExplorerService, sceneContext, watcherService) => {
|
|
20247
20890
|
const scene = sceneContext.currentScene;
|
|
20248
20891
|
if (!scene) {
|
|
20249
20892
|
return undefined;
|
|
@@ -20254,16 +20897,8 @@ const TextureExplorerServiceDefinition = {
|
|
|
20254
20897
|
getRootEntities: () => scene.textures.filter((texture) => texture.getClassName() !== "AdvancedDynamicTexture"),
|
|
20255
20898
|
getEntityDisplayInfo: (texture) => {
|
|
20256
20899
|
const onChangeObservable = new Observable();
|
|
20257
|
-
const displayNameHookToken =
|
|
20258
|
-
|
|
20259
|
-
onChangeObservable.notifyObservers();
|
|
20260
|
-
},
|
|
20261
|
-
});
|
|
20262
|
-
const nameHookToken = InterceptProperty(texture, "name", {
|
|
20263
|
-
afterSet: () => {
|
|
20264
|
-
onChangeObservable.notifyObservers();
|
|
20265
|
-
},
|
|
20266
|
-
});
|
|
20900
|
+
const displayNameHookToken = watcherService.watchProperty(texture, "displayName", () => onChangeObservable.notifyObservers());
|
|
20901
|
+
const nameHookToken = watcherService.watchProperty(texture, "name", () => onChangeObservable.notifyObservers());
|
|
20267
20902
|
return {
|
|
20268
20903
|
get name() {
|
|
20269
20904
|
return texture.displayName || texture.name || `${texture.getClassName() || "Unnamed Texture"} (${texture.uniqueId})`;
|
|
@@ -20813,97 +21448,6 @@ const GLTFValidationServiceDefinition = {
|
|
|
20813
21448
|
},
|
|
20814
21449
|
};
|
|
20815
21450
|
|
|
20816
|
-
const HighlightSelectedEntitySettingDescriptor = {
|
|
20817
|
-
key: "HighlightSelectedEntity",
|
|
20818
|
-
defaultValue: true,
|
|
20819
|
-
};
|
|
20820
|
-
const HighlightServiceDefinition = {
|
|
20821
|
-
friendlyName: "Highlight Service",
|
|
20822
|
-
consumes: [SelectionServiceIdentity, SceneContextIdentity, SettingsStoreIdentity, ThemeServiceIdentity, GizmoServiceIdentity],
|
|
20823
|
-
factory: (selectionService, sceneContext, settingsStore, themeService, gizmoService) => {
|
|
20824
|
-
let outlineLayer = null;
|
|
20825
|
-
let utilityLayer = null;
|
|
20826
|
-
let currentScene = null;
|
|
20827
|
-
let activeCameraObserver = null;
|
|
20828
|
-
function disposeOutlineLayer() {
|
|
20829
|
-
outlineLayer?.dispose();
|
|
20830
|
-
outlineLayer = null;
|
|
20831
|
-
utilityLayer?.dispose();
|
|
20832
|
-
utilityLayer = null;
|
|
20833
|
-
currentScene = null;
|
|
20834
|
-
}
|
|
20835
|
-
function getOrCreateOutlineLayer(scene) {
|
|
20836
|
-
if (!outlineLayer || currentScene !== scene) {
|
|
20837
|
-
disposeOutlineLayer();
|
|
20838
|
-
utilityLayer = gizmoService.getUtilityLayer(scene);
|
|
20839
|
-
outlineLayer = new SelectionOutlineLayer("InspectorSelectionOutline", utilityLayer.value.utilityLayerScene);
|
|
20840
|
-
updateColor(outlineLayer);
|
|
20841
|
-
currentScene = scene;
|
|
20842
|
-
}
|
|
20843
|
-
return outlineLayer;
|
|
20844
|
-
}
|
|
20845
|
-
function updateColor(outlineLayer) {
|
|
20846
|
-
outlineLayer.outlineColor = Color3.FromHexString(themeService.theme.colorBrandForeground1);
|
|
20847
|
-
}
|
|
20848
|
-
function updateHighlight() {
|
|
20849
|
-
const scene = sceneContext.currentScene;
|
|
20850
|
-
const entity = selectionService.selectedEntity instanceof AbstractMesh && !(selectionService.selectedEntity instanceof GaussianSplattingMesh)
|
|
20851
|
-
? selectionService.selectedEntity
|
|
20852
|
-
: null;
|
|
20853
|
-
if (!entity || !settingsStore.readSetting(HighlightSelectedEntitySettingDescriptor) || !scene || !scene.activeCamera) {
|
|
20854
|
-
disposeOutlineLayer();
|
|
20855
|
-
return;
|
|
20856
|
-
}
|
|
20857
|
-
const layer = getOrCreateOutlineLayer(scene);
|
|
20858
|
-
layer.clearSelection();
|
|
20859
|
-
layer.addSelection(entity);
|
|
20860
|
-
}
|
|
20861
|
-
function watchActiveCamera(scene) {
|
|
20862
|
-
activeCameraObserver?.remove();
|
|
20863
|
-
activeCameraObserver = null;
|
|
20864
|
-
if (scene) {
|
|
20865
|
-
activeCameraObserver = scene.onActiveCameraChanged.add(updateHighlight);
|
|
20866
|
-
}
|
|
20867
|
-
}
|
|
20868
|
-
// React to theme changes.
|
|
20869
|
-
const themeObserver = themeService.onChanged.add(() => {
|
|
20870
|
-
if (outlineLayer) {
|
|
20871
|
-
updateColor(outlineLayer);
|
|
20872
|
-
}
|
|
20873
|
-
});
|
|
20874
|
-
// React to selection changes.
|
|
20875
|
-
const selectionObserver = selectionService.onSelectedEntityChanged.add(updateHighlight);
|
|
20876
|
-
// React to scene changes.
|
|
20877
|
-
const sceneObserver = sceneContext.currentSceneObservable.add(() => {
|
|
20878
|
-
// Dispose the old layer when the scene changes.
|
|
20879
|
-
disposeOutlineLayer();
|
|
20880
|
-
watchActiveCamera(sceneContext.currentScene);
|
|
20881
|
-
updateHighlight();
|
|
20882
|
-
});
|
|
20883
|
-
// React to setting changes.
|
|
20884
|
-
const settingObserver = settingsStore.onChanged.add((setting) => {
|
|
20885
|
-
if (setting === HighlightSelectedEntitySettingDescriptor.key) {
|
|
20886
|
-
updateHighlight();
|
|
20887
|
-
}
|
|
20888
|
-
});
|
|
20889
|
-
// Watch active camera on the initial scene.
|
|
20890
|
-
watchActiveCamera(sceneContext.currentScene);
|
|
20891
|
-
// Initial update.
|
|
20892
|
-
updateHighlight();
|
|
20893
|
-
return {
|
|
20894
|
-
dispose: () => {
|
|
20895
|
-
themeObserver.remove();
|
|
20896
|
-
selectionObserver.remove();
|
|
20897
|
-
sceneObserver.remove();
|
|
20898
|
-
settingObserver.remove();
|
|
20899
|
-
activeCameraObserver?.remove();
|
|
20900
|
-
activeCameraObserver = null;
|
|
20901
|
-
disposeOutlineLayer();
|
|
20902
|
-
},
|
|
20903
|
-
};
|
|
20904
|
-
},
|
|
20905
|
-
};
|
|
20906
|
-
|
|
20907
21451
|
const PickingToolbar = (props) => {
|
|
20908
21452
|
const { scene, selectEntity, gizmoService, ignoreBackfaces, highlightSelectedEntity, onHighlightSelectedEntityChange } = props;
|
|
20909
21453
|
const meshDataCache = useMemo(() => new WeakMap(), [scene]);
|
|
@@ -21012,7 +21556,7 @@ const PickingServiceDefinition = {
|
|
|
21012
21556
|
key: "Picking Service",
|
|
21013
21557
|
verticalLocation: "top",
|
|
21014
21558
|
horizontalLocation: "left",
|
|
21015
|
-
|
|
21559
|
+
teachingMoment: false,
|
|
21016
21560
|
component: () => {
|
|
21017
21561
|
const scene = useObservableState(() => sceneContext.currentScene, sceneContext.currentSceneObservable);
|
|
21018
21562
|
const selectEntity = useCallback((entity) => (selectionService.selectedEntity = entity), []);
|
|
@@ -21068,7 +21612,11 @@ const UserFeedbackServiceDefinition = {
|
|
|
21068
21612
|
key: "User Feedback",
|
|
21069
21613
|
verticalLocation: "bottom",
|
|
21070
21614
|
horizontalLocation: "right",
|
|
21071
|
-
|
|
21615
|
+
order: 100 /* DefaultToolbarItemOrder.Feedback */,
|
|
21616
|
+
teachingMoment: {
|
|
21617
|
+
title: "Feedback",
|
|
21618
|
+
description: "Press this button to give feedback on Inspector v2 and help us prioritize new features and improvements!",
|
|
21619
|
+
},
|
|
21072
21620
|
component: () => {
|
|
21073
21621
|
return (jsx(Tooltip, { content: "Give Feedback on Inspector v2", children: jsx(Button, { appearance: "subtle", icon: PersonFeedbackRegular, onClick: () => window.open("https://forum.babylonjs.com/t/introducing-inspector-v2/60937", "_blank") }) }));
|
|
21074
21622
|
},
|
|
@@ -21259,7 +21807,9 @@ function ShowInspector(scene, options = {}) {
|
|
|
21259
21807
|
// Tools pane tab and related services.
|
|
21260
21808
|
ToolsServiceDefinition, ExportServiceDefinition, GLTFAnimationImportServiceDefinition, GLTFLoaderOptionsServiceDefinition, GLTFValidationServiceDefinition, CaptureToolsDefinition,
|
|
21261
21809
|
// Settings pane tab and related services.
|
|
21262
|
-
SettingsServiceDefinition, ShellSettingsServiceDefinition,
|
|
21810
|
+
SettingsServiceDefinition, WatcherSettingsServiceDefinition, ShellSettingsServiceDefinition,
|
|
21811
|
+
// Adds a button to refresh all properties manually (when watcher is in "manual" mode).
|
|
21812
|
+
WatcherRefreshToolbarServiceDefinition,
|
|
21263
21813
|
// Tracks entity selection state (e.g. which Mesh or Material or other entity is currently selected in scene explorer and bound to the properties pane, etc.).
|
|
21264
21814
|
SelectionServiceDefinition,
|
|
21265
21815
|
// Gizmos for manipulating objects in the scene.
|
|
@@ -21493,19 +22043,15 @@ function ConvertOptions(v1Options) {
|
|
|
21493
22043
|
const { additionalNodes } = v1Options;
|
|
21494
22044
|
const additionalNodesServiceDefinition = {
|
|
21495
22045
|
friendlyName: "Additional Nodes (Backward Compatibility)",
|
|
21496
|
-
consumes: [SceneExplorerServiceIdentity],
|
|
21497
|
-
factory: (sceneExplorerService) => {
|
|
22046
|
+
consumes: [SceneExplorerServiceIdentity, WatcherServiceIdentity],
|
|
22047
|
+
factory: (sceneExplorerService, watcherService) => {
|
|
21498
22048
|
const sceneExplorerSectionRegistrations = additionalNodes.map((node) => sceneExplorerService.addSection({
|
|
21499
22049
|
displayName: node.name,
|
|
21500
22050
|
order: Number.MAX_SAFE_INTEGER,
|
|
21501
22051
|
getRootEntities: () => node.getContent(),
|
|
21502
22052
|
getEntityDisplayInfo: (entity) => {
|
|
21503
22053
|
const onChangeObservable = new Observable();
|
|
21504
|
-
const nameHookToken =
|
|
21505
|
-
afterSet: () => {
|
|
21506
|
-
onChangeObservable.notifyObservers();
|
|
21507
|
-
},
|
|
21508
|
-
});
|
|
22054
|
+
const nameHookToken = watcherService.watchProperty(entity, "name", () => onChangeObservable.notifyObservers());
|
|
21509
22055
|
return {
|
|
21510
22056
|
get name() {
|
|
21511
22057
|
return entity.name;
|
|
@@ -21537,7 +22083,7 @@ function ConvertOptions(v1Options) {
|
|
|
21537
22083
|
consumes: [SceneExplorerServiceIdentity],
|
|
21538
22084
|
factory: (sceneExplorerService) => {
|
|
21539
22085
|
const sceneExplorerCommandRegistrations = explorerExtensibility.flatMap((command) => command.entries.map((entry) => sceneExplorerService.addEntityCommand({
|
|
21540
|
-
predicate: (entity) => command.predicate(entity),
|
|
22086
|
+
predicate: (entity) => typeof entity === "object" && command.predicate(entity),
|
|
21541
22087
|
getCommand: (entity) => {
|
|
21542
22088
|
return {
|
|
21543
22089
|
displayName: entry.label,
|
|
@@ -22096,5 +22642,5 @@ const TextAreaPropertyLine = (props) => {
|
|
|
22096
22642
|
// Attach Inspector v2 to Scene.debugLayer as a side effect for back compat.
|
|
22097
22643
|
AttachDebugLayer();
|
|
22098
22644
|
|
|
22099
|
-
export {
|
|
22100
|
-
//# sourceMappingURL=index-
|
|
22645
|
+
export { useObservableCollection as $, Accordion as A, Button as B, CheckboxPropertyLine as C, DebugServiceIdentity as D, ErrorBoundary as E, ExtensibleAccordion as F, GizmoServiceIdentity as G, Theme as H, Inspector as I, TeachingMoment as J, PropertyContext as K, LinkToEntity as L, MessageBar as M, NumberInputPropertyLine as N, usePropertyChangedNotifier as O, Popover as P, BuiltInsExtensionFeed as Q, useVector3Property as R, SpinButtonPropertyLine as S, TextInputPropertyLine as T, useColor3Property as U, Vector3PropertyLine as V, useColor4Property as W, useQuaternionProperty as X, MakePropertyHook as Y, useInterceptObservable as Z, useEventfulState as _, useProperty as a, Pane as a$, useOrderedObservableCollection as a0, usePollingObservable as a1, useResource as a2, useAsyncResource as a3, useSetting as a4, useAngleConverters as a5, MakeTeachingMoment as a6, MakeDialogTeachingMoment as a7, MakePopoverTeachingMoment as a8, useThemeMode as a9, Color3GradientComponent as aA, Color4GradientComponent as aB, ColorStepGradientComponent as aC, InfoLabel as aD, MakeLazyComponent as aE, List as aF, MaterialSelector as aG, NodeSelector as aH, PositionedPopover as aI, SearchBar as aJ, SearchBox as aK, SkeletonSelector as aL, SpinButton as aM, Switch as aN, SyncedSliderInput as aO, Textarea as aP, TextInput as aQ, TextureSelector as aR, ToastProvider as aS, ToggleButton as aT, Tooltip as aU, UploadButton as aV, ChildWindow as aW, FileUploadLine as aX, FactorGradientList as aY, Color3GradientList as aZ, Color4GradientList as a_, useTheme as aa, InterceptFunction as ab, GetPropertyDescriptor as ac, IsPropertyReadonly as ad, InterceptProperty as ae, ObservableCollection as af, ConstructorFactory as ag, SelectionServiceDefinition as ah, SettingsStore as ai, ShowInspector as aj, useKeyListener as ak, useKeyState as al, useEventListener as am, AccordionSectionItem as an, Checkbox as ao, Collapse as ap, ColorPickerPopup as aq, InputHexField as ar, InputHsvField as as, ComboBox as at, DraggableLine as au, Dropdown as av, NumberDropdown as aw, StringDropdown as ax, EntitySelector as ay, FactorGradientComponent as az, ShellServiceIdentity as b, TextureUpload as b0, BooleanBadgePropertyLine as b1, Color3PropertyLine as b2, Color4PropertyLine as b3, ComboBoxPropertyLine as b4, HexPropertyLine as b5, LinkPropertyLine as b6, PropertyLine as b7, LineContainer as b8, PlaceholderPropertyLine as b9, StringifiedPropertyLine as ba, SwitchPropertyLine as bb, SyncedSliderPropertyLine as bc, TextAreaPropertyLine as bd, TextPropertyLine as be, RotationVectorPropertyLine as bf, QuaternionPropertyLine as bg, Vector2PropertyLine as bh, Vector4PropertyLine as bi, SceneContextIdentity as c, SelectionServiceIdentity as d, useObservableState as e, AccordionSection as f, ButtonLine as g, ToolsServiceIdentity as h, useExtensionManager as i, Link as j, SidePaneContainer as k, PropertiesServiceIdentity as l, SceneExplorerServiceIdentity as m, SettingsServiceIdentity as n, StatsServiceIdentity as o, ThemeServiceIdentity as p, SettingsStoreIdentity as q, ConvertOptions as r, AttachDebugLayer as s, DetachDebugLayer as t, useToast as u, NumberDropdownPropertyLine as v, StringDropdownPropertyLine as w, BoundProperty as x, Property as y, LinkToEntityPropertyLine as z };
|
|
22646
|
+
//# sourceMappingURL=index-BvYg0Psk.js.map
|