@3driseai/3drise-viewer 0.1.40 → 0.1.42
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/dist/animationsV2/ClipsRuntime.d.ts.map +1 -1
- package/dist/animationsV2/ClipsRuntime.js +9 -5
- package/dist/animationsV2/clipUtils.d.ts +9 -0
- package/dist/animationsV2/clipUtils.d.ts.map +1 -1
- package/dist/animationsV2/clipUtils.js +11 -2
- package/dist/animationsV2/normalize.d.ts.map +1 -1
- package/dist/animationsV2/normalize.js +2 -0
- package/dist/animationsV2/types.d.ts +4 -0
- package/dist/animationsV2/types.d.ts.map +1 -1
- package/dist/api/sceneHandle.d.ts +40 -3
- package/dist/api/sceneHandle.d.ts.map +1 -1
- package/dist/api/sceneHandle.js +40 -0
- package/dist/components/GalleryObject.d.ts +13 -0
- package/dist/components/GalleryObject.d.ts.map +1 -0
- package/dist/components/GalleryObject.js +46 -0
- package/dist/components/ImageObject.d.ts +9 -0
- package/dist/components/ImageObject.d.ts.map +1 -0
- package/dist/components/ImageObject.js +26 -0
- package/dist/components/ObjectNode.d.ts.map +1 -1
- package/dist/components/ObjectNode.js +11 -5
- package/dist/components/ProjectLoader.d.ts +2 -0
- package/dist/components/ProjectLoader.d.ts.map +1 -1
- package/dist/components/ProjectLoader.js +2 -2
- package/dist/components/SceneBuilder.d.ts +10 -0
- package/dist/components/SceneBuilder.d.ts.map +1 -1
- package/dist/components/SceneBuilder.js +3 -2
- package/dist/components/gallery/BasicCarouselGallery.d.ts +18 -0
- package/dist/components/gallery/BasicCarouselGallery.d.ts.map +1 -0
- package/dist/components/gallery/BasicCarouselGallery.js +9 -0
- package/dist/components/gallery/ContourLinesGallery.d.ts +18 -0
- package/dist/components/gallery/ContourLinesGallery.d.ts.map +1 -0
- package/dist/components/gallery/ContourLinesGallery.js +9 -0
- package/dist/components/gallery/DepthLayersGallery.d.ts +18 -0
- package/dist/components/gallery/DepthLayersGallery.d.ts.map +1 -0
- package/dist/components/gallery/DepthLayersGallery.js +9 -0
- package/dist/components/gallery/EngravingGallery.d.ts +18 -0
- package/dist/components/gallery/EngravingGallery.d.ts.map +1 -0
- package/dist/components/gallery/EngravingGallery.js +9 -0
- package/dist/components/gallery/FlowLinesGallery.d.ts +18 -0
- package/dist/components/gallery/FlowLinesGallery.d.ts.map +1 -0
- package/dist/components/gallery/FlowLinesGallery.js +9 -0
- package/dist/components/gallery/FoldGallery.d.ts +18 -0
- package/dist/components/gallery/FoldGallery.d.ts.map +1 -0
- package/dist/components/gallery/FoldGallery.js +9 -0
- package/dist/components/gallery/GlassPanesGallery.d.ts +18 -0
- package/dist/components/gallery/GlassPanesGallery.d.ts.map +1 -0
- package/dist/components/gallery/GlassPanesGallery.js +9 -0
- package/dist/components/gallery/HaloGallery.d.ts +18 -0
- package/dist/components/gallery/HaloGallery.d.ts.map +1 -0
- package/dist/components/gallery/HaloGallery.js +9 -0
- package/dist/components/gallery/LightThreadsGallery.d.ts +18 -0
- package/dist/components/gallery/LightThreadsGallery.d.ts.map +1 -0
- package/dist/components/gallery/LightThreadsGallery.js +9 -0
- package/dist/components/gallery/LoomGallery.d.ts +18 -0
- package/dist/components/gallery/LoomGallery.d.ts.map +1 -0
- package/dist/components/gallery/LoomGallery.js +9 -0
- package/dist/components/gallery/PaperLanternsGallery.d.ts +18 -0
- package/dist/components/gallery/PaperLanternsGallery.d.ts.map +1 -0
- package/dist/components/gallery/PaperLanternsGallery.js +9 -0
- package/dist/components/gallery/ParticleShiftGallery.d.ts +18 -0
- package/dist/components/gallery/ParticleShiftGallery.d.ts.map +1 -0
- package/dist/components/gallery/ParticleShiftGallery.js +9 -0
- package/dist/components/gallery/PinscreenGallery.d.ts +18 -0
- package/dist/components/gallery/PinscreenGallery.d.ts.map +1 -0
- package/dist/components/gallery/PinscreenGallery.js +9 -0
- package/dist/components/gallery/PopUpGallery.d.ts +18 -0
- package/dist/components/gallery/PopUpGallery.d.ts.map +1 -0
- package/dist/components/gallery/PopUpGallery.js +9 -0
- package/dist/components/gallery/PrismSplitGallery.d.ts +18 -0
- package/dist/components/gallery/PrismSplitGallery.d.ts.map +1 -0
- package/dist/components/gallery/PrismSplitGallery.js +9 -0
- package/dist/components/gallery/RidgelinesGallery.d.ts +18 -0
- package/dist/components/gallery/RidgelinesGallery.d.ts.map +1 -0
- package/dist/components/gallery/RidgelinesGallery.js +9 -0
- package/dist/components/gallery/ShadowBoxGallery.d.ts +18 -0
- package/dist/components/gallery/ShadowBoxGallery.d.ts.map +1 -0
- package/dist/components/gallery/ShadowBoxGallery.js +9 -0
- package/dist/components/gallery/SilkGallery.d.ts +18 -0
- package/dist/components/gallery/SilkGallery.d.ts.map +1 -0
- package/dist/components/gallery/SilkGallery.js +9 -0
- package/dist/components/gallery/SlatsGallery.d.ts +18 -0
- package/dist/components/gallery/SlatsGallery.d.ts.map +1 -0
- package/dist/components/gallery/SlatsGallery.js +9 -0
- package/dist/components/gallery/StrataGallery.d.ts +18 -0
- package/dist/components/gallery/StrataGallery.d.ts.map +1 -0
- package/dist/components/gallery/StrataGallery.js +9 -0
- package/dist/components/gallery/TerracesGallery.d.ts +18 -0
- package/dist/components/gallery/TerracesGallery.d.ts.map +1 -0
- package/dist/components/gallery/TerracesGallery.js +9 -0
- package/dist/components/gallery/WireReliefGallery.d.ts +18 -0
- package/dist/components/gallery/WireReliefGallery.d.ts.map +1 -0
- package/dist/components/gallery/WireReliefGallery.js +9 -0
- package/dist/components/gallery/index.d.ts +46 -0
- package/dist/components/gallery/index.d.ts.map +1 -0
- package/dist/components/gallery/index.js +22 -0
- package/dist/components/gallery/kit/GalleryVariant.d.ts +68 -0
- package/dist/components/gallery/kit/GalleryVariant.d.ts.map +1 -0
- package/dist/components/gallery/kit/GalleryVariant.js +231 -0
- package/dist/components/gallery/kit/feeds.d.ts +7 -0
- package/dist/components/gallery/kit/feeds.d.ts.map +1 -0
- package/dist/components/gallery/kit/feeds.js +83 -0
- package/dist/components/gallery/kit/galleryKit.d.ts +118 -0
- package/dist/components/gallery/kit/galleryKit.d.ts.map +1 -0
- package/dist/components/gallery/kit/galleryKit.js +298 -0
- package/dist/components/gallery/kit/lookBase.d.ts +157 -0
- package/dist/components/gallery/kit/lookBase.d.ts.map +1 -0
- package/dist/components/gallery/kit/lookBase.js +211 -0
- package/dist/components/gallery/kit/pictures.d.ts +14 -0
- package/dist/components/gallery/kit/pictures.d.ts.map +1 -0
- package/dist/components/gallery/kit/pictures.js +81 -0
- package/dist/components/gallery/looks/basicCarousel.d.ts +177 -0
- package/dist/components/gallery/looks/basicCarousel.d.ts.map +1 -0
- package/dist/components/gallery/looks/basicCarousel.js +315 -0
- package/dist/components/gallery/looks/contourLines.d.ts +153 -0
- package/dist/components/gallery/looks/contourLines.d.ts.map +1 -0
- package/dist/components/gallery/looks/contourLines.js +91 -0
- package/dist/components/gallery/looks/depthLayers.d.ts +155 -0
- package/dist/components/gallery/looks/depthLayers.d.ts.map +1 -0
- package/dist/components/gallery/looks/depthLayers.js +87 -0
- package/dist/components/gallery/looks/engraving.d.ts +162 -0
- package/dist/components/gallery/looks/engraving.d.ts.map +1 -0
- package/dist/components/gallery/looks/engraving.js +96 -0
- package/dist/components/gallery/looks/flowLines.d.ts +180 -0
- package/dist/components/gallery/looks/flowLines.d.ts.map +1 -0
- package/dist/components/gallery/looks/flowLines.js +113 -0
- package/dist/components/gallery/looks/fold.d.ts +209 -0
- package/dist/components/gallery/looks/fold.d.ts.map +1 -0
- package/dist/components/gallery/looks/fold.js +155 -0
- package/dist/components/gallery/looks/glassPanes.d.ts +133 -0
- package/dist/components/gallery/looks/glassPanes.d.ts.map +1 -0
- package/dist/components/gallery/looks/glassPanes.js +208 -0
- package/dist/components/gallery/looks/halo.d.ts +100 -0
- package/dist/components/gallery/looks/halo.d.ts.map +1 -0
- package/dist/components/gallery/looks/halo.js +119 -0
- package/dist/components/gallery/looks/lightThreads.d.ts +199 -0
- package/dist/components/gallery/looks/lightThreads.d.ts.map +1 -0
- package/dist/components/gallery/looks/lightThreads.js +246 -0
- package/dist/components/gallery/looks/loom.d.ts +238 -0
- package/dist/components/gallery/looks/loom.d.ts.map +1 -0
- package/dist/components/gallery/looks/loom.js +256 -0
- package/dist/components/gallery/looks/paperLanterns.d.ts +181 -0
- package/dist/components/gallery/looks/paperLanterns.d.ts.map +1 -0
- package/dist/components/gallery/looks/paperLanterns.js +399 -0
- package/dist/components/gallery/looks/particleShift.d.ts +193 -0
- package/dist/components/gallery/looks/particleShift.d.ts.map +1 -0
- package/dist/components/gallery/looks/particleShift.js +282 -0
- package/dist/components/gallery/looks/pinscreen.d.ts +175 -0
- package/dist/components/gallery/looks/pinscreen.d.ts.map +1 -0
- package/dist/components/gallery/looks/pinscreen.js +96 -0
- package/dist/components/gallery/looks/popUp.d.ts +206 -0
- package/dist/components/gallery/looks/popUp.d.ts.map +1 -0
- package/dist/components/gallery/looks/popUp.js +132 -0
- package/dist/components/gallery/looks/prismSplit.d.ts +151 -0
- package/dist/components/gallery/looks/prismSplit.d.ts.map +1 -0
- package/dist/components/gallery/looks/prismSplit.js +84 -0
- package/dist/components/gallery/looks/ridgelines.d.ts +179 -0
- package/dist/components/gallery/looks/ridgelines.d.ts.map +1 -0
- package/dist/components/gallery/looks/ridgelines.js +111 -0
- package/dist/components/gallery/looks/shadowBox.d.ts +152 -0
- package/dist/components/gallery/looks/shadowBox.d.ts.map +1 -0
- package/dist/components/gallery/looks/shadowBox.js +82 -0
- package/dist/components/gallery/looks/silk.d.ts +140 -0
- package/dist/components/gallery/looks/silk.d.ts.map +1 -0
- package/dist/components/gallery/looks/silk.js +346 -0
- package/dist/components/gallery/looks/slats.d.ts +176 -0
- package/dist/components/gallery/looks/slats.d.ts.map +1 -0
- package/dist/components/gallery/looks/slats.js +93 -0
- package/dist/components/gallery/looks/strata.d.ts +172 -0
- package/dist/components/gallery/looks/strata.d.ts.map +1 -0
- package/dist/components/gallery/looks/strata.js +88 -0
- package/dist/components/gallery/looks/terraces.d.ts +168 -0
- package/dist/components/gallery/looks/terraces.d.ts.map +1 -0
- package/dist/components/gallery/looks/terraces.js +91 -0
- package/dist/components/gallery/looks/wireRelief.d.ts +153 -0
- package/dist/components/gallery/looks/wireRelief.d.ts.map +1 -0
- package/dist/components/gallery/looks/wireRelief.js +128 -0
- package/dist/components/image/ContourImage.d.ts +7 -0
- package/dist/components/image/ContourImage.d.ts.map +1 -0
- package/dist/components/image/ContourImage.js +6 -0
- package/dist/components/image/EngravingImage.d.ts +7 -0
- package/dist/components/image/EngravingImage.d.ts.map +1 -0
- package/dist/components/image/EngravingImage.js +6 -0
- package/dist/components/image/FrostImage.d.ts +7 -0
- package/dist/components/image/FrostImage.d.ts.map +1 -0
- package/dist/components/image/FrostImage.js +6 -0
- package/dist/components/image/LenticularImage.d.ts +7 -0
- package/dist/components/image/LenticularImage.d.ts.map +1 -0
- package/dist/components/image/LenticularImage.js +6 -0
- package/dist/components/image/PinscreenImage.d.ts +7 -0
- package/dist/components/image/PinscreenImage.d.ts.map +1 -0
- package/dist/components/image/PinscreenImage.js +6 -0
- package/dist/components/image/PrismImage.d.ts +7 -0
- package/dist/components/image/PrismImage.d.ts.map +1 -0
- package/dist/components/image/PrismImage.js +6 -0
- package/dist/components/image/ProjectorImage.d.ts +7 -0
- package/dist/components/image/ProjectorImage.d.ts.map +1 -0
- package/dist/components/image/ProjectorImage.js +6 -0
- package/dist/components/image/ReliefImage.d.ts +7 -0
- package/dist/components/image/ReliefImage.d.ts.map +1 -0
- package/dist/components/image/ReliefImage.js +6 -0
- package/dist/components/image/RollImage.d.ts +7 -0
- package/dist/components/image/RollImage.d.ts.map +1 -0
- package/dist/components/image/RollImage.js +6 -0
- package/dist/components/image/ShardsImage.d.ts +126 -0
- package/dist/components/image/ShardsImage.d.ts.map +1 -0
- package/dist/components/image/ShardsImage.js +470 -0
- package/dist/components/image/core/contour.d.ts +126 -0
- package/dist/components/image/core/contour.d.ts.map +1 -0
- package/dist/components/image/core/contour.js +123 -0
- package/dist/components/image/core/engraving.d.ts +135 -0
- package/dist/components/image/core/engraving.d.ts.map +1 -0
- package/dist/components/image/core/engraving.js +163 -0
- package/dist/components/image/core/frost.d.ts +126 -0
- package/dist/components/image/core/frost.d.ts.map +1 -0
- package/dist/components/image/core/frost.js +156 -0
- package/dist/components/image/core/imageCore.d.ts +110 -0
- package/dist/components/image/core/imageCore.d.ts.map +1 -0
- package/dist/components/image/core/imageCore.js +269 -0
- package/dist/components/image/core/lenticular.d.ts +129 -0
- package/dist/components/image/core/lenticular.d.ts.map +1 -0
- package/dist/components/image/core/lenticular.js +132 -0
- package/dist/components/image/core/pinscreen.d.ts +126 -0
- package/dist/components/image/core/pinscreen.d.ts.map +1 -0
- package/dist/components/image/core/pinscreen.js +147 -0
- package/dist/components/image/core/prism.d.ts +120 -0
- package/dist/components/image/core/prism.d.ts.map +1 -0
- package/dist/components/image/core/prism.js +164 -0
- package/dist/components/image/core/projector.d.ts +135 -0
- package/dist/components/image/core/projector.d.ts.map +1 -0
- package/dist/components/image/core/projector.js +225 -0
- package/dist/components/image/core/relief.d.ts +111 -0
- package/dist/components/image/core/relief.d.ts.map +1 -0
- package/dist/components/image/core/relief.js +94 -0
- package/dist/components/image/core/roll.d.ts +114 -0
- package/dist/components/image/core/roll.d.ts.map +1 -0
- package/dist/components/image/core/roll.js +116 -0
- package/dist/components/image/imageVariant.d.ts +46 -0
- package/dist/components/image/imageVariant.d.ts.map +1 -0
- package/dist/components/image/imageVariant.js +128 -0
- package/dist/components/image/index.d.ts +22 -0
- package/dist/components/image/index.d.ts.map +1 -0
- package/dist/components/image/index.js +10 -0
- package/dist/components/image/pictureCycle.d.ts +15 -0
- package/dist/components/image/pictureCycle.d.ts.map +1 -0
- package/dist/components/image/pictureCycle.js +82 -0
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +4 -0
- package/dist/components/volumetricClouds/VolumetricCloudsPass.d.ts +9 -7
- package/dist/components/volumetricClouds/VolumetricCloudsPass.d.ts.map +1 -1
- package/dist/components/volumetricClouds/VolumetricCloudsPass.js +39 -27
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +2 -0
- package/dist/galleryRuntime.d.ts +57 -0
- package/dist/galleryRuntime.d.ts.map +1 -0
- package/dist/galleryRuntime.js +105 -0
- package/dist/hooks/useConfigLive.d.ts +1 -1
- package/dist/hooks/useConfigLive.d.ts.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/keyPad/KeyPadControls.d.ts +16 -0
- package/dist/keyPad/KeyPadControls.d.ts.map +1 -0
- package/dist/keyPad/KeyPadControls.js +176 -0
- package/dist/keyPad/index.d.ts +16 -0
- package/dist/keyPad/index.d.ts.map +1 -0
- package/dist/keyPad/index.js +8 -0
- package/dist/keyPad/keyBindings.d.ts +43 -0
- package/dist/keyPad/keyBindings.d.ts.map +1 -0
- package/dist/keyPad/keyBindings.js +92 -0
- package/dist/keyPad/keyPadAnimation.d.ts +28 -0
- package/dist/keyPad/keyPadAnimation.d.ts.map +1 -0
- package/dist/keyPad/keyPadAnimation.js +131 -0
- package/dist/keyPad/keyPadMotion.d.ts +68 -0
- package/dist/keyPad/keyPadMotion.d.ts.map +1 -0
- package/dist/keyPad/keyPadMotion.js +283 -0
- package/dist/keyPad/keyPadStore.d.ts +25 -0
- package/dist/keyPad/keyPadStore.d.ts.map +1 -0
- package/dist/keyPad/keyPadStore.js +63 -0
- package/dist/keyPad/types.d.ts +23 -0
- package/dist/keyPad/types.d.ts.map +1 -0
- package/dist/keyPad/types.js +9 -0
- package/dist/operations/catalogue/visibility.d.ts.map +1 -1
- package/dist/operations/catalogue/visibility.js +23 -1
- package/dist/shaders/clouds/CloudResolveMaterial.d.ts +2 -1
- package/dist/shaders/clouds/CloudResolveMaterial.d.ts.map +1 -1
- package/dist/shaders/clouds/CloudResolveMaterial.js +37 -12
- package/dist/shaders/clouds/CloudVolumeMaterial.d.ts +4 -0
- package/dist/shaders/clouds/CloudVolumeMaterial.d.ts.map +1 -1
- package/dist/shaders/clouds/CloudVolumeMaterial.js +15 -3
- package/dist/triggers/kinds/gallery.d.ts +2 -0
- package/dist/triggers/kinds/gallery.d.ts.map +1 -0
- package/dist/triggers/kinds/gallery.js +39 -0
- package/dist/triggers/kinds/index.d.ts +1 -0
- package/dist/triggers/kinds/index.d.ts.map +1 -1
- package/dist/triggers/kinds/index.js +1 -0
- package/dist/ui/UiLayers.d.ts +17 -1
- package/dist/ui/UiLayers.d.ts.map +1 -1
- package/dist/ui/UiLayers.js +76 -3
- package/dist/ui/index.d.ts +2 -2
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// KeyPadControls — keyboard → camera / animations (headless)
|
|
3
|
+
// =============================================================================
|
|
4
|
+
//
|
|
5
|
+
// Mounted by SceneBuilder (published / saved projects) and by the studio. Listens
|
|
6
|
+
// to the real keyboard (unless the mode is 'none') and runs the motion for whatever
|
|
7
|
+
// is held — from the keyboard or an on-screen pad (keyPadStore).
|
|
8
|
+
//
|
|
9
|
+
// Control modes (KeyPadSettings.mode, see keyPadMotion):
|
|
10
|
+
// none / orbit / fly → camera (fly: ↑↓ forward / back, ←→ turn; Alt flips the style)
|
|
11
|
+
// animation → ←/→ run the chosen animations / clips, ↑/↓ held speed
|
|
12
|
+
// them up / down (back to normal when let go)
|
|
13
|
+
// + / − zoom and Home (main view) work in every mode but 'none'. The general speed
|
|
14
|
+
// (KeyPadSettings.speed) scales every mode.
|
|
15
|
+
//
|
|
16
|
+
// Key bindings (KeyPadSettings.bindings, see keyBindings) run in every mode, 'none'
|
|
17
|
+
// included: a key that plays, applies, glides to a view or switches the mode. A
|
|
18
|
+
// binding's mode switch is live (keyPadStore.modeOverride), never saved.
|
|
19
|
+
//
|
|
20
|
+
import { useEffect, useMemo, useRef } from 'react';
|
|
21
|
+
import { HELD_MOVE_KEYS, KEYBOARD_MAP, goToMainView, isFlying, isTypingTarget, stepKeyPad } from './keyPadMotion.js';
|
|
22
|
+
import { getBoost, releaseAnimations, resolveTargets, settleBoost, stepAnimations, stepBoost } from './keyPadAnimation.js';
|
|
23
|
+
import { keyPadStore, useKeyPadHeld, useKeyPadState } from './keyPadStore.js';
|
|
24
|
+
import { bindingDown, bindingUp, comboFromEvent } from './keyBindings.js';
|
|
25
|
+
import { resolveKeyPadSettings } from './types.js';
|
|
26
|
+
const NO_CLIPS = [];
|
|
27
|
+
const NO_SEQUENCES = [];
|
|
28
|
+
const KeyPadControls = ({ settings, clips = NO_CLIPS, sequences = NO_SEQUENCES, homePosition }) => {
|
|
29
|
+
const resolved = resolveKeyPadSettings(settings);
|
|
30
|
+
const { speed, animations, bindings } = resolved;
|
|
31
|
+
// A key binding may have switched the mode (live); otherwise the saved one.
|
|
32
|
+
const { modeOverride } = useKeyPadState();
|
|
33
|
+
const mode = modeOverride ?? resolved.mode;
|
|
34
|
+
const animating = mode === 'animation';
|
|
35
|
+
const homeRef = useRef(homePosition);
|
|
36
|
+
homeRef.current = homePosition;
|
|
37
|
+
// A mode change drops held keys and latched modifiers (they meant something else).
|
|
38
|
+
useEffect(() => { keyPadStore.reset(); }, [mode]);
|
|
39
|
+
// --- Real keyboard → the same keys as an on-screen pad ------------------------
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (mode === 'none')
|
|
42
|
+
return;
|
|
43
|
+
const onDown = (e) => {
|
|
44
|
+
const k = KEYBOARD_MAP[e.key];
|
|
45
|
+
if (!k || isTypingTarget(e))
|
|
46
|
+
return;
|
|
47
|
+
if (animating && (k === 'pan' || k === 'alt'))
|
|
48
|
+
return; // no camera modifiers here
|
|
49
|
+
// No page scroll, browser zoom (Ctrl ±), back / forward (Alt ←/→), menu bar (Alt).
|
|
50
|
+
e.preventDefault();
|
|
51
|
+
if (e.repeat)
|
|
52
|
+
return;
|
|
53
|
+
if (k === 'home') {
|
|
54
|
+
if (homeRef.current)
|
|
55
|
+
goToMainView(homeRef.current);
|
|
56
|
+
}
|
|
57
|
+
else
|
|
58
|
+
keyPadStore.press(k);
|
|
59
|
+
};
|
|
60
|
+
// Release is never filtered: a key pressed on the canvas and let go in a
|
|
61
|
+
// text field must still come up.
|
|
62
|
+
const onUp = (e) => {
|
|
63
|
+
const k = KEYBOARD_MAP[e.key];
|
|
64
|
+
if (!k || k === 'home')
|
|
65
|
+
return;
|
|
66
|
+
if (k === 'alt' && !animating)
|
|
67
|
+
e.preventDefault(); // Alt released alone would focus the browser menu
|
|
68
|
+
keyPadStore.release(k);
|
|
69
|
+
};
|
|
70
|
+
const onBlur = () => keyPadStore.releaseAll();
|
|
71
|
+
window.addEventListener('keydown', onDown);
|
|
72
|
+
window.addEventListener('keyup', onUp);
|
|
73
|
+
window.addEventListener('blur', onBlur);
|
|
74
|
+
return () => {
|
|
75
|
+
window.removeEventListener('keydown', onDown);
|
|
76
|
+
window.removeEventListener('keyup', onUp);
|
|
77
|
+
window.removeEventListener('blur', onBlur);
|
|
78
|
+
keyPadStore.releaseAll();
|
|
79
|
+
};
|
|
80
|
+
}, [mode, animating]);
|
|
81
|
+
// --- Key bindings ------------------------------------------------------------------
|
|
82
|
+
const bindingsRef = useRef(bindings);
|
|
83
|
+
bindingsRef.current = bindings;
|
|
84
|
+
const modeRef = useRef(mode);
|
|
85
|
+
modeRef.current = mode;
|
|
86
|
+
const savedModeRef = useRef(resolved.mode);
|
|
87
|
+
savedModeRef.current = resolved.mode;
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
const ctx = {
|
|
90
|
+
getControlMode: () => modeRef.current,
|
|
91
|
+
// Back to the saved mode = no override.
|
|
92
|
+
setControlMode: (m) => keyPadStore.setModeOverride(m === savedModeRef.current ? null : m),
|
|
93
|
+
};
|
|
94
|
+
const held = new Map(); // by KeyboardEvent.code
|
|
95
|
+
const onDown = (e) => {
|
|
96
|
+
if (e.ctrlKey || e.metaKey || isTypingTarget(e))
|
|
97
|
+
return;
|
|
98
|
+
const combo = comboFromEvent(e);
|
|
99
|
+
const b = bindingsRef.current.find(x => x.enabled !== false && x.combo === combo);
|
|
100
|
+
if (!b)
|
|
101
|
+
return;
|
|
102
|
+
e.preventDefault();
|
|
103
|
+
if (e.repeat || held.has(e.code))
|
|
104
|
+
return;
|
|
105
|
+
const h = bindingDown(b, ctx);
|
|
106
|
+
if (h)
|
|
107
|
+
held.set(e.code, h);
|
|
108
|
+
};
|
|
109
|
+
const onUp = (e) => {
|
|
110
|
+
const h = held.get(e.code);
|
|
111
|
+
if (!h)
|
|
112
|
+
return;
|
|
113
|
+
held.delete(e.code);
|
|
114
|
+
bindingUp(h, ctx);
|
|
115
|
+
};
|
|
116
|
+
const releaseAll = () => { for (const h of held.values())
|
|
117
|
+
bindingUp(h, ctx); held.clear(); };
|
|
118
|
+
window.addEventListener('keydown', onDown);
|
|
119
|
+
window.addEventListener('keyup', onUp);
|
|
120
|
+
window.addEventListener('blur', releaseAll);
|
|
121
|
+
return () => {
|
|
122
|
+
window.removeEventListener('keydown', onDown);
|
|
123
|
+
window.removeEventListener('keyup', onUp);
|
|
124
|
+
window.removeEventListener('blur', releaseAll);
|
|
125
|
+
releaseAll();
|
|
126
|
+
};
|
|
127
|
+
}, []);
|
|
128
|
+
// The saved mode changed (studio dropdown): it wins over a key's switch.
|
|
129
|
+
useEffect(() => { keyPadStore.setModeOverride(null); }, [resolved.mode]);
|
|
130
|
+
// --- Animation targets; let go of the ones that stop being targets -------------
|
|
131
|
+
const targets = useMemo(() => resolveTargets(animations, clips, sequences), [animations, clips, sequences]);
|
|
132
|
+
const animKey = animations.join('|');
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
releaseAnimations(new Set(animating ? animKey.split('|').filter(Boolean) : []));
|
|
135
|
+
}, [animating, animKey]);
|
|
136
|
+
useEffect(() => () => releaseAnimations(), []);
|
|
137
|
+
// --- Held keys → motion --------------------------------------------------------
|
|
138
|
+
const held = useKeyPadHeld();
|
|
139
|
+
const zoom = (held.has('zoomIn') ? 1 : 0) - (held.has('zoomOut') ? 1 : 0);
|
|
140
|
+
const timeDir = animating ? (held.has('right') ? 1 : 0) - (held.has('left') ? 1 : 0) : 0;
|
|
141
|
+
const boostDir = animating ? (held.has('up') ? 1 : 0) - (held.has('down') ? 1 : 0) : 0;
|
|
142
|
+
const moving = animating
|
|
143
|
+
? zoom !== 0 || timeDir !== 0 || boostDir !== 0
|
|
144
|
+
: HELD_MOVE_KEYS.some(k => held.has(k));
|
|
145
|
+
const live = useRef({ held, zoom, speed, mode, timeDir, boostDir, targets });
|
|
146
|
+
live.current = { held, zoom, speed, mode, timeDir, boostDir, targets };
|
|
147
|
+
useEffect(() => {
|
|
148
|
+
if (!moving)
|
|
149
|
+
return;
|
|
150
|
+
let raf = 0;
|
|
151
|
+
let last = performance.now();
|
|
152
|
+
const tick = (now) => {
|
|
153
|
+
const dt = Math.min((now - last) / 1000, 0.05); // no jump after a stalled frame
|
|
154
|
+
last = now;
|
|
155
|
+
const l = live.current;
|
|
156
|
+
if (l.mode === 'animation') {
|
|
157
|
+
stepBoost(l.boostDir, dt);
|
|
158
|
+
if (l.timeDir)
|
|
159
|
+
stepAnimations(l.targets, l.timeDir, dt, l.speed * getBoost());
|
|
160
|
+
if (l.zoom)
|
|
161
|
+
stepKeyPad(l.held, l.zoom, dt, l.speed, false, false);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
stepKeyPad(l.held, l.zoom, dt, l.speed, isFlying(l.mode, l.held));
|
|
165
|
+
}
|
|
166
|
+
raf = requestAnimationFrame(tick);
|
|
167
|
+
};
|
|
168
|
+
raf = requestAnimationFrame(tick);
|
|
169
|
+
return () => {
|
|
170
|
+
cancelAnimationFrame(raf);
|
|
171
|
+
settleBoost(); // let go of ↑ / ↓: ease back to 1× after the loop stops
|
|
172
|
+
};
|
|
173
|
+
}, [moving]);
|
|
174
|
+
return null;
|
|
175
|
+
};
|
|
176
|
+
export default KeyPadControls;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { default as KeyPadControls } from './KeyPadControls';
|
|
2
|
+
export type { KeyPadControlsProps } from './KeyPadControls';
|
|
3
|
+
export { keyPadStore, useKeyPadState, useKeyPadHeld } from './keyPadStore';
|
|
4
|
+
export type { KeyPadLiveState } from './keyPadStore';
|
|
5
|
+
export { DEFAULT_KEYPAD_SETTINGS, resolveKeyPadSettings } from './types';
|
|
6
|
+
export type { KeyPadSettings } from './types';
|
|
7
|
+
export { CONTROL_MODES, KEY_HINTS, KEYBOARD_MAP, HELD_MOVE_KEYS, isFlying, goToMainView, zoomRange, distanceToT, tToDistance, setOrbitDistance, } from './keyPadMotion';
|
|
8
|
+
export type { KeyPadKey, KeyPadModifier, KeyPadControlMode, ZoomRange } from './keyPadMotion';
|
|
9
|
+
export { clipRef as keyPadClipRef, seqRef as keyPadSeqRef, formatSpeed as formatKeyPadSpeed, getBoost as getKeyPadBoost, subscribeBoost as subscribeKeyPadBoost } from './keyPadAnimation';
|
|
10
|
+
export { comboFromEvent, comboProblem, comboKeys, comboCode, keyName, isModifierCode } from './keyBindings';
|
|
11
|
+
export type { KeyBinding, KeyBindingTrigger } from './keyBindings';
|
|
12
|
+
export { captureView, glideToView } from './keyPadMotion';
|
|
13
|
+
export type { CameraView } from './keyPadMotion';
|
|
14
|
+
export { bindingDown, bindingUp } from './keyBindings';
|
|
15
|
+
export type { HeldKey, KeyBindingContext } from './keyBindings';
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/keyPad/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC7D,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC3E,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AACzE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EACH,aAAa,EAAE,SAAS,EAAE,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY,EAC9E,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,gBAAgB,GACxD,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC9F,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,IAAI,YAAY,EAAE,WAAW,IAAI,iBAAiB,EAAE,QAAQ,IAAI,cAAc,EAAE,cAAc,IAAI,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC3L,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC5G,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC1D,YAAY,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACvD,YAAY,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { default as KeyPadControls } from './KeyPadControls.js';
|
|
2
|
+
export { keyPadStore, useKeyPadState, useKeyPadHeld } from './keyPadStore.js';
|
|
3
|
+
export { DEFAULT_KEYPAD_SETTINGS, resolveKeyPadSettings } from './types.js';
|
|
4
|
+
export { CONTROL_MODES, KEY_HINTS, KEYBOARD_MAP, HELD_MOVE_KEYS, isFlying, goToMainView, zoomRange, distanceToT, tToDistance, setOrbitDistance, } from './keyPadMotion.js';
|
|
5
|
+
export { clipRef as keyPadClipRef, seqRef as keyPadSeqRef, formatSpeed as formatKeyPadSpeed, getBoost as getKeyPadBoost, subscribeBoost as subscribeKeyPadBoost } from './keyPadAnimation.js';
|
|
6
|
+
export { comboFromEvent, comboProblem, comboKeys, comboCode, keyName, isModifierCode } from './keyBindings.js';
|
|
7
|
+
export { captureView, glideToView } from './keyPadMotion.js';
|
|
8
|
+
export { bindingDown, bindingUp } from './keyBindings.js';
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { SceneCommand } from '@3driseai/3drise-core';
|
|
2
|
+
import { type CameraView, type KeyPadControlMode } from './keyPadMotion';
|
|
3
|
+
export type KeyBindingTrigger = 'press' | 'hold';
|
|
4
|
+
export interface KeyBinding {
|
|
5
|
+
id: string;
|
|
6
|
+
/** `[Alt+][Shift+]<KeyboardEvent.code>`, e.g. `Digit1`, `Shift+KeyL`. */
|
|
7
|
+
combo: string;
|
|
8
|
+
trigger: KeyBindingTrigger;
|
|
9
|
+
/** Optional name shown in the list. */
|
|
10
|
+
label?: string;
|
|
11
|
+
/** Off: kept but never runs. */
|
|
12
|
+
enabled?: boolean;
|
|
13
|
+
commands: SceneCommand[];
|
|
14
|
+
view?: CameraView | null;
|
|
15
|
+
controlMode?: KeyPadControlMode | null;
|
|
16
|
+
}
|
|
17
|
+
export declare const isModifierCode: (code: string) => boolean;
|
|
18
|
+
/** The combo a key event makes (Alt / Shift kept; Ctrl / ⌘ combos are not bindable). */
|
|
19
|
+
export declare function comboFromEvent(e: Pick<KeyboardEvent, 'code' | 'altKey' | 'shiftKey'>): string;
|
|
20
|
+
/** Why a key can't be bound (null = it can). */
|
|
21
|
+
export declare function comboProblem(e: Pick<KeyboardEvent, 'code' | 'ctrlKey' | 'metaKey'>): string | null;
|
|
22
|
+
/** One key's display name: KeyA → A, Digit1 → 1, Numpad1 → Num 1, F5 → F5. */
|
|
23
|
+
export declare function keyName(code: string): string;
|
|
24
|
+
/** A combo as keycaps: `Shift+Digit1` → ['Shift', '1']. */
|
|
25
|
+
export declare function comboKeys(combo: string): string[];
|
|
26
|
+
/** The key part of a combo (its KeyboardEvent.code). */
|
|
27
|
+
export declare const comboCode: (combo: string) => string;
|
|
28
|
+
/** What a key binding needs from where it runs (the mode lives in the host's state). */
|
|
29
|
+
export interface KeyBindingContext {
|
|
30
|
+
getControlMode(): KeyPadControlMode;
|
|
31
|
+
setControlMode(mode: KeyPadControlMode): void;
|
|
32
|
+
}
|
|
33
|
+
/** Undo info for a held key. */
|
|
34
|
+
export interface HeldKey {
|
|
35
|
+
binding: KeyBinding;
|
|
36
|
+
prevView: CameraView | null;
|
|
37
|
+
prevMode: KeyPadControlMode | null;
|
|
38
|
+
}
|
|
39
|
+
/** Key down. Returns what to undo on key up for a hold binding (null for a press). */
|
|
40
|
+
export declare function bindingDown(b: KeyBinding, ctx: KeyBindingContext): HeldKey | null;
|
|
41
|
+
/** Key up of a hold binding: switch off what it switched on, put view / mode back. */
|
|
42
|
+
export declare function bindingUp(h: HeldKey, ctx: KeyBindingContext): void;
|
|
43
|
+
//# sourceMappingURL=keyBindings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyBindings.d.ts","sourceRoot":"","sources":["../../src/keyPad/keyBindings.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,OAAO,EAA4B,KAAK,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnG,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,MAAM,CAAC;AAEjD,MAAM,WAAW,UAAU;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,iBAAiB,CAAC;IAC3B,uCAAuC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gCAAgC;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,IAAI,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IACzB,WAAW,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;CAC1C;AAQD,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,YAA6B,CAAC;AAEzE,wFAAwF;AACxF,wBAAgB,cAAc,CAAC,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,QAAQ,GAAG,UAAU,CAAC,GAAG,MAAM,CAE7F;AAED,gDAAgD;AAChD,wBAAgB,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC,GAAG,MAAM,GAAG,IAAI,CAMlG;AASD,8EAA8E;AAC9E,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK5C;AAED,2DAA2D;AAC3D,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAIjD;AAED,wDAAwD;AACxD,eAAO,MAAM,SAAS,GAAI,OAAO,MAAM,WAAiC,CAAC;AAIzE,wFAAwF;AACxF,MAAM,WAAW,iBAAiB;IAC9B,cAAc,IAAI,iBAAiB,CAAC;IACpC,cAAc,CAAC,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAAC;CACjD;AAiBD,gCAAgC;AAChC,MAAM,WAAW,OAAO;IACpB,OAAO,EAAE,UAAU,CAAC;IACpB,QAAQ,EAAE,UAAU,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAC;CACtC;AAED,sFAAsF;AACtF,wBAAgB,WAAW,CAAC,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,iBAAiB,GAAG,OAAO,GAAG,IAAI,CAgBjF;AAED,sFAAsF;AACtF,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,iBAAiB,GAAG,IAAI,CAQlE"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { runCommand } from '../triggers/engine.js';
|
|
2
|
+
import { captureView, glideToView } from './keyPadMotion.js';
|
|
3
|
+
// --- Combos ----------------------------------------------------------------------
|
|
4
|
+
const MODIFIER_CODES = new Set(['ShiftLeft', 'ShiftRight', 'AltLeft', 'AltRight', 'ControlLeft', 'ControlRight', 'MetaLeft', 'MetaRight', 'OSLeft', 'OSRight']);
|
|
5
|
+
const CAMERA_CODES = new Set(['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Equal', 'Minus', 'NumpadAdd', 'NumpadSubtract', 'Home']);
|
|
6
|
+
const STUDIO_CODES = new Set(['Escape', 'Tab', 'Backspace', 'Delete', 'CapsLock', 'ContextMenu', 'NumLock', 'ScrollLock', 'Pause', 'PrintScreen']);
|
|
7
|
+
export const isModifierCode = (code) => MODIFIER_CODES.has(code);
|
|
8
|
+
/** The combo a key event makes (Alt / Shift kept; Ctrl / ⌘ combos are not bindable). */
|
|
9
|
+
export function comboFromEvent(e) {
|
|
10
|
+
return `${e.altKey ? 'Alt+' : ''}${e.shiftKey ? 'Shift+' : ''}${e.code}`;
|
|
11
|
+
}
|
|
12
|
+
/** Why a key can't be bound (null = it can). */
|
|
13
|
+
export function comboProblem(e) {
|
|
14
|
+
if (e.ctrlKey || e.metaKey)
|
|
15
|
+
return 'Ctrl / ⌘ shortcuts belong to the browser and the studio — try the key alone, or with Shift / Alt.';
|
|
16
|
+
if (CAMERA_CODES.has(e.code))
|
|
17
|
+
return 'This key moves the camera (arrows, + / −, Home). Pick another one.';
|
|
18
|
+
if (STUDIO_CODES.has(e.code))
|
|
19
|
+
return 'This key is used by the studio. Pick another one.';
|
|
20
|
+
if (!e.code)
|
|
21
|
+
return 'This key can\'t be told apart. Pick another one.';
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
const CODE_NAMES = {
|
|
25
|
+
Space: 'Space', Enter: 'Enter', NumpadEnter: 'Num Enter', Backquote: '`', BracketLeft: '[', BracketRight: ']',
|
|
26
|
+
Semicolon: ';', Quote: "'", Comma: ',', Period: '.', Slash: '/', Backslash: '\\', IntlBackslash: '\\',
|
|
27
|
+
PageUp: 'Page Up', PageDown: 'Page Down', End: 'End', Insert: 'Insert',
|
|
28
|
+
NumpadMultiply: 'Num *', NumpadDivide: 'Num /', NumpadDecimal: 'Num .',
|
|
29
|
+
};
|
|
30
|
+
/** One key's display name: KeyA → A, Digit1 → 1, Numpad1 → Num 1, F5 → F5. */
|
|
31
|
+
export function keyName(code) {
|
|
32
|
+
if (/^Key[A-Z]$/.test(code))
|
|
33
|
+
return code.slice(3);
|
|
34
|
+
if (/^Digit\d$/.test(code))
|
|
35
|
+
return code.slice(5);
|
|
36
|
+
if (/^Numpad\d$/.test(code))
|
|
37
|
+
return `Num ${code.slice(6)}`;
|
|
38
|
+
return CODE_NAMES[code] ?? code;
|
|
39
|
+
}
|
|
40
|
+
/** A combo as keycaps: `Shift+Digit1` → ['Shift', '1']. */
|
|
41
|
+
export function comboKeys(combo) {
|
|
42
|
+
const parts = combo.split('+');
|
|
43
|
+
const code = parts.pop() ?? '';
|
|
44
|
+
return [...parts, keyName(code)];
|
|
45
|
+
}
|
|
46
|
+
/** The key part of a combo (its KeyboardEvent.code). */
|
|
47
|
+
export const comboCode = (combo) => combo.split('+').pop() ?? '';
|
|
48
|
+
/** A command for a held key: on (down) or off (up); null = nothing to do that way. */
|
|
49
|
+
function heldCommand(c, on) {
|
|
50
|
+
switch (c.kind) {
|
|
51
|
+
case 'animationClip':
|
|
52
|
+
case 'animationSequence':
|
|
53
|
+
return { ...c, command: on ? 'play' : 'stop' };
|
|
54
|
+
case 'gallery':
|
|
55
|
+
return on ? c : null; // an event: fires on the press only
|
|
56
|
+
default:
|
|
57
|
+
return { ...c, command: on ? 'apply' : 'revert' };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const run = (b, c) => runCommand(c, { source: { host: 'key', id: b.id }, chain: [] });
|
|
61
|
+
/** Key down. Returns what to undo on key up for a hold binding (null for a press). */
|
|
62
|
+
export function bindingDown(b, ctx) {
|
|
63
|
+
const hold = b.trigger === 'hold';
|
|
64
|
+
const held = { binding: b, prevView: null, prevMode: null };
|
|
65
|
+
if (b.controlMode) {
|
|
66
|
+
held.prevMode = ctx.getControlMode();
|
|
67
|
+
ctx.setControlMode(b.controlMode);
|
|
68
|
+
}
|
|
69
|
+
if (b.view) {
|
|
70
|
+
held.prevView = hold ? captureView() : null;
|
|
71
|
+
glideToView(b.view);
|
|
72
|
+
}
|
|
73
|
+
for (const c of b.commands ?? []) {
|
|
74
|
+
const cmd = hold ? heldCommand(c, true) : c;
|
|
75
|
+
if (cmd)
|
|
76
|
+
run(b, cmd);
|
|
77
|
+
}
|
|
78
|
+
return hold ? held : null;
|
|
79
|
+
}
|
|
80
|
+
/** Key up of a hold binding: switch off what it switched on, put view / mode back. */
|
|
81
|
+
export function bindingUp(h, ctx) {
|
|
82
|
+
const b = h.binding;
|
|
83
|
+
for (const c of [...(b.commands ?? [])].reverse()) {
|
|
84
|
+
const cmd = heldCommand(c, false);
|
|
85
|
+
if (cmd)
|
|
86
|
+
run(b, cmd);
|
|
87
|
+
}
|
|
88
|
+
if (h.prevView)
|
|
89
|
+
glideToView(h.prevView);
|
|
90
|
+
if (h.prevMode)
|
|
91
|
+
ctx.setControlMode(h.prevMode);
|
|
92
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type ClipLookup } from '../animationsV2/sequenceUtils';
|
|
2
|
+
import type { AnimationClip, AnimationSequence } from '../animationsV2/types';
|
|
3
|
+
/** Stored ids: `seq:<id>` for an animation (sequence), `clip:<id>` for a clip. */
|
|
4
|
+
export type KeyPadAnimationRef = string;
|
|
5
|
+
export declare const seqRef: (id: string) => KeyPadAnimationRef;
|
|
6
|
+
export declare const clipRef: (id: string) => KeyPadAnimationRef;
|
|
7
|
+
export declare const getBoost: () => number;
|
|
8
|
+
export declare function subscribeBoost(fn: () => void): () => void;
|
|
9
|
+
/** One frame: dir +1 accelerate, −1 slow down, 0 ease back to 1×. */
|
|
10
|
+
export declare function stepBoost(dir: number, dt: number): void;
|
|
11
|
+
/** Nothing held any more: ease the boost back to 1× on its own frames. */
|
|
12
|
+
export declare function settleBoost(): void;
|
|
13
|
+
export declare const formatSpeed: (s: number) => string;
|
|
14
|
+
export interface AnimationTargets {
|
|
15
|
+
clips: AnimationClip[];
|
|
16
|
+
seqs: {
|
|
17
|
+
seq: AnimationSequence;
|
|
18
|
+
duration: number;
|
|
19
|
+
}[];
|
|
20
|
+
lookup: ClipLookup;
|
|
21
|
+
}
|
|
22
|
+
/** The chosen refs → live clips / sequences (missing ones dropped). */
|
|
23
|
+
export declare function resolveTargets(refs: readonly KeyPadAnimationRef[], clips: readonly AnimationClip[], sequences: readonly AnimationSequence[]): AnimationTargets;
|
|
24
|
+
/** One frame: run every target `dir` (±1) × `speed` × dt seconds along its timeline. */
|
|
25
|
+
export declare function stepAnimations(t: AnimationTargets, dir: number, dt: number, speed: number): void;
|
|
26
|
+
/** Stop everything the pad posed that is not in `keep` (authored values back). */
|
|
27
|
+
export declare function releaseAnimations(keep?: ReadonlySet<KeyPadAnimationRef>): void;
|
|
28
|
+
//# sourceMappingURL=keyPadAnimation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyPadAnimation.d.ts","sourceRoot":"","sources":["../../src/keyPad/keyPadAnimation.ts"],"names":[],"mappings":"AAgBA,OAAO,EAA4B,KAAK,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC1F,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE9E,kFAAkF;AAClF,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC;AACxC,eAAO,MAAM,MAAM,GAAI,IAAI,MAAM,KAAG,kBAAiC,CAAC;AACtE,eAAO,MAAM,OAAO,GAAI,IAAI,MAAM,KAAG,kBAAkC,CAAC;AAmBxE,eAAO,MAAM,QAAQ,cAAc,CAAC;AACpC,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAGzD;AAQD,qEAAqE;AACrE,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAShD;AAED,0EAA0E;AAC1E,wBAAgB,WAAW,SAW1B;AAED,eAAO,MAAM,WAAW,GAAI,GAAG,MAAM,WAA8C,CAAC;AAEpF,MAAM,WAAW,gBAAgB;IAC7B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,IAAI,EAAE;QAAE,GAAG,EAAE,iBAAiB,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACrD,MAAM,EAAE,UAAU,CAAC;CACtB;AAED,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,kBAAkB,EAAE,EAAE,KAAK,EAAE,SAAS,aAAa,EAAE,EAAE,SAAS,EAAE,SAAS,iBAAiB,EAAE,GAAG,gBAAgB,CAiB9J;AAOD,wFAAwF;AACxF,wBAAgB,cAAc,CAAC,CAAC,EAAE,gBAAgB,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAYzF;AAED,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,IAAI,GAAE,WAAW,CAAC,kBAAkB,CAAa,QAQlF"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// keyPadAnimation — animation control mode
|
|
3
|
+
// =============================================================================
|
|
4
|
+
//
|
|
5
|
+
// The chosen animations (sequences) and clips are driven by hand:
|
|
6
|
+
// ← / → held → their timelines run back / forward at the current speed
|
|
7
|
+
// (each is posed at its new time every frame — ClipsRuntime.pose /
|
|
8
|
+
// poseSequence — so letting go holds the frame)
|
|
9
|
+
// ↑ / ↓ held → accelerate / slow down; back to 1× when let go (see Boost)
|
|
10
|
+
// the general speed (keyPadSettings.speed) scales it all
|
|
11
|
+
//
|
|
12
|
+
// Each target is clamped to its own [0, duration]. Targets the pad posed are
|
|
13
|
+
// stopped (authored values back) when they stop being targets — mode changed or
|
|
14
|
+
// unselected — so nothing stays frozen mid-animation.
|
|
15
|
+
//
|
|
16
|
+
import ClipsRuntime from '../animationsV2/ClipsRuntime.js';
|
|
17
|
+
import { clipMap, resolveSequence } from '../animationsV2/sequenceUtils.js';
|
|
18
|
+
export const seqRef = (id) => `seq:${id}`;
|
|
19
|
+
export const clipRef = (id) => `clip:${id}`;
|
|
20
|
+
// --- Boost: ↑ / ↓ accelerate / slow down while held, ease back to 1× on release ----
|
|
21
|
+
//
|
|
22
|
+
// Per-frame, so it lives here (with a subscribe for the label), not in Redux: a
|
|
23
|
+
// sceneSlice write every frame would re-render everything that reads state.scene.
|
|
24
|
+
// The rate the timelines run at = general speed (keyPadSettings.speed) × boost.
|
|
25
|
+
const BOOST_MAX = 4;
|
|
26
|
+
const BOOST_MIN = 0.1;
|
|
27
|
+
/** Held ↑ reaches 4× in ~1.5 s (and ↓ 0.1× in ~2.5 s). */
|
|
28
|
+
const BOOST_RATE = Math.log(BOOST_MAX) / 1.5;
|
|
29
|
+
/** Release: back to 1× with this time constant (s). */
|
|
30
|
+
const BOOST_RETURN = 0.25;
|
|
31
|
+
let boost = 1;
|
|
32
|
+
let settleRaf = 0;
|
|
33
|
+
const boostListeners = new Set();
|
|
34
|
+
export const getBoost = () => boost;
|
|
35
|
+
export function subscribeBoost(fn) {
|
|
36
|
+
boostListeners.add(fn);
|
|
37
|
+
return () => { boostListeners.delete(fn); };
|
|
38
|
+
}
|
|
39
|
+
function setBoost(v) {
|
|
40
|
+
if (v === boost)
|
|
41
|
+
return;
|
|
42
|
+
boost = v;
|
|
43
|
+
boostListeners.forEach(fn => fn());
|
|
44
|
+
}
|
|
45
|
+
/** One frame: dir +1 accelerate, −1 slow down, 0 ease back to 1×. */
|
|
46
|
+
export function stepBoost(dir, dt) {
|
|
47
|
+
cancelAnimationFrame(settleRaf);
|
|
48
|
+
settleRaf = 0;
|
|
49
|
+
if (dir > 0)
|
|
50
|
+
setBoost(Math.min(BOOST_MAX, boost * Math.exp(BOOST_RATE * dt)));
|
|
51
|
+
else if (dir < 0)
|
|
52
|
+
setBoost(Math.max(BOOST_MIN, boost * Math.exp(-BOOST_RATE * dt)));
|
|
53
|
+
else {
|
|
54
|
+
const next = 1 + (boost - 1) * Math.exp(-dt / BOOST_RETURN);
|
|
55
|
+
setBoost(Math.abs(next - 1) < 0.005 ? 1 : next);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** Nothing held any more: ease the boost back to 1× on its own frames. */
|
|
59
|
+
export function settleBoost() {
|
|
60
|
+
if (boost === 1 || settleRaf)
|
|
61
|
+
return;
|
|
62
|
+
let last = performance.now();
|
|
63
|
+
const tick = (now) => {
|
|
64
|
+
const dt = Math.min((now - last) / 1000, 0.05);
|
|
65
|
+
last = now;
|
|
66
|
+
const next = 1 + (boost - 1) * Math.exp(-dt / BOOST_RETURN);
|
|
67
|
+
setBoost(Math.abs(next - 1) < 0.005 ? 1 : next);
|
|
68
|
+
settleRaf = boost === 1 ? 0 : requestAnimationFrame(tick);
|
|
69
|
+
};
|
|
70
|
+
settleRaf = requestAnimationFrame(tick);
|
|
71
|
+
}
|
|
72
|
+
export const formatSpeed = (s) => `${s < 1 ? s.toFixed(2) : s.toFixed(1)}×`;
|
|
73
|
+
/** The chosen refs → live clips / sequences (missing ones dropped). */
|
|
74
|
+
export function resolveTargets(refs, clips, sequences) {
|
|
75
|
+
const lookup = clipMap(clips);
|
|
76
|
+
const out = { clips: [], seqs: [], lookup };
|
|
77
|
+
for (const ref of refs) {
|
|
78
|
+
const [kind, id] = [ref.slice(0, ref.indexOf(':')), ref.slice(ref.indexOf(':') + 1)];
|
|
79
|
+
if (kind === 'clip') {
|
|
80
|
+
const c = lookup.get(id);
|
|
81
|
+
if (c)
|
|
82
|
+
out.clips.push(c);
|
|
83
|
+
}
|
|
84
|
+
else if (kind === 'seq') {
|
|
85
|
+
const seq = sequences.find(s => s.id === id);
|
|
86
|
+
if (!seq)
|
|
87
|
+
continue;
|
|
88
|
+
let duration = 0;
|
|
89
|
+
try {
|
|
90
|
+
duration = resolveSequence(seq, lookup).duration;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
continue;
|
|
94
|
+
} // missing clips
|
|
95
|
+
out.seqs.push({ seq, duration });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
100
|
+
const clamp = (v, max) => Math.max(0, Math.min(max, v));
|
|
101
|
+
/** Refs the pad has posed and not yet let go. */
|
|
102
|
+
const touched = new Set();
|
|
103
|
+
/** One frame: run every target `dir` (±1) × `speed` × dt seconds along its timeline. */
|
|
104
|
+
export function stepAnimations(t, dir, dt, speed) {
|
|
105
|
+
const d = dir * speed * dt;
|
|
106
|
+
for (const clip of t.clips) {
|
|
107
|
+
if (ClipsRuntime.isPlaying(clip.id))
|
|
108
|
+
ClipsRuntime.pause(clip.id);
|
|
109
|
+
ClipsRuntime.pose(clip, clamp(ClipsRuntime.getTime(clip.id) + d, clip.duration));
|
|
110
|
+
touched.add(clipRef(clip.id));
|
|
111
|
+
}
|
|
112
|
+
for (const { seq, duration } of t.seqs) {
|
|
113
|
+
if (ClipsRuntime.isSequencePlaying(seq.id))
|
|
114
|
+
ClipsRuntime.pauseSequence(seq.id);
|
|
115
|
+
ClipsRuntime.poseSequence(seq, t.lookup, clamp(ClipsRuntime.getSequenceTime(seq.id) + d, duration));
|
|
116
|
+
touched.add(seqRef(seq.id));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/** Stop everything the pad posed that is not in `keep` (authored values back). */
|
|
120
|
+
export function releaseAnimations(keep = new Set()) {
|
|
121
|
+
for (const ref of [...touched]) {
|
|
122
|
+
if (keep.has(ref))
|
|
123
|
+
continue;
|
|
124
|
+
const id = ref.slice(ref.indexOf(':') + 1);
|
|
125
|
+
if (ref.startsWith('clip:'))
|
|
126
|
+
ClipsRuntime.stop(id);
|
|
127
|
+
else
|
|
128
|
+
ClipsRuntime.stopSequence(id);
|
|
129
|
+
touched.delete(ref);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { Vector3Array } from '@3driseai/3drise-core';
|
|
2
|
+
export type KeyPadKey = 'up' | 'down' | 'left' | 'right' | 'zoomIn' | 'zoomOut' | 'pan' | 'alt' | 'home';
|
|
3
|
+
/** Modifier keys: held on the keyboard, or latched by a KeyPad click. */
|
|
4
|
+
export type KeyPadModifier = 'pan' | 'alt';
|
|
5
|
+
/**
|
|
6
|
+
* What the keyboard (and the KeyPad's arrows) drive:
|
|
7
|
+
* none no keyboard control (the KeyPad's buttons still orbit)
|
|
8
|
+
* orbit orbit first, Alt = fly
|
|
9
|
+
* fly fly first, Alt = orbit
|
|
10
|
+
* animation ← / → play the chosen animations / clips back / forward, ↑ / ↓ speed
|
|
11
|
+
*/
|
|
12
|
+
export type KeyPadControlMode = 'none' | 'orbit' | 'fly' | 'animation';
|
|
13
|
+
export declare const CONTROL_MODES: {
|
|
14
|
+
value: KeyPadControlMode;
|
|
15
|
+
label: string;
|
|
16
|
+
}[];
|
|
17
|
+
/** Fly style right now: the mode's default, flipped while Alt is held / latched. */
|
|
18
|
+
export declare const isFlying: (mode: KeyPadControlMode, held: ReadonlySet<KeyPadKey>) => boolean;
|
|
19
|
+
/** Keys that move the camera while held (home is one-shot, pan is a modifier). */
|
|
20
|
+
export declare const HELD_MOVE_KEYS: KeyPadKey[];
|
|
21
|
+
/** Real keyboard → KeyPad key. Matched on KeyboardEvent.key. */
|
|
22
|
+
export declare const KEYBOARD_MAP: Record<string, KeyPadKey>;
|
|
23
|
+
/** Shown on the KeyPad buttons' tooltips. */
|
|
24
|
+
export declare const KEY_HINTS: Record<KeyPadKey, string>;
|
|
25
|
+
/** Orbit around the orbit point by (theta, phi) radians; the controls' limits clamp in update(). */
|
|
26
|
+
export declare function orbitBy(dTheta: number, dPhi: number): void;
|
|
27
|
+
/** Slide camera + orbit point together in the view plane (x right, y up), in metres. */
|
|
28
|
+
export declare function panBy(dx: number, dy: number): void;
|
|
29
|
+
/** Walk camera + orbit point along the view direction flattened onto the ground
|
|
30
|
+
* (height kept). Looking straight down, "forward" is the top of the screen. */
|
|
31
|
+
export declare function moveBy(d: number): void;
|
|
32
|
+
export interface ZoomRange {
|
|
33
|
+
near: number;
|
|
34
|
+
far: number;
|
|
35
|
+
distance: number;
|
|
36
|
+
enabled: boolean;
|
|
37
|
+
}
|
|
38
|
+
export declare function zoomRange(): ZoomRange | null;
|
|
39
|
+
export declare function distanceToT(d: number, r: ZoomRange): number;
|
|
40
|
+
export declare function tToDistance(t: number, r: ZoomRange): number;
|
|
41
|
+
/** Put the camera at `d` from the orbit point, same direction. Limits clamp in update(). */
|
|
42
|
+
export declare function setOrbitDistance(d: number): void;
|
|
43
|
+
/** Turn in place: swing the orbit point around the camera about world up (+ = right). */
|
|
44
|
+
export declare function turnBy(a: number): void;
|
|
45
|
+
/** Scale the distance to the orbit point (<1 = closer). Limits clamp in update(). */
|
|
46
|
+
export declare function dollyBy(scale: number): void;
|
|
47
|
+
/**
|
|
48
|
+
* One frame of camera motion.
|
|
49
|
+
* @param held keys down (modifiers included, latched or held)
|
|
50
|
+
* @param zoom zoom rate -1 … 1 (±1 from the +/− keys)
|
|
51
|
+
* @param fly fly style (else orbit) — see isFlying
|
|
52
|
+
* @param arrows false: the arrows drive something else (animation mode); zoom only
|
|
53
|
+
*/
|
|
54
|
+
export declare function stepKeyPad(held: ReadonlySet<KeyPadKey>, zoom: number, dt: number, speed: number, fly: boolean, arrows?: boolean): void;
|
|
55
|
+
/** Where the camera is and what it looks at (the orbit point). */
|
|
56
|
+
export interface CameraView {
|
|
57
|
+
position: Vector3Array;
|
|
58
|
+
target: Vector3Array;
|
|
59
|
+
}
|
|
60
|
+
/** The live camera as a view, or null before the camera is up. */
|
|
61
|
+
export declare function captureView(): CameraView | null;
|
|
62
|
+
/** Glide camera and orbit point together to a view (a new glide replaces one in flight). */
|
|
63
|
+
export declare function glideToView(view: CameraView, duration?: number): void;
|
|
64
|
+
/** Main view: glide the camera to the project's saved camera position. */
|
|
65
|
+
export declare function goToMainView(position: Vector3Array): Promise<void>;
|
|
66
|
+
/** Is this keyboard event aimed at a text field / editor, or already handled? */
|
|
67
|
+
export declare function isTypingTarget(e: KeyboardEvent): boolean;
|
|
68
|
+
//# sourceMappingURL=keyPadMotion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyPadMotion.d.ts","sourceRoot":"","sources":["../../src/keyPad/keyPadMotion.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAG1D,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;AAEzG,yEAAyE;AACzE,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,KAAK,CAAC;AAE3C;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,WAAW,CAAC;AAEvE,eAAO,MAAM,aAAa,EAAE;IAAE,KAAK,EAAE,iBAAiB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAKtE,CAAC;AAEF,oFAAoF;AACpF,eAAO,MAAM,QAAQ,GAAI,MAAM,iBAAiB,EAAE,MAAM,WAAW,CAAC,SAAS,CAAC,YACtC,CAAC;AAEzC,kFAAkF;AAClF,eAAO,MAAM,cAAc,EAAE,SAAS,EAAyD,CAAC;AAEhG,gEAAgE;AAChE,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAYlD,CAAC;AAEF,6CAA6C;AAC7C,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAU/C,CAAC;AAoBF,oGAAoG;AACpG,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAUnD;AAED,wFAAwF;AACxF,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAU3C;AAED;gFACgF;AAChF,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,QAe/B;AAaD,MAAM,WAAW,SAAS;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE;AAE5F,wBAAgB,SAAS,IAAI,SAAS,GAAG,IAAI,CAS5C;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,GAAG,MAAM,CAG3D;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,GAAG,MAAM,CAE3D;AAED,4FAA4F;AAC5F,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,QAQzC;AAED,yFAAyF;AACzF,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,QAO/B;AAED,qFAAqF;AACrF,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,QAOpC;AAQD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,WAAW,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,UAAO,QA2B5H;AAID,kEAAkE;AAClE,MAAM,WAAW,UAAU;IACvB,QAAQ,EAAE,YAAY,CAAC;IACvB,MAAM,EAAE,YAAY,CAAC;CACxB;AAED,kEAAkE;AAClE,wBAAgB,WAAW,IAAI,UAAU,GAAG,IAAI,CAS/C;AASD,4FAA4F;AAC5F,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,SAAM,QAuB3D;AAED,0EAA0E;AAC1E,wBAAgB,YAAY,CAAC,QAAQ,EAAE,YAAY,iBAElD;AAED,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,CAAC,EAAE,aAAa,GAAG,OAAO,CAMxD"}
|