@satinhq/kit 0.0.0-stage → 0.1.0-beta.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/LICENSE +21 -0
- package/NOTICE +96 -0
- package/README.md +454 -2
- package/THIRD_PARTY_NOTICES.md +65 -0
- package/bin/satin.mjs +13 -0
- package/dist/acknowledgements/Acknowledgements.d.ts +4 -0
- package/dist/acknowledgements/Acknowledgements.js +42 -0
- package/dist/acknowledgements/bundledNotices.d.ts +4 -0
- package/dist/acknowledgements/bundledNotices.js +58 -0
- package/dist/acknowledgements/inventory.d.ts +17 -0
- package/dist/acknowledgements/inventory.js +10 -0
- package/dist/acknowledgements/inventory.json +39 -0
- package/dist/actions/ActionMenu.d.ts +30 -0
- package/dist/actions/ActionMenu.js +103 -0
- package/dist/actions/RowActions.d.ts +42 -0
- package/dist/actions/RowActions.js +820 -0
- package/dist/actions/contextMotion.d.ts +78 -0
- package/dist/actions/contextMotion.js +329 -0
- package/dist/actions/motion.d.ts +19 -0
- package/dist/actions/motion.js +74 -0
- package/dist/actions/nativeContextAppearance.json +86 -0
- package/dist/actions/nativeContextMotion.json +734 -0
- package/dist/actions/nativeMotion.json +388 -0
- package/dist/actions/preview.d.ts +27 -0
- package/dist/actions/preview.js +217 -0
- package/dist/actions/recognizers.d.ts +24 -0
- package/dist/actions/recognizers.js +87 -0
- package/dist/actions/rowScrub.d.ts +59 -0
- package/dist/actions/rowScrub.js +400 -0
- package/dist/actions/swipeSpring.d.ts +14 -0
- package/dist/actions/swipeSpring.js +35 -0
- package/dist/actions/types.d.ts +71 -0
- package/dist/actions/types.js +28 -0
- package/dist/app/InstallComponents.d.ts +16 -0
- package/dist/app/InstallComponents.js +104 -0
- package/dist/app/InstallationDiagnostics.d.ts +7 -0
- package/dist/app/InstallationDiagnostics.js +23 -0
- package/dist/app/OrientationGuard.d.ts +8 -0
- package/dist/app/OrientationGuard.js +9 -0
- package/dist/app/appearance.d.ts +18 -0
- package/dist/app/appearance.js +15 -0
- package/dist/app/appearanceStore.d.ts +30 -0
- package/dist/app/appearanceStore.js +228 -0
- package/dist/app/backgroundUpdate.d.ts +20 -0
- package/dist/app/backgroundUpdate.js +151 -0
- package/dist/app/buildInfo.d.ts +11 -0
- package/dist/app/buildInfo.js +12 -0
- package/dist/app/buildInfoBuild.d.ts +3 -0
- package/dist/app/buildInfoBuild.js +12 -0
- package/dist/app/checkCli.d.ts +1 -0
- package/dist/app/checkCli.js +73 -0
- package/dist/app/coldLaunchUpdate.d.ts +14 -0
- package/dist/app/coldLaunchUpdate.js +384 -0
- package/dist/app/definition.d.ts +85 -0
- package/dist/app/definition.js +347 -0
- package/dist/app/deployment.d.ts +28 -0
- package/dist/app/deployment.js +352 -0
- package/dist/app/deploymentBuild.d.ts +3 -0
- package/dist/app/deploymentBuild.js +55 -0
- package/dist/app/earlyWorker.d.ts +3 -0
- package/dist/app/earlyWorker.js +77 -0
- package/dist/app/head.d.ts +10 -0
- package/dist/app/head.js +120 -0
- package/dist/app/icons.d.ts +4 -0
- package/dist/app/icons.js +35 -0
- package/dist/app/install.d.ts +18 -0
- package/dist/app/install.js +82 -0
- package/dist/app/installCheck.d.ts +2 -0
- package/dist/app/installCheck.js +148 -0
- package/dist/app/installContext.d.ts +35 -0
- package/dist/app/installContext.js +45 -0
- package/dist/app/installCustom.d.ts +25 -0
- package/dist/app/installCustom.js +55 -0
- package/dist/app/installExperience.d.ts +22 -0
- package/dist/app/installExperience.js +389 -0
- package/dist/app/installExperienceDevice.d.ts +29 -0
- package/dist/app/installExperienceDevice.js +160 -0
- package/dist/app/installExperienceHome.d.ts +16 -0
- package/dist/app/installExperienceHome.js +304 -0
- package/dist/app/installExperienceRuntime.d.ts +37 -0
- package/dist/app/installExperienceRuntime.js +285 -0
- package/dist/app/installPolicy.d.ts +28 -0
- package/dist/app/installPolicy.js +90 -0
- package/dist/app/installQr.d.ts +10 -0
- package/dist/app/installQr.js +139 -0
- package/dist/app/installStrings.d.ts +52 -0
- package/dist/app/installStrings.js +75 -0
- package/dist/app/installTipPath.d.ts +12 -0
- package/dist/app/installTipPath.js +9 -0
- package/dist/app/installationFailure.d.ts +14 -0
- package/dist/app/installationFailure.js +38 -0
- package/dist/app/launch.d.ts +7 -0
- package/dist/app/launch.js +241 -0
- package/dist/app/launchReadiness.d.ts +9 -0
- package/dist/app/launchReadiness.js +72 -0
- package/dist/app/launchScreen.d.ts +16 -0
- package/dist/app/launchScreen.js +14 -0
- package/dist/app/launchScreenRuntime.d.ts +19 -0
- package/dist/app/launchScreenRuntime.js +313 -0
- package/dist/app/launchWatchdog.d.ts +1 -0
- package/dist/app/launchWatchdog.js +202 -0
- package/dist/app/manifests.d.ts +19 -0
- package/dist/app/manifests.js +20 -0
- package/dist/app/mdm.d.ts +3 -0
- package/dist/app/mdm.js +20 -0
- package/dist/app/mediaCheck.d.ts +9 -0
- package/dist/app/mediaCheck.js +122 -0
- package/dist/app/mediaTypes.d.ts +2 -0
- package/dist/app/mediaTypes.js +13 -0
- package/dist/app/platformPolicy.d.ts +3 -0
- package/dist/app/platformPolicy.js +10 -0
- package/dist/app/platformScripts.d.ts +6 -0
- package/dist/app/platformScripts.js +65 -0
- package/dist/app/releaseUpdates.d.ts +7 -0
- package/dist/app/releaseUpdates.js +53 -0
- package/dist/app/runtime.d.ts +9 -0
- package/dist/app/runtime.js +38 -0
- package/dist/app/startup.d.ts +30 -0
- package/dist/app/startup.js +24 -0
- package/dist/app/statusBand.d.ts +11 -0
- package/dist/app/statusBand.js +257 -0
- package/dist/app/viewport.d.ts +3 -0
- package/dist/app/viewport.js +42 -0
- package/dist/app/viewportValue.d.ts +5 -0
- package/dist/app/viewportValue.js +14 -0
- package/dist/app/vite.d.ts +27 -0
- package/dist/app/vite.js +440 -0
- package/dist/app/warmImage.d.ts +3 -0
- package/dist/app/warmImage.js +59 -0
- package/dist/content/content.d.ts +153 -0
- package/dist/content/content.js +315 -0
- package/dist/content/layout.d.ts +26 -0
- package/dist/content/layout.js +114 -0
- package/dist/content/scaledMetric.d.ts +3 -0
- package/dist/content/scaledMetric.js +68 -0
- package/dist/content/typography.d.ts +83 -0
- package/dist/content/typography.js +32 -0
- package/dist/controls/ActivityIndicatorSegments.d.ts +4 -0
- package/dist/controls/ActivityIndicatorSegments.js +5 -0
- package/dist/controls/ColorPicker.d.ts +20 -0
- package/dist/controls/ColorPicker.js +70 -0
- package/dist/controls/ControlRow.d.ts +5 -0
- package/dist/controls/ControlRow.js +10 -0
- package/dist/controls/DatePicker.d.ts +41 -0
- package/dist/controls/DatePicker.js +148 -0
- package/dist/controls/ProgressView.d.ts +17 -0
- package/dist/controls/ProgressView.js +24 -0
- package/dist/controls/civilDate.d.ts +49 -0
- package/dist/controls/civilDate.js +112 -0
- package/dist/data/database.d.ts +154 -0
- package/dist/data/database.js +710 -0
- package/dist/data/value-equal.d.ts +2 -0
- package/dist/data/value-equal.js +36 -0
- package/dist/data/write-store.d.ts +38 -0
- package/dist/data/write-store.js +192 -0
- package/dist/dev-menu/DevMenu.d.ts +1 -0
- package/dist/dev-menu/DevMenu.js +121 -0
- package/dist/dev-menu/actions.d.ts +12 -0
- package/dist/dev-menu/actions.js +99 -0
- package/dist/dev-menu/build.d.ts +5 -0
- package/dist/dev-menu/build.js +40 -0
- package/dist/dev-menu/gesture.d.ts +18 -0
- package/dist/dev-menu/gesture.js +109 -0
- package/dist/dev-menu/shake.d.ts +2 -0
- package/dist/dev-menu/shake.js +36 -0
- package/dist/dev-menu/types.d.ts +8 -0
- package/dist/dev-menu/types.js +1 -0
- package/dist/diagnostics.d.ts +3 -0
- package/dist/diagnostics.js +4 -0
- package/dist/docs/app-recipes.md +411 -0
- package/dist/docs/components/file-importer.md +54 -0
- package/dist/docs/components/paste-button.md +48 -0
- package/dist/docs/components/photos-picker.md +53 -0
- package/dist/docs/components/share-link.md +58 -0
- package/dist/docs/content.md +159 -0
- package/dist/docs/controls.md +151 -0
- package/dist/docs/data.md +332 -0
- package/dist/docs/limitations.md +40 -0
- package/dist/docs/messaging-scroll.md +234 -0
- package/dist/docs/networking.md +190 -0
- package/dist/docs/push.md +215 -0
- package/dist/docs/swiftui-reference.md +1895 -0
- package/dist/docs/symbols.md +293 -0
- package/dist/docs/tokens.md +60 -0
- package/dist/fidelity/devAssertions.d.ts +21 -0
- package/dist/fidelity/devAssertions.js +199 -0
- package/dist/focus/focusIntentMachine.d.ts +106 -0
- package/dist/focus/focusIntentMachine.js +359 -0
- package/dist/focus/navigationFocus.d.ts +42 -0
- package/dist/focus/navigationFocus.js +241 -0
- package/dist/focus/programmaticFocus.d.ts +15 -0
- package/dist/focus/programmaticFocus.js +152 -0
- package/dist/gestures/GestureView.d.ts +12 -0
- package/dist/gestures/GestureView.js +321 -0
- package/dist/gestures/arena.d.ts +138 -0
- package/dist/gestures/arena.js +417 -0
- package/dist/gestures/existingCompetitors.d.ts +79 -0
- package/dist/gestures/existingCompetitors.js +82 -0
- package/dist/gestures/gesture.d.ts +62 -0
- package/dist/gestures/gesture.js +107 -0
- package/dist/glass/ControlLens.d.ts +46 -0
- package/dist/glass/ControlLens.js +88 -0
- package/dist/glass/GlassBar.d.ts +9 -0
- package/dist/glass/GlassBar.js +22 -0
- package/dist/glass/GlassConfusionReview.d.ts +3 -0
- package/dist/glass/GlassConfusionReview.js +185 -0
- package/dist/glass/GlassEffect.d.ts +69 -0
- package/dist/glass/GlassEffect.js +550 -0
- package/dist/glass/GlassMaterialRoot.d.ts +7 -0
- package/dist/glass/GlassMaterialRoot.js +39 -0
- package/dist/glass/GlassPresentation.d.ts +29 -0
- package/dist/glass/GlassPresentation.js +1041 -0
- package/dist/glass/GlassProbeIcons.d.ts +8 -0
- package/dist/glass/GlassProbeIcons.js +12 -0
- package/dist/glass/GlassProbeOverlay.d.ts +52 -0
- package/dist/glass/GlassProbeOverlay.js +45 -0
- package/dist/glass/GlassTextField.d.ts +22 -0
- package/dist/glass/GlassTextField.js +12 -0
- package/dist/glass/MaterialEffectRoot.d.ts +9 -0
- package/dist/glass/MaterialEffectRoot.js +189 -0
- package/dist/glass/Menu.d.ts +3 -0
- package/dist/glass/Menu.js +35 -0
- package/dist/glass/MenuStack.d.ts +23 -0
- package/dist/glass/MenuStack.js +371 -0
- package/dist/glass/MenuSymbol.d.ts +7 -0
- package/dist/glass/MenuSymbol.js +16 -0
- package/dist/glass/MorphEllipsisArtwork.d.ts +2 -0
- package/dist/glass/MorphEllipsisArtwork.js +6 -0
- package/dist/glass/TabBar.d.ts +29 -0
- package/dist/glass/TabBar.js +176 -0
- package/dist/glass/capability.d.ts +25 -0
- package/dist/glass/capability.js +63 -0
- package/dist/glass/captureWindow.d.ts +60 -0
- package/dist/glass/captureWindow.js +119 -0
- package/dist/glass/confusionSession.d.ts +41 -0
- package/dist/glass/confusionSession.js +44 -0
- package/dist/glass/contactPhysics.d.ts +130 -0
- package/dist/glass/contactPhysics.js +214 -0
- package/dist/glass/context.d.ts +37 -0
- package/dist/glass/context.js +11 -0
- package/dist/glass/errorAlert.d.ts +6 -0
- package/dist/glass/errorAlert.js +57 -0
- package/dist/glass/errors.d.ts +22 -0
- package/dist/glass/errors.js +78 -0
- package/dist/glass/failurePolicy.d.ts +4 -0
- package/dist/glass/failurePolicy.js +20 -0
- package/dist/glass/fallback/material.d.ts +16 -0
- package/dist/glass/fallback/material.js +87 -0
- package/dist/glass/fallback/regions.d.ts +16 -0
- package/dist/glass/fallback/regions.js +249 -0
- package/dist/glass/fallback/runtime.d.ts +72 -0
- package/dist/glass/fallback/runtime.js +278 -0
- package/dist/glass/fallback/selection.d.ts +2 -0
- package/dist/glass/fallback/selection.js +9 -0
- package/dist/glass/fieldGeometry.d.ts +28 -0
- package/dist/glass/fieldGeometry.js +64 -0
- package/dist/glass/fieldMotion.d.ts +42 -0
- package/dist/glass/fieldMotion.js +47 -0
- package/dist/glass/glassContact.d.ts +76 -0
- package/dist/glass/glassContact.js +153 -0
- package/dist/glass/glassProbeSettings.d.ts +18 -0
- package/dist/glass/glassProbeSettings.js +133 -0
- package/dist/glass/lens/Scene.d.ts +108 -0
- package/dist/glass/lens/Scene.js +553 -0
- package/dist/glass/lens/backgrounds.json +103 -0
- package/dist/glass/lens/bitmapBackdrop.d.ts +14 -0
- package/dist/glass/lens/bitmapBackdrop.js +35 -0
- package/dist/glass/lens/controlLift.d.ts +94 -0
- package/dist/glass/lens/controlLift.js +78 -0
- package/dist/glass/lens/data.d.ts +19 -0
- package/dist/glass/lens/data.js +161 -0
- package/dist/glass/lens/drawableTop.d.ts +16 -0
- package/dist/glass/lens/drawableTop.js +15 -0
- package/dist/glass/lens/icons.d.ts +9 -0
- package/dist/glass/lens/icons.js +34 -0
- package/dist/glass/lens/interactiveContact.d.ts +33 -0
- package/dist/glass/lens/interactiveContact.js +158 -0
- package/dist/glass/lens/interactivePress.d.ts +95 -0
- package/dist/glass/lens/interactivePress.js +169 -0
- package/dist/glass/lens/ios27GlyphFit.json +126 -0
- package/dist/glass/lens/lensLightingSamples.json +1 -0
- package/dist/glass/lens/lensPress.d.ts +22 -0
- package/dist/glass/lens/lensPress.js +68 -0
- package/dist/glass/lens/lensPressSamples.json +1 -0
- package/dist/glass/lens/liquidMotion.d.ts +50 -0
- package/dist/glass/lens/liquidMotion.js +239 -0
- package/dist/glass/lens/materialGlassRecipes.json +776 -0
- package/dist/glass/lens/materialUniforms.d.ts +22 -0
- package/dist/glass/lens/materialUniforms.js +117 -0
- package/dist/glass/lens/menu-closing-close-fit.json +380 -0
- package/dist/glass/lens/menu-opening-native-fits.json +911 -0
- package/dist/glass/lens/menuContextFit.json +88 -0
- package/dist/glass/lens/menuContextOpacity.json +98 -0
- package/dist/glass/lens/menuContextPresentation.d.ts +15 -0
- package/dist/glass/lens/menuContextPresentation.js +65 -0
- package/dist/glass/lens/menuCornerFit.json +252 -0
- package/dist/glass/lens/menuCornerMotion.d.ts +9 -0
- package/dist/glass/lens/menuCornerMotion.js +5 -0
- package/dist/glass/lens/menuDropFit.json +64 -0
- package/dist/glass/lens/menuField.d.ts +178 -0
- package/dist/glass/lens/menuField.js +564 -0
- package/dist/glass/lens/menuGesturePresentation.d.ts +12 -0
- package/dist/glass/lens/menuGesturePresentation.js +42 -0
- package/dist/glass/lens/menuGlyphDropFit.json +38 -0
- package/dist/glass/lens/menuMorphFit.json +564 -0
- package/dist/glass/lens/menuMorphMotion.d.ts +70 -0
- package/dist/glass/lens/menuMorphMotion.js +219 -0
- package/dist/glass/lens/menuMorphPhoneFit.json +85 -0
- package/dist/glass/lens/menuMotion.d.ts +81 -0
- package/dist/glass/lens/menuMotion.js +323 -0
- package/dist/glass/lens/menuPlacement.d.ts +5 -0
- package/dist/glass/lens/menuPlacement.js +29 -0
- package/dist/glass/lens/menuSpring.d.ts +18 -0
- package/dist/glass/lens/menuSpring.js +29 -0
- package/dist/glass/lens/menuTouchLight.d.ts +13 -0
- package/dist/glass/lens/menuTouchLight.js +147 -0
- package/dist/glass/lens/mirrorConstants.d.ts +1 -0
- package/dist/glass/lens/mirrorConstants.js +2 -0
- package/dist/glass/lens/mirrorExtents.d.ts +52 -0
- package/dist/glass/lens/mirrorExtents.js +264 -0
- package/dist/glass/lens/mirrorInvalidation.d.ts +6 -0
- package/dist/glass/lens/mirrorInvalidation.js +10 -0
- package/dist/glass/lens/mirrorReuse.d.ts +20 -0
- package/dist/glass/lens/mirrorReuse.js +166 -0
- package/dist/glass/lens/motion.d.ts +62 -0
- package/dist/glass/lens/motion.js +93 -0
- package/dist/glass/lens/recordedMotion.d.ts +6 -0
- package/dist/glass/lens/recordedMotion.js +44 -0
- package/dist/glass/lens/referenceBackground.d.ts +9 -0
- package/dist/glass/lens/referenceBackground.js +151 -0
- package/dist/glass/lens/referenceProfiles.d.ts +21 -0
- package/dist/glass/lens/referenceProfiles.js +33 -0
- package/dist/glass/lens/referenceRegularTint.d.ts +12 -0
- package/dist/glass/lens/referenceRegularTint.js +41 -0
- package/dist/glass/lens/scopeSegments.d.ts +12 -0
- package/dist/glass/lens/scopeSegments.js +18 -0
- package/dist/glass/lens/scrollAnchoring.d.ts +15 -0
- package/dist/glass/lens/scrollAnchoring.js +17 -0
- package/dist/glass/lens/selectionParameters.json +94 -0
- package/dist/glass/lens/shapeParameters.json +198 -0
- package/dist/glass/lens/tab-motion-native-fits.json +778 -0
- package/dist/glass/lens/tabAccessoryMotion.d.ts +52 -0
- package/dist/glass/lens/tabAccessoryMotion.js +52 -0
- package/dist/glass/lens/tabChromaRimModel.d.ts +43 -0
- package/dist/glass/lens/tabChromaRimModel.js +93 -0
- package/dist/glass/lens/tabCollapseInkSamples.json +49 -0
- package/dist/glass/lens/tabCompactGesture.d.ts +15 -0
- package/dist/glass/lens/tabCompactGesture.js +50 -0
- package/dist/glass/lens/tabGeometryAttack.json +70 -0
- package/dist/glass/lens/tabGlyphColours.d.ts +17 -0
- package/dist/glass/lens/tabGlyphColours.js +38 -0
- package/dist/glass/lens/tabInterruptedDecaySamples.json +245 -0
- package/dist/glass/lens/tabInterruptedRestoreSamples.json +290 -0
- package/dist/glass/lens/tabMaterialModel.d.ts +10 -0
- package/dist/glass/lens/tabMaterialModel.js +108 -0
- package/dist/glass/lens/tabMinimizeInk.d.ts +15 -0
- package/dist/glass/lens/tabMinimizeInk.js +14 -0
- package/dist/glass/lens/tabMinimizeMotion.d.ts +36 -0
- package/dist/glass/lens/tabMinimizeMotion.js +107 -0
- package/dist/glass/lens/tabMinimizeSamples.json +411 -0
- package/dist/glass/lens/tabMotion.d.ts +91 -0
- package/dist/glass/lens/tabMotion.js +478 -0
- package/dist/glass/lens/tabMotionFit.d.ts +76 -0
- package/dist/glass/lens/tabMotionFit.js +61 -0
- package/dist/glass/lens/tabNativeSymbols.d.ts +1 -0
- package/dist/glass/lens/tabNativeSymbols.js +7 -0
- package/dist/glass/lens/tabPresentation.d.ts +21 -0
- package/dist/glass/lens/tabPresentation.js +20 -0
- package/dist/glass/lens/tabRelease.d.ts +5 -0
- package/dist/glass/lens/tabRelease.js +14 -0
- package/dist/glass/lens/tabRestoreMotion.d.ts +19 -0
- package/dist/glass/lens/tabRestoreMotion.js +65 -0
- package/dist/glass/lens/tabRestoreSamples.json +1272 -0
- package/dist/glass/lens/tabSearchMember.d.ts +38 -0
- package/dist/glass/lens/tabSearchMember.js +74 -0
- package/dist/glass/lens/tabSelectionMaterial.d.ts +20 -0
- package/dist/glass/lens/tabSelectionMaterial.js +23 -0
- package/dist/glass/lens/tabSelectionMeasurements.test-fixture.d.ts +9 -0
- package/dist/glass/lens/tabSelectionMeasurements.test-fixture.js +74 -0
- package/dist/glass/lens/tabSettingsSymbol.d.ts +1 -0
- package/dist/glass/lens/tabSettingsSymbol.js +2 -0
- package/dist/glass/lens/tabShapeResponse.d.ts +7 -0
- package/dist/glass/lens/tabShapeResponse.js +30 -0
- package/dist/glass/lens/theme.d.ts +18 -0
- package/dist/glass/lens/theme.js +40 -0
- package/dist/glass/lens/trace.d.ts +2 -0
- package/dist/glass/lens/trace.js +27 -0
- package/dist/glass/liquidContext.d.ts +32 -0
- package/dist/glass/liquidContext.js +2 -0
- package/dist/glass/menuPanelPlacement.d.ts +51 -0
- package/dist/glass/menuPanelPlacement.js +128 -0
- package/dist/glass/menuPresenter.d.ts +34 -0
- package/dist/glass/menuPresenter.js +983 -0
- package/dist/glass/menuSymbolMasks.d.ts +1 -0
- package/dist/glass/menuSymbolMasks.js +1 -0
- package/dist/glass/menuSymbolTokens.d.ts +22 -0
- package/dist/glass/menuSymbolTokens.js +12 -0
- package/dist/glass/menuTriggerGeometry.d.ts +99 -0
- package/dist/glass/menuTriggerGeometry.js +126 -0
- package/dist/glass/menuTriggerSnapshot.d.ts +77 -0
- package/dist/glass/menuTriggerSnapshot.js +69 -0
- package/dist/glass/menuTypes.d.ts +22 -0
- package/dist/glass/menuTypes.js +1 -0
- package/dist/glass/morphField.d.ts +18 -0
- package/dist/glass/morphField.js +139 -0
- package/dist/glass/presentationFrame.d.ts +43 -0
- package/dist/glass/presentationFrame.js +82 -0
- package/dist/glass/pressHighlight.d.ts +9 -0
- package/dist/glass/pressHighlight.js +158 -0
- package/dist/glass/regionRegistry.d.ts +93 -0
- package/dist/glass/regionRegistry.js +313 -0
- package/dist/glass/regions.d.ts +262 -0
- package/dist/glass/regions.js +147 -0
- package/dist/glass/regularMaterialContext.d.ts +45 -0
- package/dist/glass/regularMaterialContext.js +118 -0
- package/dist/glass/regularTriggerMaterial.d.ts +19 -0
- package/dist/glass/regularTriggerMaterial.js +111 -0
- package/dist/glass/regularTriggerRim.d.ts +163 -0
- package/dist/glass/regularTriggerRim.js +436 -0
- package/dist/glass/renderSpecs.d.ts +454 -0
- package/dist/glass/renderSpecs.js +231 -0
- package/dist/glass/rendererContract.d.ts +205 -0
- package/dist/glass/rendererContract.js +18 -0
- package/dist/glass/shapeTrack.d.ts +53 -0
- package/dist/glass/shapeTrack.js +74 -0
- package/dist/glass/shapeTrackRegistry.d.ts +40 -0
- package/dist/glass/shapeTrackRegistry.js +49 -0
- package/dist/glass/sourceDamage.d.ts +6 -0
- package/dist/glass/sourceDamage.js +8 -0
- package/dist/glass/submenuMotion.d.ts +205 -0
- package/dist/glass/submenuMotion.js +306 -0
- package/dist/glass/tabBarModel.d.ts +12 -0
- package/dist/glass/tabBarModel.js +32 -0
- package/dist/glass/testing.d.ts +14 -0
- package/dist/glass/testing.js +14 -0
- package/dist/glass/tokens.d.ts +79 -0
- package/dist/glass/tokens.js +86 -0
- package/dist/glass/toolbarMaterialContext.d.ts +28 -0
- package/dist/glass/toolbarMaterialContext.js +66 -0
- package/dist/glass/topLayer.d.ts +5 -0
- package/dist/glass/topLayer.js +10 -0
- package/dist/glass/unionOutline.d.ts +11 -0
- package/dist/glass/unionOutline.js +98 -0
- package/dist/glass/visibleDeadline.d.ts +21 -0
- package/dist/glass/visibleDeadline.js +61 -0
- package/dist/glass/webgpu/accessoryMorph.d.ts +99 -0
- package/dist/glass/webgpu/accessoryMorph.js +473 -0
- package/dist/glass/webgpu/accessoryTrack.d.ts +47 -0
- package/dist/glass/webgpu/accessoryTrack.js +59 -0
- package/dist/glass/webgpu/areaMips.d.ts +50 -0
- package/dist/glass/webgpu/areaMips.js +166 -0
- package/dist/glass/webgpu/backdrop.d.ts +86 -0
- package/dist/glass/webgpu/backdrop.js +243 -0
- package/dist/glass/webgpu/backdropLayers.d.ts +29 -0
- package/dist/glass/webgpu/backdropLayers.js +182 -0
- package/dist/glass/webgpu/backgroundLayers.d.ts +71 -0
- package/dist/glass/webgpu/backgroundLayers.js +385 -0
- package/dist/glass/webgpu/cadence.d.ts +18 -0
- package/dist/glass/webgpu/cadence.js +35 -0
- package/dist/glass/webgpu/canvasSubmission.d.ts +6 -0
- package/dist/glass/webgpu/canvasSubmission.js +22 -0
- package/dist/glass/webgpu/capture.d.ts +77 -0
- package/dist/glass/webgpu/capture.js +534 -0
- package/dist/glass/webgpu/captureBands.d.ts +39 -0
- package/dist/glass/webgpu/captureBands.js +53 -0
- package/dist/glass/webgpu/captureContext.d.ts +1 -0
- package/dist/glass/webgpu/captureContext.js +6 -0
- package/dist/glass/webgpu/captureDemand.d.ts +44 -0
- package/dist/glass/webgpu/captureDemand.js +81 -0
- package/dist/glass/webgpu/captureRelease.d.ts +18 -0
- package/dist/glass/webgpu/captureRelease.js +39 -0
- package/dist/glass/webgpu/client.d.ts +49 -0
- package/dist/glass/webgpu/client.js +336 -0
- package/dist/glass/webgpu/contextBackdrop.d.ts +6 -0
- package/dist/glass/webgpu/contextBackdrop.js +13 -0
- package/dist/glass/webgpu/contextComposite.d.ts +64 -0
- package/dist/glass/webgpu/contextComposite.js +66 -0
- package/dist/glass/webgpu/decodedImage.d.ts +1 -0
- package/dist/glass/webgpu/decodedImage.js +2 -0
- package/dist/glass/webgpu/deviceLimits.d.ts +5 -0
- package/dist/glass/webgpu/deviceLimits.js +8 -0
- package/dist/glass/webgpu/edgePass.d.ts +9 -0
- package/dist/glass/webgpu/edgePass.js +170 -0
- package/dist/glass/webgpu/entry.worker.d.ts +1 -0
- package/dist/glass/webgpu/entry.worker.js +28 -0
- package/dist/glass/webgpu/errorCode.d.ts +3 -0
- package/dist/glass/webgpu/errorCode.js +27 -0
- package/dist/glass/webgpu/errors.d.ts +8 -0
- package/dist/glass/webgpu/errors.js +55 -0
- package/dist/glass/webgpu/fieldInk.d.ts +9 -0
- package/dist/glass/webgpu/fieldInk.js +62 -0
- package/dist/glass/webgpu/foreground.d.ts +29 -0
- package/dist/glass/webgpu/foreground.js +313 -0
- package/dist/glass/webgpu/frameClock.d.ts +34 -0
- package/dist/glass/webgpu/frameClock.js +60 -0
- package/dist/glass/webgpu/frameGraph.d.ts +57 -0
- package/dist/glass/webgpu/frameGraph.js +287 -0
- package/dist/glass/webgpu/frameProbe.d.ts +58 -0
- package/dist/glass/webgpu/frameProbe.js +181 -0
- package/dist/glass/webgpu/interactionProfiles.d.ts +10 -0
- package/dist/glass/webgpu/interactionProfiles.js +25 -0
- package/dist/glass/webgpu/keptGlyphs.d.ts +68 -0
- package/dist/glass/webgpu/keptGlyphs.js +85 -0
- package/dist/glass/webgpu/keyboardBackdrop.d.ts +12 -0
- package/dist/glass/webgpu/keyboardBackdrop.js +51 -0
- package/dist/glass/webgpu/lagTrace.d.ts +31 -0
- package/dist/glass/webgpu/lagTrace.js +22 -0
- package/dist/glass/webgpu/layers.d.ts +100 -0
- package/dist/glass/webgpu/layers.js +301 -0
- package/dist/glass/webgpu/lensDebug.d.ts +4 -0
- package/dist/glass/webgpu/lensDebug.js +28 -0
- package/dist/glass/webgpu/lensDiffusion.d.ts +2 -0
- package/dist/glass/webgpu/lensDiffusion.js +20 -0
- package/dist/glass/webgpu/lensLightingFit.json +222 -0
- package/dist/glass/webgpu/menuLiftInput.d.ts +30 -0
- package/dist/glass/webgpu/menuLiftInput.js +39 -0
- package/dist/glass/webgpu/menuSurfaceGuard.d.ts +17 -0
- package/dist/glass/webgpu/menuSurfaceGuard.js +26 -0
- package/dist/glass/webgpu/minimize.d.ts +184 -0
- package/dist/glass/webgpu/minimize.js +470 -0
- package/dist/glass/webgpu/navigationAtlasCopy.d.ts +12 -0
- package/dist/glass/webgpu/navigationAtlasCopy.js +121 -0
- package/dist/glass/webgpu/navigationTracks.d.ts +3 -0
- package/dist/glass/webgpu/navigationTracks.js +84 -0
- package/dist/glass/webgpu/passes.d.ts +204 -0
- package/dist/glass/webgpu/passes.js +394 -0
- package/dist/glass/webgpu/pendingSurfaceInput.d.ts +29 -0
- package/dist/glass/webgpu/pendingSurfaceInput.js +51 -0
- package/dist/glass/webgpu/preparedSourcePixels.d.ts +21 -0
- package/dist/glass/webgpu/preparedSourcePixels.js +43 -0
- package/dist/glass/webgpu/presentationBackdrop.d.ts +3 -0
- package/dist/glass/webgpu/presentationBackdrop.js +33 -0
- package/dist/glass/webgpu/presentationDiffusion.d.ts +48 -0
- package/dist/glass/webgpu/presentationDiffusion.js +57 -0
- package/dist/glass/webgpu/presentationMaterial.d.ts +9 -0
- package/dist/glass/webgpu/presentationMaterial.js +156 -0
- package/dist/glass/webgpu/presenterViewport.d.ts +11 -0
- package/dist/glass/webgpu/presenterViewport.js +193 -0
- package/dist/glass/webgpu/production.d.ts +552 -0
- package/dist/glass/webgpu/production.js +1929 -0
- package/dist/glass/webgpu/productionInstalls.d.ts +53 -0
- package/dist/glass/webgpu/productionInstalls.js +225 -0
- package/dist/glass/webgpu/productionNavigation.d.ts +5 -0
- package/dist/glass/webgpu/productionNavigation.js +360 -0
- package/dist/glass/webgpu/productionPinnedTop.d.ts +80 -0
- package/dist/glass/webgpu/productionPinnedTop.js +77 -0
- package/dist/glass/webgpu/productionPinnedTopPrepare.d.ts +21 -0
- package/dist/glass/webgpu/productionPinnedTopPrepare.js +132 -0
- package/dist/glass/webgpu/productionPlanes.d.ts +135 -0
- package/dist/glass/webgpu/productionPlanes.js +412 -0
- package/dist/glass/webgpu/productionPrepare.d.ts +18 -0
- package/dist/glass/webgpu/productionPrepare.js +833 -0
- package/dist/glass/webgpu/productionTracks.d.ts +73 -0
- package/dist/glass/webgpu/productionTracks.js +22 -0
- package/dist/glass/webgpu/productionViewport.d.ts +20 -0
- package/dist/glass/webgpu/productionViewport.js +52 -0
- package/dist/glass/webgpu/profile.d.ts +37 -0
- package/dist/glass/webgpu/profile.js +101 -0
- package/dist/glass/webgpu/raster.d.ts +337 -0
- package/dist/glass/webgpu/raster.js +1360 -0
- package/dist/glass/webgpu/rasterMaterial.d.ts +19 -0
- package/dist/glass/webgpu/rasterMaterial.js +197 -0
- package/dist/glass/webgpu/rasterShaders.d.ts +7 -0
- package/dist/glass/webgpu/rasterShaders.js +105 -0
- package/dist/glass/webgpu/rasterUniforms.d.ts +304 -0
- package/dist/glass/webgpu/rasterUniforms.js +415 -0
- package/dist/glass/webgpu/regionBackdrop.d.ts +8 -0
- package/dist/glass/webgpu/regionBackdrop.js +12 -0
- package/dist/glass/webgpu/regionBridge.d.ts +145 -0
- package/dist/glass/webgpu/regionBridge.js +1691 -0
- package/dist/glass/webgpu/regionScene.d.ts +294 -0
- package/dist/glass/webgpu/regionScene.js +2144 -0
- package/dist/glass/webgpu/renderer.d.ts +8 -0
- package/dist/glass/webgpu/renderer.js +1478 -0
- package/dist/glass/webgpu/residentPlanes.d.ts +69 -0
- package/dist/glass/webgpu/residentPlanes.js +104 -0
- package/dist/glass/webgpu/restMaterial.d.ts +6 -0
- package/dist/glass/webgpu/restMaterial.js +65 -0
- package/dist/glass/webgpu/resumeOverlay.d.ts +44 -0
- package/dist/glass/webgpu/resumeOverlay.js +82 -0
- package/dist/glass/webgpu/sampleTrace.d.ts +11 -0
- package/dist/glass/webgpu/sampleTrace.js +50 -0
- package/dist/glass/webgpu/scrollEdgeBackdrop.d.ts +89 -0
- package/dist/glass/webgpu/scrollEdgeBackdrop.js +177 -0
- package/dist/glass/webgpu/scrollEdgePose.d.ts +4 -0
- package/dist/glass/webgpu/scrollEdgePose.js +49 -0
- package/dist/glass/webgpu/selectionThumb.d.ts +81 -0
- package/dist/glass/webgpu/selectionThumb.js +209 -0
- package/dist/glass/webgpu/shaders.d.ts +67 -0
- package/dist/glass/webgpu/shaders.js +595 -0
- package/dist/glass/webgpu/sharedLift.d.ts +7 -0
- package/dist/glass/webgpu/sharedLift.js +223 -0
- package/dist/glass/webgpu/sourcePixels.d.ts +42 -0
- package/dist/glass/webgpu/sourcePixels.js +140 -0
- package/dist/glass/webgpu/sourcePixels.worker.d.ts +1 -0
- package/dist/glass/webgpu/sourcePixels.worker.js +62 -0
- package/dist/glass/webgpu/tabGlyphInk.d.ts +14 -0
- package/dist/glass/webgpu/tabGlyphInk.js +72 -0
- package/dist/glass/webgpu/touchLight.d.ts +4 -0
- package/dist/glass/webgpu/touchLight.js +15 -0
- package/dist/glass/worker/client.d.ts +21 -0
- package/dist/glass/worker/client.js +152 -0
- package/dist/glass/worker/experiment.d.ts +13 -0
- package/dist/glass/worker/experiment.js +221 -0
- package/dist/glass/worker/fixture.d.ts +21 -0
- package/dist/glass/worker/fixture.js +152 -0
- package/dist/glass/worker/protocol.d.ts +439 -0
- package/dist/glass/worker/protocol.js +5 -0
- package/dist/graphicsProbe.d.ts +19 -0
- package/dist/graphicsProbe.js +111 -0
- package/dist/haptics/SensoryButton.d.ts +21 -0
- package/dist/haptics/SensoryButton.js +80 -0
- package/dist/image/ContentAdmission.d.ts +19 -0
- package/dist/image/ContentAdmission.js +70 -0
- package/dist/image/Image.d.ts +30 -0
- package/dist/image/Image.js +341 -0
- package/dist/image/asset.d.ts +9 -0
- package/dist/image/asset.js +1 -0
- package/dist/image/contentReadiness.d.ts +7 -0
- package/dist/image/contentReadiness.js +54 -0
- package/dist/image/imports.d.ts +4 -0
- package/dist/image/resource.d.ts +53 -0
- package/dist/image/resource.js +286 -0
- package/dist/image/vite.d.ts +3 -0
- package/dist/image/vite.js +63 -0
- package/dist/index.d.ts +87 -0
- package/dist/index.js +102 -0
- package/dist/install.d.ts +8 -0
- package/dist/install.js +8 -0
- package/dist/internal.d.ts +96 -0
- package/dist/internal.js +80 -0
- package/dist/keyboard/adapter.d.ts +82 -0
- package/dist/keyboard/adapter.js +727 -0
- package/dist/keyboard/avoidance.d.ts +24 -0
- package/dist/keyboard/avoidance.js +283 -0
- package/dist/keyboard/caretMirror.d.ts +13 -0
- package/dist/keyboard/caretMirror.js +122 -0
- package/dist/keyboard/expectedKeyboard.d.ts +10 -0
- package/dist/keyboard/expectedKeyboard.js +60 -0
- package/dist/keyboard/geometry.d.ts +10 -0
- package/dist/keyboard/geometry.js +21 -0
- package/dist/keyboard/modalTapFocus.d.ts +41 -0
- package/dist/keyboard/modalTapFocus.js +224 -0
- package/dist/keyboard/motion.d.ts +54 -0
- package/dist/keyboard/motion.js +80 -0
- package/dist/keyboard/translation.d.ts +12 -0
- package/dist/keyboard/translation.js +19 -0
- package/dist/layout/adaptiveTabsContext.d.ts +5 -0
- package/dist/layout/adaptiveTabsContext.js +2 -0
- package/dist/layout/deviceIdiom.d.ts +4 -0
- package/dist/layout/deviceIdiom.js +12 -0
- package/dist/layout/layoutClass.d.ts +44 -0
- package/dist/layout/layoutClass.js +102 -0
- package/dist/layout/navigationLayoutContext.d.ts +8 -0
- package/dist/layout/navigationLayoutContext.js +5 -0
- package/dist/list-form/ListForm.d.ts +158 -0
- package/dist/list-form/ListForm.js +427 -0
- package/dist/list-form/ListItem.d.ts +30 -0
- package/dist/list-form/ListItem.js +251 -0
- package/dist/list-form/RowLabel.d.ts +14 -0
- package/dist/list-form/RowLabel.js +11 -0
- package/dist/list-form/Slider.d.ts +18 -0
- package/dist/list-form/Slider.js +235 -0
- package/dist/list-form/StepperButton.d.ts +9 -0
- package/dist/list-form/StepperButton.js +60 -0
- package/dist/list-form/Toggle.d.ts +16 -0
- package/dist/list-form/Toggle.js +204 -0
- package/dist/list-form/editMotion.d.ts +15 -0
- package/dist/list-form/editMotion.js +25 -0
- package/dist/list-form/editing.d.ts +102 -0
- package/dist/list-form/editing.js +51 -0
- package/dist/list-form/listContext.d.ts +4 -0
- package/dist/list-form/listContext.js +5 -0
- package/dist/list-form/offsets.d.ts +26 -0
- package/dist/list-form/offsets.js +34 -0
- package/dist/list-form/reorder.d.ts +20 -0
- package/dist/list-form/reorder.js +193 -0
- package/dist/list-form/rowMotion.d.ts +11 -0
- package/dist/list-form/rowMotion.js +236 -0
- package/dist/list-form/rowMotionScope.d.ts +14 -0
- package/dist/list-form/rowMotionScope.js +61 -0
- package/dist/list-form/separatorInsets.d.ts +4 -0
- package/dist/list-form/separatorInsets.js +87 -0
- package/dist/list-form/sliderContact.d.ts +40 -0
- package/dist/list-form/sliderContact.js +98 -0
- package/dist/list-form/state.d.ts +5 -0
- package/dist/list-form/state.js +8 -0
- package/dist/list-form/toggleContact.d.ts +55 -0
- package/dist/list-form/toggleContact.js +164 -0
- package/dist/media/controller.d.ts +7 -0
- package/dist/media/controller.js +612 -0
- package/dist/media/react.d.ts +9 -0
- package/dist/media/react.js +38 -0
- package/dist/media/systemMedia.d.ts +6 -0
- package/dist/media/systemMedia.js +200 -0
- package/dist/media/types.d.ts +83 -0
- package/dist/media/types.js +1 -0
- package/dist/menu/menuContact.d.ts +20 -0
- package/dist/menu/menuContact.js +286 -0
- package/dist/menu/menuGesture.d.ts +84 -0
- package/dist/menu/menuGesture.js +502 -0
- package/dist/menu/menuInteraction.d.ts +17 -0
- package/dist/menu/menuInteraction.js +34 -0
- package/dist/menu/menuOutside.d.ts +9 -0
- package/dist/menu/menuOutside.js +58 -0
- package/dist/menu/menuPanelContact.d.ts +11 -0
- package/dist/menu/menuPanelContact.js +77 -0
- package/dist/menu/menuTriggerHold.d.ts +3 -0
- package/dist/menu/menuTriggerHold.js +52 -0
- package/dist/menu/sampledMotion.d.ts +37 -0
- package/dist/menu/sampledMotion.js +86 -0
- package/dist/modal/lifecycle.d.ts +10 -0
- package/dist/modal/lifecycle.js +189 -0
- package/dist/motion/TransitionView.d.ts +12 -0
- package/dist/motion/TransitionView.js +118 -0
- package/dist/motion/animate.d.ts +20 -0
- package/dist/motion/animate.js +79 -0
- package/dist/motion/animationValue.d.ts +25 -0
- package/dist/motion/animationValue.js +42 -0
- package/dist/motion/composerMotion.d.ts +3 -0
- package/dist/motion/composerMotion.js +15 -0
- package/dist/motion/layoutTransaction.d.ts +7 -0
- package/dist/motion/layoutTransaction.js +28 -0
- package/dist/motion/matchedGeometry.d.ts +55 -0
- package/dist/motion/matchedGeometry.js +412 -0
- package/dist/motion/springPath.d.ts +27 -0
- package/dist/motion/springPath.js +46 -0
- package/dist/motion/springTrack.d.ts +33 -0
- package/dist/motion/springTrack.js +69 -0
- package/dist/motion/transition.d.ts +48 -0
- package/dist/motion/transition.js +56 -0
- package/dist/motion/transitionImages.d.ts +7 -0
- package/dist/motion/transitionImages.js +49 -0
- package/dist/motionProbe.d.ts +227 -0
- package/dist/motionProbe.js +697 -0
- package/dist/motionProbeControls.d.ts +2 -0
- package/dist/motionProbeControls.js +118 -0
- package/dist/motionProbeDelivery.d.ts +15 -0
- package/dist/motionProbeDelivery.js +84 -0
- package/dist/navigation/DetailList.d.ts +9 -0
- package/dist/navigation/DetailList.js +7 -0
- package/dist/navigation/NavigationChrome.d.ts +18 -0
- package/dist/navigation/NavigationChrome.js +195 -0
- package/dist/navigation/NavigationSearchRow.d.ts +9 -0
- package/dist/navigation/NavigationSearchRow.js +101 -0
- package/dist/navigation/NavigationTitleSlots.d.ts +18 -0
- package/dist/navigation/NavigationTitleSlots.js +64 -0
- package/dist/navigation/NavigatorHost.d.ts +17 -0
- package/dist/navigation/NavigatorHost.js +534 -0
- package/dist/navigation/ScreenContainer.d.ts +31 -0
- package/dist/navigation/ScreenContainer.js +219 -0
- package/dist/navigation/ScrubSourceFrames.d.ts +18 -0
- package/dist/navigation/ScrubSourceFrames.js +67 -0
- package/dist/navigation/Sidebar.d.ts +16 -0
- package/dist/navigation/Sidebar.js +20 -0
- package/dist/navigation/SidebarSymbol.d.ts +4 -0
- package/dist/navigation/SidebarSymbol.js +6 -0
- package/dist/navigation/ToolbarGlass.d.ts +2 -0
- package/dist/navigation/ToolbarGlass.js +429 -0
- package/dist/navigation/TransitionEdge.d.ts +4 -0
- package/dist/navigation/TransitionEdge.js +33 -0
- package/dist/navigation/bottomToolbarGeometry.d.ts +6 -0
- package/dist/navigation/bottomToolbarGeometry.js +14 -0
- package/dist/navigation/chromeCurves.d.ts +48 -0
- package/dist/navigation/chromeCurves.js +255 -0
- package/dist/navigation/glassTransition.d.ts +32 -0
- package/dist/navigation/glassTransition.js +55 -0
- package/dist/navigation/horizontalDrag.d.ts +6 -0
- package/dist/navigation/horizontalDrag.js +15 -0
- package/dist/navigation/interactivePop.d.ts +45 -0
- package/dist/navigation/interactivePop.js +86 -0
- package/dist/navigation/interactivePopTypes.d.ts +85 -0
- package/dist/navigation/interactivePopTypes.js +36 -0
- package/dist/navigation/keyboard.d.ts +9 -0
- package/dist/navigation/keyboard.js +55 -0
- package/dist/navigation/motion.d.ts +19 -0
- package/dist/navigation/motion.js +645 -0
- package/dist/navigation/motionTokens.d.ts +43 -0
- package/dist/navigation/motionTokens.js +75 -0
- package/dist/navigation/nativeScrubExperiment.d.ts +5 -0
- package/dist/navigation/nativeScrubExperiment.js +153 -0
- package/dist/navigation/nativeScrubSource.d.ts +15 -0
- package/dist/navigation/nativeScrubSource.js +88 -0
- package/dist/navigation/navigationChromeModel.d.ts +8 -0
- package/dist/navigation/navigationChromeModel.js +30 -0
- package/dist/navigation/navigationContext.d.ts +22 -0
- package/dist/navigation/navigationContext.js +65 -0
- package/dist/navigation/navigationEdge.d.ts +4 -0
- package/dist/navigation/navigationEdge.js +101 -0
- package/dist/navigation/pointerPop.d.ts +32 -0
- package/dist/navigation/pointerPop.js +206 -0
- package/dist/navigation/probeLayers.d.ts +7 -0
- package/dist/navigation/probeLayers.js +8 -0
- package/dist/navigation/registry.d.ts +14 -0
- package/dist/navigation/registry.js +31 -0
- package/dist/navigation/releaseDecision.d.ts +21 -0
- package/dist/navigation/releaseDecision.js +94 -0
- package/dist/navigation/releaseTrajectory.d.ts +20 -0
- package/dist/navigation/releaseTrajectory.js +95 -0
- package/dist/navigation/releaseVelocity.d.ts +9 -0
- package/dist/navigation/releaseVelocity.js +38 -0
- package/dist/navigation/routeCodec.d.ts +33 -0
- package/dist/navigation/routeCodec.js +123 -0
- package/dist/navigation/scrollEdge.d.ts +18 -0
- package/dist/navigation/scrollEdge.js +66 -0
- package/dist/navigation/scrubFrameLoop.d.ts +44 -0
- package/dist/navigation/scrubFrameLoop.js +129 -0
- package/dist/navigation/sidebarSelection.d.ts +5 -0
- package/dist/navigation/sidebarSelection.js +8 -0
- package/dist/navigation/splitContext.d.ts +17 -0
- package/dist/navigation/splitContext.js +9 -0
- package/dist/navigation/splitState.d.ts +46 -0
- package/dist/navigation/splitState.js +130 -0
- package/dist/navigation/stack.d.ts +25 -0
- package/dist/navigation/stack.js +136 -0
- package/dist/navigation/store.d.ts +80 -0
- package/dist/navigation/store.js +464 -0
- package/dist/navigation/toolbarGlassMotion.d.ts +40 -0
- package/dist/navigation/toolbarGlassMotion.js +202 -0
- package/dist/navigation/toolbarMinimization.d.ts +9 -0
- package/dist/navigation/toolbarMinimization.js +76 -0
- package/dist/navigation/toolbarMotionOwner.d.ts +6 -0
- package/dist/navigation/toolbarMotionOwner.js +30 -0
- package/dist/navigation/touchArbitration.d.ts +34 -0
- package/dist/navigation/touchArbitration.js +550 -0
- package/dist/navigation/tracking.d.ts +7 -0
- package/dist/navigation/tracking.js +19 -0
- package/dist/navigation/transitionBindings.d.ts +31 -0
- package/dist/navigation/transitionBindings.js +106 -0
- package/dist/navigation/transitionChrome.d.ts +31 -0
- package/dist/navigation/transitionChrome.js +380 -0
- package/dist/navigation/transitionPose.d.ts +52 -0
- package/dist/navigation/transitionPose.js +295 -0
- package/dist/navigation/types.d.ts +133 -0
- package/dist/navigation/types.js +1 -0
- package/dist/navigation/zoomDrag.d.ts +3 -0
- package/dist/navigation/zoomDrag.js +125 -0
- package/dist/navigation/zoomGeometry.d.ts +48 -0
- package/dist/navigation/zoomGeometry.js +94 -0
- package/dist/navigation/zoomGlass.d.ts +27 -0
- package/dist/navigation/zoomGlass.js +64 -0
- package/dist/navigation/zoomMotion.d.ts +73 -0
- package/dist/navigation/zoomMotion.js +104 -0
- package/dist/navigation/zoomTransition.d.ts +82 -0
- package/dist/navigation/zoomTransition.js +975 -0
- package/dist/presentation/Presentation.d.ts +61 -0
- package/dist/presentation/Presentation.js +92 -0
- package/dist/presentation/PresentationSurface.d.ts +29 -0
- package/dist/presentation/PresentationSurface.js +586 -0
- package/dist/presentation/PresentedSheet.d.ts +2 -0
- package/dist/presentation/PresentedSheet.js +571 -0
- package/dist/presentation/activity.d.ts +3 -0
- package/dist/presentation/activity.js +24 -0
- package/dist/presentation/coldLaunch.d.ts +2 -0
- package/dist/presentation/coldLaunch.js +19 -0
- package/dist/presentation/dismiss.d.ts +26 -0
- package/dist/presentation/dismiss.js +30 -0
- package/dist/presentation/glassMotion.d.ts +39 -0
- package/dist/presentation/glassMotion.js +259 -0
- package/dist/presentation/glassReady.d.ts +11 -0
- package/dist/presentation/glassReady.js +246 -0
- package/dist/presentation/model.d.ts +108 -0
- package/dist/presentation/model.js +183 -0
- package/dist/presentation/motion.d.ts +56 -0
- package/dist/presentation/motion.js +263 -0
- package/dist/presentation/presentationMotion.d.ts +10 -0
- package/dist/presentation/presentationMotion.js +74 -0
- package/dist/presentation/presentationSource.d.ts +2 -0
- package/dist/presentation/presentationSource.js +28 -0
- package/dist/presentation/sheetChromeContext.d.ts +2 -0
- package/dist/presentation/sheetChromeContext.js +3 -0
- package/dist/presentation/sheetMaterialTransition.d.ts +14 -0
- package/dist/presentation/sheetMaterialTransition.js +33 -0
- package/dist/presentation/sheetReleaseMotion.d.ts +18 -0
- package/dist/presentation/sheetReleaseMotion.js +123 -0
- package/dist/presentation/useAlertKeyboardMotion.d.ts +8 -0
- package/dist/presentation/useAlertKeyboardMotion.js +104 -0
- package/dist/press/Pressable.d.ts +19 -0
- package/dist/press/Pressable.js +16 -0
- package/dist/press/activation.d.ts +20 -0
- package/dist/press/activation.js +28 -0
- package/dist/press/contactReceipt.d.ts +23 -0
- package/dist/press/contactReceipt.js +177 -0
- package/dist/press/gestureArbitration.d.ts +13 -0
- package/dist/press/gestureArbitration.js +38 -0
- package/dist/press/stateMachine.d.ts +29 -0
- package/dist/press/stateMachine.js +39 -0
- package/dist/press/tokens.d.ts +18 -0
- package/dist/press/tokens.js +19 -0
- package/dist/press/usePressInteraction.d.ts +30 -0
- package/dist/press/usePressInteraction.js +247 -0
- package/dist/public/Label.d.ts +16 -0
- package/dist/public/Label.js +40 -0
- package/dist/public/NativeFilePicker.d.ts +19 -0
- package/dist/public/NativeFilePicker.js +75 -0
- package/dist/public/Screen.d.ts +42 -0
- package/dist/public/Screen.js +87 -0
- package/dist/public/actions.d.ts +16 -0
- package/dist/public/actions.js +15 -0
- package/dist/public/buttonBorderShape.d.ts +7 -0
- package/dist/public/buttonBorderShape.js +9 -0
- package/dist/public/chrome.d.ts +3 -0
- package/dist/public/chrome.js +5 -0
- package/dist/public/colors.d.ts +29 -0
- package/dist/public/colors.js +28 -0
- package/dist/public/context.d.ts +19 -0
- package/dist/public/context.js +8 -0
- package/dist/public/controlStyle.d.ts +93 -0
- package/dist/public/controlStyle.js +52 -0
- package/dist/public/controls.d.ts +360 -0
- package/dist/public/controls.js +301 -0
- package/dist/public/definition.d.ts +65 -0
- package/dist/public/definition.js +58 -0
- package/dist/public/deviceActions.d.ts +23 -0
- package/dist/public/deviceActions.js +87 -0
- package/dist/public/environment.d.ts +77 -0
- package/dist/public/environment.js +171 -0
- package/dist/public/fieldContext.d.ts +2 -0
- package/dist/public/fieldContext.js +3 -0
- package/dist/public/filePickers.d.ts +25 -0
- package/dist/public/filePickers.js +11 -0
- package/dist/public/glass.d.ts +31 -0
- package/dist/public/glass.js +29 -0
- package/dist/public/glassValue.d.ts +19 -0
- package/dist/public/glassValue.js +32 -0
- package/dist/public/keyboard.d.ts +4 -0
- package/dist/public/keyboard.js +6 -0
- package/dist/public/navigationTransition.d.ts +48 -0
- package/dist/public/navigationTransition.js +49 -0
- package/dist/public/pickerNavigation.d.ts +7 -0
- package/dist/public/pickerNavigation.js +64 -0
- package/dist/public/presentation.d.ts +40 -0
- package/dist/public/presentation.js +21 -0
- package/dist/public/presentationContext.d.ts +2 -0
- package/dist/public/presentationContext.js +3 -0
- package/dist/public/routeRegistry.d.ts +3 -0
- package/dist/public/routeRegistry.js +10 -0
- package/dist/public/routes.d.ts +74 -0
- package/dist/public/routes.js +105 -0
- package/dist/public/runtime.d.ts +134 -0
- package/dist/public/runtime.js +292 -0
- package/dist/public/screenChrome.d.ts +91 -0
- package/dist/public/screenChrome.js +187 -0
- package/dist/public/scroll.d.ts +38 -0
- package/dist/public/scroll.js +7 -0
- package/dist/public/storage.d.ts +9 -0
- package/dist/public/storage.js +9 -0
- package/dist/public/structure.d.ts +65 -0
- package/dist/public/structure.js +40 -0
- package/dist/public/toolbar.d.ts +37 -0
- package/dist/public/toolbar.js +76 -0
- package/dist/push/PushPermissionExplainer.d.ts +14 -0
- package/dist/push/PushPermissionExplainer.js +30 -0
- package/dist/push/notifications.d.ts +54 -0
- package/dist/push/notifications.js +276 -0
- package/dist/push/routes.d.ts +20 -0
- package/dist/push/routes.js +63 -0
- package/dist/push/schema.d.ts +42 -0
- package/dist/push/schema.js +41 -0
- package/dist/push/urlProjection.d.ts +24 -0
- package/dist/push/urlProjection.js +207 -0
- package/dist/reachability/controller.d.ts +21 -0
- package/dist/reachability/controller.js +202 -0
- package/dist/reachability/model.d.ts +94 -0
- package/dist/reachability/model.js +131 -0
- package/dist/reachability/react.d.ts +29 -0
- package/dist/reachability/react.js +69 -0
- package/dist/restoration/navigation.d.ts +9 -0
- package/dist/restoration/navigation.js +55 -0
- package/dist/restoration/react.d.ts +6 -0
- package/dist/restoration/react.js +21 -0
- package/dist/restoration/routes.d.ts +22 -0
- package/dist/restoration/routes.js +166 -0
- package/dist/restoration/scene.d.ts +44 -0
- package/dist/restoration/scene.js +284 -0
- package/dist/root/SatinRoot.d.ts +25 -0
- package/dist/root/SatinRoot.js +512 -0
- package/dist/root/mountSatin.d.ts +12 -0
- package/dist/root/mountSatin.js +60 -0
- package/dist/root/runtimeContext.d.ts +26 -0
- package/dist/root/runtimeContext.js +30 -0
- package/dist/safe-area/Overlay.d.ts +9 -0
- package/dist/safe-area/Overlay.js +26 -0
- package/dist/safe-area/SafeAreaInset.d.ts +25 -0
- package/dist/safe-area/SafeAreaInset.js +138 -0
- package/dist/safe-area/bottomInsetGeometry.d.ts +12 -0
- package/dist/safe-area/bottomInsetGeometry.js +73 -0
- package/dist/safe-area/context.d.ts +15 -0
- package/dist/safe-area/context.js +8 -0
- package/dist/safe-area/displayCorners.d.ts +21 -0
- package/dist/safe-area/displayCorners.js +70 -0
- package/dist/safe-area/keyboardCarrier.d.ts +26 -0
- package/dist/safe-area/keyboardCarrier.js +394 -0
- package/dist/safe-area/keyboardCarrierGeometry.d.ts +37 -0
- package/dist/safe-area/keyboardCarrierGeometry.js +49 -0
- package/dist/safe-area/modifiers.d.ts +52 -0
- package/dist/safe-area/modifiers.js +86 -0
- package/dist/safe-area/useSafeAreaMetrics.d.ts +83 -0
- package/dist/safe-area/useSafeAreaMetrics.js +367 -0
- package/dist/safe-area/windowChrome.d.ts +7 -0
- package/dist/safe-area/windowChrome.js +14 -0
- package/dist/scroll/RefreshControl.d.ts +8 -0
- package/dist/scroll/RefreshControl.js +135 -0
- package/dist/scroll/ScrollSurface.d.ts +62 -0
- package/dist/scroll/ScrollSurface.js +278 -0
- package/dist/scroll/ScrollTarget.d.ts +6 -0
- package/dist/scroll/ScrollTarget.js +11 -0
- package/dist/scroll/edgePolicy.d.ts +21 -0
- package/dist/scroll/edgePolicy.js +17 -0
- package/dist/scroll/insertionMotion.d.ts +15 -0
- package/dist/scroll/insertionMotion.js +128 -0
- package/dist/scroll/nativeScroll.d.ts +14 -0
- package/dist/scroll/nativeScroll.js +71 -0
- package/dist/scroll/position.d.ts +31 -0
- package/dist/scroll/position.js +273 -0
- package/dist/scroll/pressOwnership.d.ts +4 -0
- package/dist/scroll/pressOwnership.js +65 -0
- package/dist/scroll/refreshMotion.d.ts +9 -0
- package/dist/scroll/refreshMotion.js +15 -0
- package/dist/scroll/refreshable.d.ts +6 -0
- package/dist/scroll/refreshable.js +50 -0
- package/dist/scroll/registry.d.ts +38 -0
- package/dist/scroll/registry.js +261 -0
- package/dist/scroll/screenGeometry.d.ts +8 -0
- package/dist/scroll/screenGeometry.js +50 -0
- package/dist/scroll/scrollIndicator.d.ts +7 -0
- package/dist/scroll/scrollIndicator.js +114 -0
- package/dist/scroll/scrollJump.d.ts +7 -0
- package/dist/scroll/scrollJump.js +16 -0
- package/dist/scroll/types.d.ts +12 -0
- package/dist/scroll/types.js +1 -0
- package/dist/search/SearchDrawer.d.ts +16 -0
- package/dist/search/SearchDrawer.js +421 -0
- package/dist/search/SearchScopesBar.d.ts +6 -0
- package/dist/search/SearchScopesBar.js +12 -0
- package/dist/search/SearchToolbarField.d.ts +8 -0
- package/dist/search/SearchToolbarField.js +115 -0
- package/dist/search/searchBackdrop.d.ts +12 -0
- package/dist/search/searchBackdrop.js +45 -0
- package/dist/search/searchChrome.d.ts +13 -0
- package/dist/search/searchChrome.js +31 -0
- package/dist/search/searchMotion.d.ts +30 -0
- package/dist/search/searchMotion.js +46 -0
- package/dist/search/searchPlacement.d.ts +11 -0
- package/dist/search/searchPlacement.js +15 -0
- package/dist/search/searchable.d.ts +46 -0
- package/dist/search/searchable.js +5 -0
- package/dist/search/useSearchPresentation.d.ts +15 -0
- package/dist/search/useSearchPresentation.js +61 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.js +144 -0
- package/dist/settings/DisclosureIndicator.d.ts +4 -0
- package/dist/settings/DisclosureIndicator.js +6 -0
- package/dist/settings/tokens.d.ts +129 -0
- package/dist/settings/tokens.js +143 -0
- package/dist/shapes/corners.d.ts +14 -0
- package/dist/shapes/corners.js +80 -0
- package/dist/styles.css +9078 -0
- package/dist/styles.css.d.ts +3 -0
- package/dist/sw/activationState.d.ts +20 -0
- package/dist/sw/activationState.js +39 -0
- package/dist/sw/generator.d.ts +14 -0
- package/dist/sw/generator.js +26 -0
- package/dist/sw/manifest.d.ts +32 -0
- package/dist/sw/manifest.js +137 -0
- package/dist/sw/protocol.d.ts +7 -0
- package/dist/sw/protocol.js +7 -0
- package/dist/sw/pushRuntime.d.ts +2 -0
- package/dist/sw/pushRuntime.js +161 -0
- package/dist/sw/registerAppShell.d.ts +25 -0
- package/dist/sw/registerAppShell.js +259 -0
- package/dist/sw/renderServiceWorker.d.ts +10 -0
- package/dist/sw/renderServiceWorker.js +370 -0
- package/dist/sw/requestRouting.d.ts +13 -0
- package/dist/sw/requestRouting.js +26 -0
- package/dist/sw/updateCheck.d.ts +2 -0
- package/dist/sw/updateCheck.js +25 -0
- package/dist/sw/vite.d.ts +20 -0
- package/dist/sw/vite.js +71 -0
- package/dist/symbols/Symbol.d.ts +33 -0
- package/dist/symbols/Symbol.js +59 -0
- package/dist/symbols/catalogue.d.ts +4 -0
- package/dist/symbols/catalogue.js +4139 -0
- package/dist/symbols/exclusions.d.ts +2 -0
- package/dist/symbols/exclusions.js +4 -0
- package/dist/symbols/inheritedWeight.d.ts +3 -0
- package/dist/symbols/inheritedWeight.js +104 -0
- package/dist/symbols/metrics.d.ts +38 -0
- package/dist/symbols/metrics.js +53 -0
- package/dist/symbols/svg.d.ts +3 -0
- package/dist/symbols/svg.js +16 -0
- package/dist/symbols/types.d.ts +30 -0
- package/dist/symbols/types.js +1 -0
- package/dist/tabs/TabSection.d.ts +12 -0
- package/dist/tabs/TabSection.js +4 -0
- package/dist/tabs/TabSidebarSurface.d.ts +7 -0
- package/dist/tabs/TabSidebarSurface.js +29 -0
- package/dist/tabs/TabView.d.ts +28 -0
- package/dist/tabs/TabView.js +258 -0
- package/dist/tabs/accessoryContact.d.ts +2 -0
- package/dist/tabs/accessoryContact.js +42 -0
- package/dist/tabs/compactBar.d.ts +4 -0
- package/dist/tabs/compactBar.js +28 -0
- package/dist/tabs/context.d.ts +8 -0
- package/dist/tabs/context.js +6 -0
- package/dist/tabs/keyboard.d.ts +3 -0
- package/dist/tabs/keyboard.js +23 -0
- package/dist/tabs/model.d.ts +6 -0
- package/dist/tabs/model.js +16 -0
- package/dist/tabs/presentation.d.ts +7 -0
- package/dist/tabs/presentation.js +14 -0
- package/dist/tabs/sourceRevision.d.ts +3 -0
- package/dist/tabs/sourceRevision.js +36 -0
- package/dist/tabs/tabSidebarGlyph.d.ts +2 -0
- package/dist/tabs/tabSidebarGlyph.js +11 -0
- package/dist/tasks/useTask.d.ts +16 -0
- package/dist/tasks/useTask.js +67 -0
- package/dist/testing.d.ts +9 -0
- package/dist/testing.js +11 -0
- package/dist/text-input/TextEditor.d.ts +25 -0
- package/dist/text-input/TextEditor.js +74 -0
- package/dist/text-input/TextField.d.ts +32 -0
- package/dist/text-input/TextField.js +29 -0
- package/dist/text-input/editorGeometry.d.ts +2 -0
- package/dist/text-input/editorGeometry.js +6 -0
- package/dist/text-input/editorTextLayout.d.ts +2 -0
- package/dist/text-input/editorTextLayout.js +31 -0
- package/dist/text-input/focusChain.d.ts +27 -0
- package/dist/text-input/focusChain.js +62 -0
- package/dist/text-input/kinds.d.ts +8 -0
- package/dist/text-input/kinds.js +34 -0
- package/dist/text-input/measureEditor.d.ts +2 -0
- package/dist/text-input/measureEditor.js +50 -0
- package/dist/text-input/submit.d.ts +9 -0
- package/dist/text-input/submit.js +7 -0
- package/dist/unstable.d.ts +16 -0
- package/dist/unstable.js +16 -0
- package/dist/vite.d.ts +3 -0
- package/dist/vite.js +3 -0
- package/dist/web/WebView.d.ts +42 -0
- package/dist/web/WebView.js +115 -0
- package/dist/web/authentication.d.ts +75 -0
- package/dist/web/authentication.js +488 -0
- package/dist/web/openURL.d.ts +37 -0
- package/dist/web/openURL.js +89 -0
- package/dist/web/urlPolicy.d.ts +3 -0
- package/dist/web/urlPolicy.js +19 -0
- package/dist/web/useWebAuthentication.d.ts +3 -0
- package/dist/web/useWebAuthentication.js +6 -0
- package/dist/zoom/Zoomable.d.ts +10 -0
- package/dist/zoom/Zoomable.js +116 -0
- package/dist/zoom/geometry.d.ts +11 -0
- package/dist/zoom/geometry.js +16 -0
- package/package.json +116 -3
|
@@ -0,0 +1,2144 @@
|
|
|
1
|
+
import { sampleKeyboardTranslation } from '../../keyboard/translation.js';
|
|
2
|
+
import { accessoryPose, accessoryOffset } from './accessoryTrack.js';
|
|
3
|
+
import { shapeTrackRunning, trackedShapeSpec } from '../shapeTrack.js';
|
|
4
|
+
import { MenuTouchLight } from '../lens/menuTouchLight.js';
|
|
5
|
+
import { FIELD_MOTION_DURATION, ZERO_FIELD_VELOCITY, sampleFieldMember, followFieldTarget, } from '../fieldMotion.js';
|
|
6
|
+
import { sampleMenuCorner } from '../lens/menuCornerMotion.js';
|
|
7
|
+
import { transitionMenuMorph, menuMorphDuration, menuMotionProfile as emergeProfile, menuMorphEndpoint, MENU_MORPH_ZERO, } from '../lens/menuMorphMotion.js';
|
|
8
|
+
import { lowerShapeTop } from '../lens/drawableTop.js';
|
|
9
|
+
import { menuMaterialProgress } from '../lens/menuMotion.js';
|
|
10
|
+
import { glassBlendRadius } from '../morphField.js';
|
|
11
|
+
import { menuFieldFrame, menuSeed, plainMenuPresentation } from '../lens/menuField.js';
|
|
12
|
+
import { menuContextPresentation } from '../lens/menuContextPresentation.js';
|
|
13
|
+
import { contextSampleInset } from './contextComposite.js';
|
|
14
|
+
import { Backdrop } from './backdrop.js';
|
|
15
|
+
import { FrameGraph } from './frameGraph.js';
|
|
16
|
+
import { TabMotion, TAB_INTERACTIVE_MOTION } from '../lens/tabMotion.js';
|
|
17
|
+
import { InteractivePress, interactivePressScale, interactiveHighlight, withHighlight, INTERACTIVE_HIGHLIGHT_RELEASE_MS, } from '../lens/interactivePress.js';
|
|
18
|
+
import { GLASS_CONTACT_PHYSICS, glassContactKind } from '../contactPhysics.js';
|
|
19
|
+
import { InteractiveContact, GLASS_CONTACT_REST, } from '../lens/interactiveContact.js';
|
|
20
|
+
import { GLASS_RENDERER_PROTOCOL, } from '../rendererContract.js';
|
|
21
|
+
import { frameUnion, glassPresenterBounds, glassRadius, glassViewportFrame, isPlainMenuSource, validateGlassRegions, } from '../regions.js';
|
|
22
|
+
import { LayerDevice } from './layers.js';
|
|
23
|
+
import { controlLensSize, controlLiftProfile } from '../lens/controlLift.js';
|
|
24
|
+
import { SelectionThumb, selectionPill } from './selectionThumb.js';
|
|
25
|
+
import { WebGPURaster, drawableCosts } from './raster.js';
|
|
26
|
+
import { glassTiming, glassNow } from './profile.js';
|
|
27
|
+
/** A presenter is a DOM stacking slot. Its shader coordinates remain local. */
|
|
28
|
+
export function regionSpec(region, presenter, scene) {
|
|
29
|
+
const inset = contextSampleInset(region.material.treatment);
|
|
30
|
+
const frame = glassViewportFrame(region, scene.scrolls), radius = glassRadius(region.shape, frame.width, frame.height);
|
|
31
|
+
const spec = {
|
|
32
|
+
id: `region:${region.id}`,
|
|
33
|
+
backdropExclusions: region.backdropExclusions,
|
|
34
|
+
// A menu's glass sees the dim it was opened over. That includes its submenu platters: on
|
|
35
|
+
// their own they would sample the undimmed page, so a bright colour behind the dim (a sent
|
|
36
|
+
// bubble) shone through them at full strength.
|
|
37
|
+
backdropMenuOverlay: !!region.emerge || !!region.inMenuPanel || region.material.treatment === 'contextMenu',
|
|
38
|
+
variant: region.material.variant,
|
|
39
|
+
treatment: region.material.treatment,
|
|
40
|
+
sheetOpaqueMix: region.sheetOpaqueMix,
|
|
41
|
+
popoverArrow: region.popoverArrow,
|
|
42
|
+
// A segmented glass (the scope bar) is pressed like interactive glass: it lights too.
|
|
43
|
+
tint: (region.material.interactive && !region.material.control) || region.selection
|
|
44
|
+
? withHighlight(region.material.tint, HIGHLIGHT_FLOOR)
|
|
45
|
+
: region.material.tint,
|
|
46
|
+
frameX: frame.x - presenter.origin.x,
|
|
47
|
+
frameY: frame.y - presenter.origin.y,
|
|
48
|
+
frameWidth: frame.width,
|
|
49
|
+
frameHeight: frame.height,
|
|
50
|
+
radius,
|
|
51
|
+
sampleWidth: frame.width + 2 * inset,
|
|
52
|
+
sampleHeight: frame.height + 2 * inset,
|
|
53
|
+
sampleInsetX: inset,
|
|
54
|
+
sampleInsetY: inset,
|
|
55
|
+
shaderAperture: true,
|
|
56
|
+
materialMix: 1,
|
|
57
|
+
// Every menu surface is the menu recipe's frosted material, as native's is: the root menu (the
|
|
58
|
+
// emerging region) and its submenu platters alike. Through the bar recipe (a 2.9 px blur) the
|
|
59
|
+
// page's text and bubbles read clearly through the menu. The root's morph from its trigger
|
|
60
|
+
// blends from the bar recipe (emergeRegionSpec `recipeMix`).
|
|
61
|
+
materialProfile: region.material.treatment === 'contextMenu' || region.inMenuPanel || region.emerge
|
|
62
|
+
? 'menu'
|
|
63
|
+
: (region.material.recipe ?? 'chrome'),
|
|
64
|
+
shape: {
|
|
65
|
+
blendRadius: 0,
|
|
66
|
+
rimAlpha: (region.material.treatment && region.material.treatment !== 'contextMenu') ||
|
|
67
|
+
region.material.variant === 'clear'
|
|
68
|
+
? 0
|
|
69
|
+
: 1,
|
|
70
|
+
whiteMix: 0,
|
|
71
|
+
first: {
|
|
72
|
+
centerX: frame.width / 2,
|
|
73
|
+
centerY: frame.height / 2,
|
|
74
|
+
halfWidth: frame.width / 2,
|
|
75
|
+
halfHeight: frame.height / 2,
|
|
76
|
+
radius,
|
|
77
|
+
...(typeof region.shape === 'object' && region.shape.bottomCornerRadius !== undefined
|
|
78
|
+
? {
|
|
79
|
+
bottomRadius: Math.min(region.shape.bottomCornerRadius, frame.width / 2, frame.height / 2),
|
|
80
|
+
}
|
|
81
|
+
: {}),
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
if (region.selection)
|
|
86
|
+
return { ...spec, tabSelection: selectionPill(spec, region.selection) };
|
|
87
|
+
// A control's lens is installed transparent at rest, before the page lifts it.
|
|
88
|
+
const control = region.material.control;
|
|
89
|
+
return control ? controlLensSpec(spec, control, 0, spec.frameWidth / 2) : spec;
|
|
90
|
+
}
|
|
91
|
+
/** The selection's labels as ink: the region's whole foreground atlas, captured in the region's
|
|
92
|
+
* sample space (regionBridge), drawn over the pill (rasterMaterial encodeTabSelection). */
|
|
93
|
+
export function selectionInk(spec, foreground) {
|
|
94
|
+
if (!spec.tabSelection || !foreground)
|
|
95
|
+
return spec;
|
|
96
|
+
const { width, height } = foreground;
|
|
97
|
+
return {
|
|
98
|
+
...spec,
|
|
99
|
+
ink: {
|
|
100
|
+
atlas: { width, height },
|
|
101
|
+
layers: [
|
|
102
|
+
{
|
|
103
|
+
source: { x: 0, y: 0, width, height },
|
|
104
|
+
frame: { x: -spec.sampleInsetX, y: -spec.sampleInsetY, width, height },
|
|
105
|
+
opacity: 1,
|
|
106
|
+
blur: 0,
|
|
107
|
+
lens: 0,
|
|
108
|
+
lensWidth: 1,
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
export function fieldRegionSpec(regions, presenter, scene) {
|
|
115
|
+
const base = regionSpec(regions[0], presenter, scene), inset = base.sampleInsetX;
|
|
116
|
+
const members = regions.map(region => {
|
|
117
|
+
const frame = glassViewportFrame(region, scene.scrolls);
|
|
118
|
+
return {
|
|
119
|
+
centerX: frame.x + frame.width / 2 - presenter.origin.x - inset,
|
|
120
|
+
centerY: frame.y + frame.height / 2 - presenter.origin.y - inset,
|
|
121
|
+
halfWidth: frame.width / 2,
|
|
122
|
+
halfHeight: frame.height / 2,
|
|
123
|
+
radius: glassRadius(region.shape, frame.width, frame.height),
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
return {
|
|
127
|
+
...base,
|
|
128
|
+
frameX: inset,
|
|
129
|
+
frameY: inset,
|
|
130
|
+
frameWidth: presenter.width - 2 * inset,
|
|
131
|
+
frameHeight: presenter.height - 2 * inset,
|
|
132
|
+
sampleWidth: presenter.width,
|
|
133
|
+
sampleHeight: presenter.height,
|
|
134
|
+
shape: {
|
|
135
|
+
...base.shape,
|
|
136
|
+
first: members[0],
|
|
137
|
+
members,
|
|
138
|
+
blendRadius: glassBlendRadius(regions[0].container.spacing),
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/** The phone menu table's time zero after the trigger's release epoch. The fit's zero is native
|
|
143
|
+
* release +43 ms (menuMorphPhoneFit.json); our release epoch is the page's commit and the frame
|
|
144
|
+
* then reaches the screen later, so Catalyst captures (BC5) trailed native by about 25 ms at 43.
|
|
145
|
+
* 20 aligns the measured outline within the native recording's ±30 ms calibration. */
|
|
146
|
+
const MENU_PHONE_RELEASE_LEAD = 20;
|
|
147
|
+
/** How long a phone close holds its open pose after its scene arrives (see the close start). */
|
|
148
|
+
const MENU_PHONE_CLOSE_HOLD = 10;
|
|
149
|
+
/** A release older than this is not the one that opened the menu (keyboard, reopen). */
|
|
150
|
+
const MENU_RELEASE_ANCHOR_WINDOW = 400;
|
|
151
|
+
export { menuMotionProfile as emergeProfile } from '../lens/menuMorphMotion.js';
|
|
152
|
+
/** Shared measured mass 1, stiffness 186.57, damping 25.13. */
|
|
153
|
+
export function fieldSpringProgress(ms) {
|
|
154
|
+
const t = Math.max(0, ms) / 1000, a = 25.13 / 2, w = Math.sqrt(186.57 - a * a);
|
|
155
|
+
return 1 - Math.exp(-a * t) * (Math.cos(w * t) + (a / w) * Math.sin(w * t));
|
|
156
|
+
}
|
|
157
|
+
/** Interactive glass always carries a tint so the press highlight's pass exists from install (no
|
|
158
|
+
* mid-press reallocation); the floor is invisible (1/10000 white). */
|
|
159
|
+
const HIGHLIGHT_FLOOR = 1e-4;
|
|
160
|
+
/** An emerging region samples over its trigger and panel (the presenter bounds). Its glass is one
|
|
161
|
+
* field of two members (lens/menuField.ts): the panel member moves from the trigger's seed (its
|
|
162
|
+
* round part at the menu's own button: the whole trigger when it is a circle; progress 0) to the
|
|
163
|
+
* panel (1) inside that fixed envelope, and the trigger member holds, is absorbed on open and
|
|
164
|
+
* grows back on close. `field` gives the time since the morph's first frame; without it the pose
|
|
165
|
+
* alone decides (the trigger member is present only at progress 0). */
|
|
166
|
+
export function emergeRegionSpec(spec, region, presenter, progress, corner = { rounding: 0 }, liveSource, field, appearance = 'dark') {
|
|
167
|
+
const pose = typeof progress === 'number'
|
|
168
|
+
? {
|
|
169
|
+
...MENU_MORPH_ZERO,
|
|
170
|
+
centerX: progress,
|
|
171
|
+
centerY: progress,
|
|
172
|
+
width: progress,
|
|
173
|
+
height: progress,
|
|
174
|
+
}
|
|
175
|
+
: progress;
|
|
176
|
+
const source = liveSource ?? region.emerge?.from;
|
|
177
|
+
if (!source)
|
|
178
|
+
return spec;
|
|
179
|
+
const rest = region.emerge?.returnTo ?? source;
|
|
180
|
+
const from = source;
|
|
181
|
+
const inset = spec.sampleInsetX, bounds = glassPresenterBounds(region.frame, inset, region.emerge);
|
|
182
|
+
const ux = bounds.x + inset, uy = bounds.y + inset, uw = bounds.width - 2 * inset, uh = bounds.height - 2 * inset;
|
|
183
|
+
const lerp = (a, b, p) => a + (b - a) * p;
|
|
184
|
+
const panel = {
|
|
185
|
+
cx: region.frame.x + region.frame.width / 2 - ux,
|
|
186
|
+
cy: region.frame.y + region.frame.height / 2 - uy,
|
|
187
|
+
hw: region.frame.width / 2,
|
|
188
|
+
hh: region.frame.height / 2,
|
|
189
|
+
r: spec.shape.first.radius,
|
|
190
|
+
};
|
|
191
|
+
/** A trigger rectangle (viewport points) as a field member in the presenter's frame. */
|
|
192
|
+
const member = (rect) => ({
|
|
193
|
+
centerX: rect.x + rect.width / 2 - ux,
|
|
194
|
+
centerY: rect.y + rect.height / 2 - uy,
|
|
195
|
+
halfWidth: rect.width / 2,
|
|
196
|
+
halfHeight: rect.height / 2,
|
|
197
|
+
radius: Math.min(rect.radius, rect.width / 2, rect.height / 2),
|
|
198
|
+
});
|
|
199
|
+
const anchor = region.emerge?.anchor && {
|
|
200
|
+
x: region.emerge.anchor.x - ux,
|
|
201
|
+
y: region.emerge.anchor.y - uy,
|
|
202
|
+
};
|
|
203
|
+
const home = member(rest);
|
|
204
|
+
// The drop leaves from the trigger's seed: the pressed aperture's on open, the released
|
|
205
|
+
// trigger's on the way back. For a circle both are the trigger itself.
|
|
206
|
+
// A plain source draws no glass at rest, so its drop grows from (and pours back into) the
|
|
207
|
+
// centre of its seed: menuDropFit.json (a label) and menuGlyphDropFit.json (a glyph) were
|
|
208
|
+
// fitted from that point.
|
|
209
|
+
const seedOf = (shape) => {
|
|
210
|
+
if (region.emerge?.sourceKind === 'context')
|
|
211
|
+
return shape;
|
|
212
|
+
const seed = menuSeed(shape, anchor);
|
|
213
|
+
return isPlainMenuSource(region.emerge?.sourceKind)
|
|
214
|
+
? { ...seed, halfWidth: 0.05, halfHeight: 0.05, radius: 0.05 }
|
|
215
|
+
: seed;
|
|
216
|
+
};
|
|
217
|
+
const pressed = seedOf(member(from));
|
|
218
|
+
const released = seedOf(home);
|
|
219
|
+
const trigger = {
|
|
220
|
+
cx: pressed.centerX,
|
|
221
|
+
cy: pressed.centerY,
|
|
222
|
+
hw: pressed.halfWidth,
|
|
223
|
+
hh: pressed.halfHeight,
|
|
224
|
+
r: pressed.radius,
|
|
225
|
+
};
|
|
226
|
+
// Return corrections are additive and axis-specific. Multiplying a changing
|
|
227
|
+
// source by (1-progress) inflated the middle of a close and lost the measured path.
|
|
228
|
+
const hw = Math.max(1, lerp(trigger.hw, panel.hw, pose.width) +
|
|
229
|
+
(released.halfWidth - pressed.halfWidth) * pose.returnWidth);
|
|
230
|
+
const hh = Math.max(1, lerp(trigger.hh, panel.hh, pose.height) +
|
|
231
|
+
(released.halfHeight - pressed.halfHeight) * pose.returnHeight);
|
|
232
|
+
const cx = lerp(trigger.cx, panel.cx, pose.centerX) + (released.centerX - pressed.centerX) * pose.returnX;
|
|
233
|
+
const cy = lerp(trigger.cy, panel.cy, pose.centerY) + (released.centerY - pressed.centerY) * pose.returnY;
|
|
234
|
+
// A plain drop's rounding and fade are its own measured curves: it has no glass button to
|
|
235
|
+
// return to.
|
|
236
|
+
const sourceKind = region.emerge?.sourceKind;
|
|
237
|
+
const plainPresentation = sourceKind === 'label' || sourceKind === 'glyph'
|
|
238
|
+
? plainMenuPresentation(sourceKind, field?.direction ?? 'open', field?.ms ?? 0, field?.settled ?? pose.width >= 1)
|
|
239
|
+
: undefined;
|
|
240
|
+
const contextPresentation = region.emerge?.sourceKind === 'context'
|
|
241
|
+
? menuContextPresentation(field?.direction ?? 'open', field?.ms ?? 0, field?.settled ?? false, appearance)
|
|
242
|
+
: undefined;
|
|
243
|
+
const moving = lowerShapeTop({
|
|
244
|
+
centerX: cx,
|
|
245
|
+
centerY: cy,
|
|
246
|
+
halfWidth: hw,
|
|
247
|
+
halfHeight: hh,
|
|
248
|
+
// The recorded iPad corner curve squares the early aperture; the phone drop stays round.
|
|
249
|
+
radius: Math.max(0, Math.min(lerp(trigger.r, panel.r, contextPresentation?.radiusProgress ?? pose.width) +
|
|
250
|
+
(released.radius - pressed.radius) * pose.returnWidth +
|
|
251
|
+
(contextPresentation?.rounding ??
|
|
252
|
+
plainPresentation?.rounding ??
|
|
253
|
+
Math.max(0, corner.rounding)) *
|
|
254
|
+
Math.min(hw, hh), hw, hh)),
|
|
255
|
+
}, uy);
|
|
256
|
+
const local = (rect) => ({
|
|
257
|
+
...rect,
|
|
258
|
+
x: rect.x - ux,
|
|
259
|
+
y: rect.y - uy,
|
|
260
|
+
});
|
|
261
|
+
const ink = region.emerge?.ink;
|
|
262
|
+
const direction = field?.direction ?? 'open';
|
|
263
|
+
const frame = menuFieldFrame({
|
|
264
|
+
sourceKind: region.emerge?.sourceKind,
|
|
265
|
+
direction,
|
|
266
|
+
ms: field?.ms ?? (pose.width >= 1 ? Infinity : 0),
|
|
267
|
+
panel: moving,
|
|
268
|
+
home,
|
|
269
|
+
anchor,
|
|
270
|
+
pressedScale: source.width / rest.width,
|
|
271
|
+
pressHighlight: field?.pressHighlight ?? 0,
|
|
272
|
+
panelFrame: local(region.frame),
|
|
273
|
+
rows: ink?.rows && !(direction === 'open' && region.emerge?.inkDom)
|
|
274
|
+
? { source: ink.rows.source, frame: local(ink.rows.frame) }
|
|
275
|
+
: undefined,
|
|
276
|
+
glyph: ink?.glyph ? { source: ink.glyph.source, frame: local(ink.glyph.frame) } : undefined,
|
|
277
|
+
settled: field?.settled ?? pose.width >= 1,
|
|
278
|
+
});
|
|
279
|
+
// A source with nothing visible at rest (a label, a recognising context press) prewarms its
|
|
280
|
+
// glass transparent until the presenter is ready; then it is drawn whole.
|
|
281
|
+
const held = (isPlainMenuSource(region.emerge?.sourceKind) ||
|
|
282
|
+
region.emerge?.recognitionEpoch !== undefined) &&
|
|
283
|
+
direction === 'open' &&
|
|
284
|
+
region.emerge.ready === false;
|
|
285
|
+
return {
|
|
286
|
+
...spec,
|
|
287
|
+
opacity: held
|
|
288
|
+
? 0
|
|
289
|
+
: (contextPresentation?.opacity ?? plainPresentation?.opacity ?? spec.opacity),
|
|
290
|
+
prewarmTransparent: held,
|
|
291
|
+
frameX: ux - presenter.origin.x,
|
|
292
|
+
frameY: uy - presenter.origin.y,
|
|
293
|
+
frameWidth: uw,
|
|
294
|
+
frameHeight: uh,
|
|
295
|
+
sampleWidth: uw + 2 * inset,
|
|
296
|
+
sampleHeight: uh + 2 * inset,
|
|
297
|
+
// The press highlight carried from the trigger; the floor keeps its pass allocated.
|
|
298
|
+
tint: withHighlight(region.material.tint, Math.max(HIGHLIGHT_FLOOR, frame.highlight)),
|
|
299
|
+
shape: {
|
|
300
|
+
...spec.shape,
|
|
301
|
+
first: frame.members[0],
|
|
302
|
+
members: frame.members,
|
|
303
|
+
blendRadius: frame.blendRadius,
|
|
304
|
+
},
|
|
305
|
+
...(ink && frame.ink.length ? { ink: { atlas: ink.atlas, layers: frame.ink } } : {}),
|
|
306
|
+
recipeMix: field?.recipeMix ??
|
|
307
|
+
emergeRecipeMix(sourceKind, direction, field?.ms ?? (pose.width >= 1 ? Infinity : 0)),
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
/** Where an emerging menu's material is between its trigger's bar glass (0) and the menu's
|
|
311
|
+
* frosted material (1). A glass button, or a plain glyph's glass bubble, turns into the menu
|
|
312
|
+
* material on native's measured curve (menuMaterialProgress), so the morph starts on the
|
|
313
|
+
* trigger's own glass and ends on the menu's without a swap. A context menu or a text label has
|
|
314
|
+
* no glass to start from: its menu is the frosted material from its first visible frame. */
|
|
315
|
+
function emergeRecipeMix(sourceKind, direction, ms, from = direction === 'open' ? 0 : 1) {
|
|
316
|
+
if (sourceKind === 'context' || sourceKind === 'label')
|
|
317
|
+
return 1;
|
|
318
|
+
const progress = menuMaterialProgress(direction, ms / 1000);
|
|
319
|
+
return direction === 'open' ? from + (1 - from) * progress : from * progress;
|
|
320
|
+
}
|
|
321
|
+
/** The shared native press growth (INTERACTIVE_GLASS_PRESS): a surface's 160pt circle becomes
|
|
322
|
+
* 164pt while held, a button grows 16pt wide. Preserve the material and expand its actual
|
|
323
|
+
* aperture; never cover it with the unfiltered page. `amount` is the press spring (it
|
|
324
|
+
* overshoots on press and undershoots on release). `pose` is the contact's follow and squish
|
|
325
|
+
* (InteractiveContact): it moves the grown shape and changes its width and height, keeping a
|
|
326
|
+
* capsule's ends round.
|
|
327
|
+
*/
|
|
328
|
+
export function pressedRegionSpec(spec, amount, kind = 'surface', memberIndex = 0, pose = GLASS_CONTACT_REST) {
|
|
329
|
+
if (amount === 0 && pose === GLASS_CONTACT_REST)
|
|
330
|
+
return spec;
|
|
331
|
+
const member = spec.shape.members?.[memberIndex] ?? spec.shape.first;
|
|
332
|
+
const scale = interactivePressScale(kind, member.halfWidth * 2, amount);
|
|
333
|
+
const halfWidth = Math.max(1, member.halfWidth * scale + pose.width / 2);
|
|
334
|
+
const halfHeight = Math.max(1, member.halfHeight * scale + pose.height / 2);
|
|
335
|
+
const capsule = member.radius >= Math.min(member.halfWidth, member.halfHeight) - 0.01;
|
|
336
|
+
const grown = {
|
|
337
|
+
...member,
|
|
338
|
+
centerX: member.centerX + pose.x,
|
|
339
|
+
centerY: member.centerY + pose.y,
|
|
340
|
+
halfWidth,
|
|
341
|
+
halfHeight,
|
|
342
|
+
radius: capsule
|
|
343
|
+
? Math.min(halfWidth, halfHeight)
|
|
344
|
+
: Math.min(member.radius * scale, halfWidth, halfHeight),
|
|
345
|
+
};
|
|
346
|
+
const members = spec.shape.members?.map((shape, index) => (index === memberIndex ? grown : shape));
|
|
347
|
+
return {
|
|
348
|
+
...spec,
|
|
349
|
+
shape: {
|
|
350
|
+
...spec.shape,
|
|
351
|
+
first: members?.[0] ?? grown,
|
|
352
|
+
...(members ? { members } : {}),
|
|
353
|
+
},
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
/** A Toggle or Slider thumb lens inside its control's frame: a capsule of the lifted size at the
|
|
357
|
+
* contact's lens centre (frame-local CSS px). At rest it is prepared but transparent; the page's
|
|
358
|
+
* opaque white thumb covers the hand-off while the lens grows. */
|
|
359
|
+
export function controlLensSpec(spec, kind, amount, centerX) {
|
|
360
|
+
const size = controlLensSize(kind, amount);
|
|
361
|
+
const first = {
|
|
362
|
+
...spec.shape.first,
|
|
363
|
+
centerX,
|
|
364
|
+
halfWidth: size.width / 2,
|
|
365
|
+
halfHeight: size.height / 2,
|
|
366
|
+
radius: size.height / 2,
|
|
367
|
+
};
|
|
368
|
+
return {
|
|
369
|
+
...spec,
|
|
370
|
+
// The held lens keeps the clear material's rim (native shows a darker lens edge).
|
|
371
|
+
shape: { ...spec.shape, first, rimAlpha: 1 },
|
|
372
|
+
opacity: amount > 0 ? 1 : 0,
|
|
373
|
+
prewarmTransparent: true,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
/** Implements the public scene/source protocol on the page's existing GPU owner. */
|
|
377
|
+
export class RegionScene {
|
|
378
|
+
samplePixels = 0;
|
|
379
|
+
scene = { revision: 0, layers: [], regions: [], scrolls: [] };
|
|
380
|
+
presenters = new Map();
|
|
381
|
+
sources = new Map();
|
|
382
|
+
emptyForeground;
|
|
383
|
+
drop(id) {
|
|
384
|
+
const source = this.sources.get(id);
|
|
385
|
+
if (!source)
|
|
386
|
+
return;
|
|
387
|
+
source.lease.release();
|
|
388
|
+
this.sources.delete(id);
|
|
389
|
+
}
|
|
390
|
+
/** Install the chrome's shared capture (already retained in the frame graph under
|
|
391
|
+
* `key`) as the root backdrop: one transfer and one upload serve chrome and regions. */
|
|
392
|
+
async shareSource(descriptor, bitmap, key, ordered = false) {
|
|
393
|
+
const previous = this.sources.get(descriptor.id);
|
|
394
|
+
// Standalone source updates reject stale captures. The worker transport can explicitly
|
|
395
|
+
// restore an older resident capture after a pop; its request revision already orders it.
|
|
396
|
+
if (!ordered && previous && previous.descriptor.revision >= descriptor.revision)
|
|
397
|
+
return;
|
|
398
|
+
const lease = FrameGraph.for(this.owner).image(key, bitmap);
|
|
399
|
+
this.drop(descriptor.id);
|
|
400
|
+
this.sources.set(descriptor.id, { descriptor, bitmap: lease.value, lease });
|
|
401
|
+
this.sourcesChanged = true;
|
|
402
|
+
// The settled capture after a navigation replaces its route atlas.
|
|
403
|
+
if (descriptor.id === 'root-backdrop')
|
|
404
|
+
this.navigation = undefined;
|
|
405
|
+
await this.install();
|
|
406
|
+
}
|
|
407
|
+
get sharedRevision() {
|
|
408
|
+
return this.sources.get('root-backdrop')?.descriptor.revision;
|
|
409
|
+
}
|
|
410
|
+
get navigationActive() {
|
|
411
|
+
return !!this.navigation;
|
|
412
|
+
}
|
|
413
|
+
motions = new Map();
|
|
414
|
+
touchLights = new Map();
|
|
415
|
+
/** Scope thumbs in flight (GlassRegion.selection): segment index from → to since `start`. */
|
|
416
|
+
/** The thumbs of segmented regions (the search scope bar), by region id. */
|
|
417
|
+
thumbs = new Map();
|
|
418
|
+
reducedMotion = false;
|
|
419
|
+
/** Emerging regions (glass Menus): direction, start (epoch ms) and the progress it starts from. */
|
|
420
|
+
emerges = new Map();
|
|
421
|
+
fields = new Map();
|
|
422
|
+
appearance;
|
|
423
|
+
/** Presenters whose backdrop only moved (scroll under a fixed region): redrawn once
|
|
424
|
+
* in the next worker frame instead of reinstalled and fenced per scroll event. */
|
|
425
|
+
moved = new Set();
|
|
426
|
+
/** A pressed interactive region is being dragged or held (its motion is live). */
|
|
427
|
+
get pressing() {
|
|
428
|
+
return [...this.motions.values()].some(m => m.motion.needsFrame ||
|
|
429
|
+
m.press.needsFrame ||
|
|
430
|
+
this.highlighting(m, performance.timeOrigin + performance.now()));
|
|
431
|
+
}
|
|
432
|
+
/** The press highlight still changes (held: its first 600 ms; released: its fade). */
|
|
433
|
+
highlighting(m, at) {
|
|
434
|
+
return (m.downAt !== undefined &&
|
|
435
|
+
(m.upAt === undefined
|
|
436
|
+
? at < m.downAt + 620
|
|
437
|
+
: at < m.upAt + INTERACTIVE_HIGHLIGHT_RELEASE_MS + 20));
|
|
438
|
+
}
|
|
439
|
+
/** A backdrop plane scrolled or the plane set changed: redraw only presenters whose
|
|
440
|
+
* composed backdrop differs (scroll-attached glass over its own plane does not). */
|
|
441
|
+
backdropMoved() {
|
|
442
|
+
for (const p of this.presenters.values())
|
|
443
|
+
if (p.raster?.spec && !p.covered && !p.suspended && p.raster.backdropStale())
|
|
444
|
+
this.moved.add(p);
|
|
445
|
+
}
|
|
446
|
+
/** The plane set is about to be replaced by another tab's (a source preparation committed).
|
|
447
|
+
* `regionIds` are the regions of tabs that are not selected. Called BEFORE the new planes are
|
|
448
|
+
* installed, so each of those presenters is drawn one last time over its own tab's backdrop (a
|
|
449
|
+
* press still animating is settled at rest first) and then parked: from here on its canvas keeps
|
|
450
|
+
* those pixels, and nothing can draw another tab's backdrop into it. */
|
|
451
|
+
holdForBackdropChange(regionIds) {
|
|
452
|
+
const held = new Set(regionIds);
|
|
453
|
+
this.scene = {
|
|
454
|
+
...this.scene,
|
|
455
|
+
regions: this.scene.regions.map(region => held.has(region.id) && !region.suspended ? { ...region, suspended: true } : region),
|
|
456
|
+
};
|
|
457
|
+
// A press that is still animating ends at rest (drawn over the region's own backdrop).
|
|
458
|
+
for (const id of held) {
|
|
459
|
+
const state = this.motions.get(id);
|
|
460
|
+
if (!state || !this.motionLive(state))
|
|
461
|
+
continue;
|
|
462
|
+
state.motion.reset(0);
|
|
463
|
+
state.press.reset();
|
|
464
|
+
state.downAt = undefined;
|
|
465
|
+
state.upAt = undefined;
|
|
466
|
+
this.presentMotion(id, state);
|
|
467
|
+
}
|
|
468
|
+
for (const p of this.presenters.values()) {
|
|
469
|
+
if (!held.has(p.descriptor.regionIds[0]) || p.parked)
|
|
470
|
+
continue;
|
|
471
|
+
p.suspended = true;
|
|
472
|
+
this.moved.delete(p);
|
|
473
|
+
// Repainted while nothing could be submitted (an appearance change waiting to be presented):
|
|
474
|
+
// its canvas still shows the old appearance. `present` draws it, then parks it.
|
|
475
|
+
if (p.parkAfterPresent)
|
|
476
|
+
continue;
|
|
477
|
+
// A merge or split still running ends at its target (drawn over the region's own backdrop).
|
|
478
|
+
const field = this.fields.get(p.descriptor.id);
|
|
479
|
+
if (field) {
|
|
480
|
+
this.fields.delete(p.descriptor.id);
|
|
481
|
+
if (p.raster?.spec && !p.covered) {
|
|
482
|
+
p.raster.updateSpec(field.to);
|
|
483
|
+
p.raster.draw(0);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
// Never drawn (it mounted while its tab was hidden): nothing to keep, nothing to sample.
|
|
487
|
+
if (!p.raster || !p.materialKey) {
|
|
488
|
+
this.park(p);
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
if (p.raster.spec && !p.covered && p.raster.backdropStale())
|
|
492
|
+
p.raster.draw(0);
|
|
493
|
+
this.park(p);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
/** The new plane set is installed. Every region that is not in `heldIds` belongs to the selected
|
|
497
|
+
* tab: it resumes now (the next install redraws it over its own backdrop), at the same point of
|
|
498
|
+
* the batch where an unheld region is redrawn. */
|
|
499
|
+
resumeForBackdropChange(heldIds) {
|
|
500
|
+
const held = new Set(heldIds);
|
|
501
|
+
this.scene = {
|
|
502
|
+
...this.scene,
|
|
503
|
+
regions: this.scene.regions.map(region => region.suspended && !held.has(region.id) ? { ...region, suspended: false } : region),
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
/** Install what the last resume left pending: a parked presenter whose region is no longer held.
|
|
507
|
+
* Nothing is pending in a scene that held nothing, so a renderer without regions (or one that has
|
|
508
|
+
* not been given its appearance yet) is never installed from here. */
|
|
509
|
+
async installPending() {
|
|
510
|
+
const pending = [...this.presenters.values()].some(presenter => {
|
|
511
|
+
if (!presenter.parked)
|
|
512
|
+
return false;
|
|
513
|
+
const region = this.scene.regions.find(r => r.id === presenter.descriptor.regionIds[0]);
|
|
514
|
+
return !!region && !region.suspended;
|
|
515
|
+
});
|
|
516
|
+
if (pending)
|
|
517
|
+
await this.install();
|
|
518
|
+
}
|
|
519
|
+
motionLive(state) {
|
|
520
|
+
const at = performance.timeOrigin + performance.now();
|
|
521
|
+
return (state.motion.needsFrame ||
|
|
522
|
+
state.press.needsFrame ||
|
|
523
|
+
this.highlighting(state, at) ||
|
|
524
|
+
!!state.contact?.needsFrame(at));
|
|
525
|
+
}
|
|
526
|
+
park(p) {
|
|
527
|
+
p.raster?.park();
|
|
528
|
+
p.parked = true;
|
|
529
|
+
p.parkAfterPresent = false;
|
|
530
|
+
p.key = undefined;
|
|
531
|
+
p.storageKey = undefined;
|
|
532
|
+
}
|
|
533
|
+
/** Marks parked presenters whose region is not held for a repaint; true when there is one. */
|
|
534
|
+
repaintVisibleParked() {
|
|
535
|
+
let any = false;
|
|
536
|
+
for (const p of this.presenters.values())
|
|
537
|
+
if (p.parked && !p.suspended) {
|
|
538
|
+
p.repaint = true;
|
|
539
|
+
any = true;
|
|
540
|
+
}
|
|
541
|
+
return any;
|
|
542
|
+
}
|
|
543
|
+
keyboardSampledAt = -Infinity;
|
|
544
|
+
get active() {
|
|
545
|
+
return ([...this.presenters.values()].some(p => !p.parked && !p.suspended && p.keyboard && this.keyboardSampledAt < p.keyboard.end) ||
|
|
546
|
+
this.scene.regions.some(region => {
|
|
547
|
+
const track = region.presentationViewportTrack ?? region.accessoryViewportTrack ?? region.accessoryTrack;
|
|
548
|
+
return (track &&
|
|
549
|
+
track.epoch !== Number.MAX_VALUE &&
|
|
550
|
+
performance.timeOrigin + performance.now() < track.epoch + track.duration);
|
|
551
|
+
}) ||
|
|
552
|
+
this.scene.regions.some(region => region.shapeTrack &&
|
|
553
|
+
shapeTrackRunning(region.shapeTrack, performance.timeOrigin + performance.now())) ||
|
|
554
|
+
[...this.touchLights.values()].some(light => light.needsFrame(performance.timeOrigin + performance.now())) ||
|
|
555
|
+
this.fields.size > 0 ||
|
|
556
|
+
[...this.thumbs.values()].some(thumb => thumb.live(performance.timeOrigin + performance.now())) ||
|
|
557
|
+
this.moved.size > 0 ||
|
|
558
|
+
[...this.motions.values()].some(m => this.motionLive(m)) ||
|
|
559
|
+
[...this.emerges.values()].some(e => this.emergeRunning(e, performance.timeOrigin + performance.now()) ||
|
|
560
|
+
(e.direction === 'close' && !e.settled && !e.frozen)));
|
|
561
|
+
}
|
|
562
|
+
emergeRunning(e, at) {
|
|
563
|
+
if (e.held || e.frozen)
|
|
564
|
+
return false;
|
|
565
|
+
// An open runs until its settled frame hands the rows back to the page.
|
|
566
|
+
if (e.shown && e.direction === 'open' && !e.inkNotified)
|
|
567
|
+
return true;
|
|
568
|
+
return e.shown
|
|
569
|
+
? at < e.start + menuMorphDuration(e.direction, e.idiom) + 34
|
|
570
|
+
: at < e.start + 1000;
|
|
571
|
+
}
|
|
572
|
+
emergeSample(state, at) {
|
|
573
|
+
if (state.held)
|
|
574
|
+
return { pose: state.from, velocity: MENU_MORPH_ZERO };
|
|
575
|
+
if (this.reducedMotion) {
|
|
576
|
+
return { pose: menuMorphEndpoint(state.direction), velocity: MENU_MORPH_ZERO };
|
|
577
|
+
}
|
|
578
|
+
return transitionMenuMorph(state.direction, at - state.start, state.from, state.velocity, state.idiom, this.appearance);
|
|
579
|
+
}
|
|
580
|
+
presentEmerge(id, at, present = true) {
|
|
581
|
+
if (this.deferred || !this.visible)
|
|
582
|
+
return;
|
|
583
|
+
const region = this.scene.regions.find(r => r.id === id), state = this.emerges.get(id);
|
|
584
|
+
if (!region || !state)
|
|
585
|
+
return;
|
|
586
|
+
const presenters = [...this.presenters.values()].filter(p => p.descriptor.regionIds[0] === id && p.raster?.spec);
|
|
587
|
+
if (!presenters.length)
|
|
588
|
+
return;
|
|
589
|
+
// The trigger's press highlight continues in the field until the drop leaves (native).
|
|
590
|
+
const trigger = region.emerge?.sourceRegionId
|
|
591
|
+
? this.motions.get(region.emerge.sourceRegionId)
|
|
592
|
+
: undefined;
|
|
593
|
+
const liveHighlight = state.direction === 'open' && trigger?.downAt !== undefined
|
|
594
|
+
? interactiveHighlight('menu', this.appearance, trigger.downAt, trigger.upAt, at)
|
|
595
|
+
: 0;
|
|
596
|
+
// The morph starts at the panel glass's first presented frame, not when its scene arrived: a
|
|
597
|
+
// new presenter's setup (~100 ms under load) otherwise ate the start of the morph.
|
|
598
|
+
if (!state.held && !state.frozen) {
|
|
599
|
+
if (!state.shown) {
|
|
600
|
+
// Menu captured the pressed trigger at activation. Its release spring may already
|
|
601
|
+
// have material by this first visible frame; do not replace that frozen source.
|
|
602
|
+
if (state.direction === 'open' && region.emerge?.from)
|
|
603
|
+
state.source = region.emerge.from;
|
|
604
|
+
state.shown = true;
|
|
605
|
+
// The phone table is timed from the trigger's release (+43 ms); anchoring there keeps
|
|
606
|
+
// capture and pipeline latency out of the motion (it only skips the held first knots).
|
|
607
|
+
const released = trigger?.upAt;
|
|
608
|
+
state.start =
|
|
609
|
+
state.direction === 'open' && region.emerge?.recognitionEpoch !== undefined
|
|
610
|
+
? region.emerge.recognitionEpoch
|
|
611
|
+
: state.direction === 'open' &&
|
|
612
|
+
state.idiom === 'phone' &&
|
|
613
|
+
released !== undefined &&
|
|
614
|
+
at - released < MENU_RELEASE_ANCHOR_WINDOW
|
|
615
|
+
? Math.min(at, released + MENU_PHONE_RELEASE_LEAD)
|
|
616
|
+
: at;
|
|
617
|
+
state.pressHighlight = liveHighlight;
|
|
618
|
+
glassTiming('menu-morph-start', at, state.direction);
|
|
619
|
+
}
|
|
620
|
+
state.progress = this.emergeSample(state, at).pose;
|
|
621
|
+
state.corner = this.reducedMotion
|
|
622
|
+
? { rounding: 0 }
|
|
623
|
+
: sampleMenuCorner(state.direction, at - state.start, state.cornerFrom, state.cornerVelocity).pose;
|
|
624
|
+
}
|
|
625
|
+
const ms = state.shown && !state.held ? at - state.start : 0;
|
|
626
|
+
const duration = menuMorphDuration(state.direction, state.idiom);
|
|
627
|
+
const field = state.frozen && state.field
|
|
628
|
+
? state.field
|
|
629
|
+
: {
|
|
630
|
+
direction: state.direction,
|
|
631
|
+
ms,
|
|
632
|
+
pressHighlight: state.shown ? (state.pressHighlight ?? 0) : liveHighlight,
|
|
633
|
+
settled: this.reducedMotion || (state.direction === 'open' && ms >= duration),
|
|
634
|
+
recipeMix: emergeRecipeMix(region.emerge?.sourceKind, state.direction, this.reducedMotion ? Infinity : ms, state.materialFrom),
|
|
635
|
+
};
|
|
636
|
+
state.field = field;
|
|
637
|
+
let drawn = false;
|
|
638
|
+
for (const p of presenters) {
|
|
639
|
+
const spec = emergeRegionSpec(regionSpec(region, p.descriptor, this.scene), region, p.descriptor, state.progress, state.corner, state.source, field, this.appearance);
|
|
640
|
+
p.raster.updateSpec(this.illuminate(spec, region, p.descriptor, at));
|
|
641
|
+
if (present) {
|
|
642
|
+
p.raster.draw(0);
|
|
643
|
+
drawn ||= p.descriptor.role === 'material';
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
// Ink hand-off with the page's live rows, after a submitted frame: the page shows its rows
|
|
647
|
+
// once the glass draws them settled, and hides them once the glass draws them closing.
|
|
648
|
+
// Either order overlaps identical ink for a frame rather than leaving a blank panel.
|
|
649
|
+
if (!drawn || !state.shown || state.held || state.frozen)
|
|
650
|
+
return;
|
|
651
|
+
if (state.direction === 'close' && !state.inkNotified) {
|
|
652
|
+
state.inkNotified = true;
|
|
653
|
+
this.notify({
|
|
654
|
+
type: 'emerge-ink',
|
|
655
|
+
regionId: id,
|
|
656
|
+
generation: state.generation,
|
|
657
|
+
phase: 'close-started',
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
else if (state.direction === 'open' && field.settled && !state.inkNotified) {
|
|
661
|
+
state.inkNotified = true;
|
|
662
|
+
this.notify({
|
|
663
|
+
type: 'emerge-ink',
|
|
664
|
+
regionId: id,
|
|
665
|
+
generation: state.generation,
|
|
666
|
+
phase: 'open-settled',
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
/** A navigation in progress: presenters over the settled chrome backdrop compose its route atlas
|
|
671
|
+
* (the moving routes, the destination included) in place of the content plane, at the pose of
|
|
672
|
+
* every worker frame, as native glass shows the page moving under it. The next settled backdrop
|
|
673
|
+
* (the capture after the navigation) ends it. */
|
|
674
|
+
navigation;
|
|
675
|
+
async navigationSource(navigation) {
|
|
676
|
+
// The running track's pose: presenters never present the atlas without it (all routes at rest).
|
|
677
|
+
this.navigation = { ...navigation, deferPresent: navigation.deferPresent ?? !!navigation.pose };
|
|
678
|
+
await this.install();
|
|
679
|
+
}
|
|
680
|
+
setNavigation(pose, key, draw = true) {
|
|
681
|
+
if (!this.navigation || (key !== undefined && key !== this.navigation.key))
|
|
682
|
+
return;
|
|
683
|
+
this.navigation.pose = pose;
|
|
684
|
+
for (const p of this.presenters.values())
|
|
685
|
+
if (p.navigation === this.navigation.key && p.raster?.spec && !p.covered) {
|
|
686
|
+
p.raster.setNavigation(this.presenterNavigationPose(pose, p.zoomRoute));
|
|
687
|
+
if (draw && this.visible && !this.deferred)
|
|
688
|
+
p.raster.draw(0);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
presenterNavigationPose(pose, route) {
|
|
692
|
+
if (route === undefined)
|
|
693
|
+
return pose;
|
|
694
|
+
return {
|
|
695
|
+
...pose,
|
|
696
|
+
routes: pose.routes?.map((_, index) => ({ x: index === route ? 0 : 1e9, dim: 0 })),
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
/** Glass changing size inside a still canvas: draw each frame's shape at its presentation time. */
|
|
700
|
+
presentShapeTracks(at) {
|
|
701
|
+
for (const region of this.scene.regions) {
|
|
702
|
+
const track = region.shapeTrack;
|
|
703
|
+
if (!track)
|
|
704
|
+
continue;
|
|
705
|
+
for (const p of this.presenters.values()) {
|
|
706
|
+
if (!p.descriptor.regionIds.includes(region.id) ||
|
|
707
|
+
!p.raster?.spec ||
|
|
708
|
+
p.parked ||
|
|
709
|
+
p.suspended ||
|
|
710
|
+
p.descriptor.role === 'lift')
|
|
711
|
+
continue;
|
|
712
|
+
p.raster.updateSpec(this.shapeAt(region, p.raster.spec, track, at));
|
|
713
|
+
if (this.visible && !this.deferred)
|
|
714
|
+
p.raster.draw(0);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
shapeAt(region, spec, track, at) {
|
|
719
|
+
return trackedShapeSpec(spec, track, at, (width, height) => glassRadius(region.shape, width, height));
|
|
720
|
+
}
|
|
721
|
+
presentTrackedViewport(at) {
|
|
722
|
+
for (const region of this.scene.regions) {
|
|
723
|
+
const track = region.presentationViewportTrack ?? region.accessoryViewportTrack ?? region.accessoryTrack;
|
|
724
|
+
if (!track)
|
|
725
|
+
continue;
|
|
726
|
+
const foreground = this.sources.get(`foreground:${region.id}`);
|
|
727
|
+
for (const p of this.presenters.values()) {
|
|
728
|
+
if (!p.descriptor.regionIds.includes(region.id) ||
|
|
729
|
+
!p.raster?.spec ||
|
|
730
|
+
p.parked ||
|
|
731
|
+
p.descriptor.role === 'lift')
|
|
732
|
+
continue;
|
|
733
|
+
// A pressed accessory draws through its press, which poses the run underneath it
|
|
734
|
+
// (`posedRegionSpec`). Posing here as well would draw the run without the press.
|
|
735
|
+
const interaction = this.motions.get(region.id);
|
|
736
|
+
const pressed = region.accessoryTrack &&
|
|
737
|
+
interaction &&
|
|
738
|
+
(interaction.press.amount !== 0 || this.interactionMoving(interaction, at))
|
|
739
|
+
? interaction
|
|
740
|
+
: undefined;
|
|
741
|
+
if (region.accessoryTrack?.held && foreground) {
|
|
742
|
+
// A held-still canvas keeps its sampling viewport; the glass moves inside it.
|
|
743
|
+
if (pressed)
|
|
744
|
+
this.presentMotion(region.id, pressed, at);
|
|
745
|
+
else {
|
|
746
|
+
p.raster.updateSpec(accessoryPose(p.raster.spec, region.accessoryTrack, at, foreground.descriptor, this.restingFrame(region, p.descriptor)));
|
|
747
|
+
if (this.visible && !this.deferred)
|
|
748
|
+
p.raster.draw(0);
|
|
749
|
+
}
|
|
750
|
+
continue;
|
|
751
|
+
}
|
|
752
|
+
const offset = accessoryOffset(track, at);
|
|
753
|
+
// The DOM viewport observer is paused for this sampling track. Update the
|
|
754
|
+
// sampling viewport itself: an older liveViewport overrides viewportOrigin.
|
|
755
|
+
p.raster.setViewport({
|
|
756
|
+
x: p.descriptor.origin.x + offset.x,
|
|
757
|
+
y: p.descriptor.origin.y + offset.y,
|
|
758
|
+
scaleX: 1,
|
|
759
|
+
scaleY: 1,
|
|
760
|
+
});
|
|
761
|
+
if (p.accessorySourceOrigin)
|
|
762
|
+
p.raster.setSourceOrigin({
|
|
763
|
+
x: p.accessorySourceOrigin.x + offset.x,
|
|
764
|
+
y: p.accessorySourceOrigin.y + offset.y,
|
|
765
|
+
});
|
|
766
|
+
if (pressed) {
|
|
767
|
+
this.presentMotion(region.id, pressed, at);
|
|
768
|
+
continue;
|
|
769
|
+
}
|
|
770
|
+
if (region.accessoryTrack && foreground)
|
|
771
|
+
p.raster.updateSpec(accessoryPose(p.raster.spec, region.accessoryTrack, at, foreground.descriptor));
|
|
772
|
+
if (this.visible && !this.deferred)
|
|
773
|
+
p.raster.draw(0);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
/** Whether a region's press or contact still moves (or lights) its glass at `at`. */
|
|
778
|
+
interactionMoving(state, at) {
|
|
779
|
+
return (state.motion.needsFrame ||
|
|
780
|
+
state.press.needsFrame ||
|
|
781
|
+
this.highlighting(state, at) ||
|
|
782
|
+
!!state.contact?.needsFrame(at));
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* `spec` (a region's scene spec in `presenter`) posed by its shape track and by the bottom
|
|
786
|
+
* accessory's run at `at` when one is live: the run's capsule width and ink and, for a held-still canvas, its travel
|
|
787
|
+
* inside the canvas. Without a run (or before its ink arrives) it is `spec` unchanged.
|
|
788
|
+
*
|
|
789
|
+
* The run and a press both draw the accessory, and a press builds on this pose. A press that
|
|
790
|
+
* rebuilt from the scene spec drew the run's whole envelope, without ink, at the committed
|
|
791
|
+
* row, until the run's next frame: the owner's smudge recording (a finger on the mini player
|
|
792
|
+
* while the bar collapsed) showed that empty capsule running past the Search circle.
|
|
793
|
+
*/
|
|
794
|
+
posedRegionSpec(region, presenter, spec, at) {
|
|
795
|
+
// Glass changing size inside a still canvas (a submenu growing or collapsing) presses
|
|
796
|
+
// the frame it is drawn at, not its whole envelope.
|
|
797
|
+
const shaped = region.shapeTrack ? this.shapeAt(region, spec, region.shapeTrack, at) : spec;
|
|
798
|
+
const track = region.accessoryTrack;
|
|
799
|
+
const foreground = this.sources.get(`foreground:${region.id}`);
|
|
800
|
+
if (!track || !foreground)
|
|
801
|
+
return shaped;
|
|
802
|
+
const resting = track.held ? this.restingFrame(region, presenter) : undefined;
|
|
803
|
+
return accessoryPose(shaped, track, at, foreground.descriptor, resting);
|
|
804
|
+
}
|
|
805
|
+
/** Where a region's glass frame sits in its presenter at the region's laid-out position. */
|
|
806
|
+
restingFrame(region, presenter) {
|
|
807
|
+
const frame = glassViewportFrame(region, this.scene.scrolls);
|
|
808
|
+
return { x: frame.x - presenter.origin.x, y: frame.y - presenter.origin.y };
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* The time the last frame sampled the tracks at. A draw between frames (an install or a
|
|
812
|
+
* scroll update) poses accessory tracks at that time too: the other surfaces of the frame,
|
|
813
|
+
* such as the tab bar on its minimize track, stay where that frame sampled them, and a pose
|
|
814
|
+
* taken at the draw's own time put the moving accessory a frame ahead of the bar.
|
|
815
|
+
*/
|
|
816
|
+
sampledAt;
|
|
817
|
+
trackClock() {
|
|
818
|
+
return this.sampledAt ?? performance.timeOrigin + performance.now();
|
|
819
|
+
}
|
|
820
|
+
sample(at, presentationAt = at) {
|
|
821
|
+
this.sampledAt = presentationAt;
|
|
822
|
+
if (this.sourcePending)
|
|
823
|
+
return;
|
|
824
|
+
// Only compositor-owned viewport trajectories use the predicted presentation
|
|
825
|
+
// time. Input, canvas-owned animation and teardown keep the actual clock.
|
|
826
|
+
// Keyboard motion has no presentation lead: source and lens use the actual
|
|
827
|
+
// carrier clock, including its endpoint hold while the document is stalled.
|
|
828
|
+
this.keyboardSampledAt = at;
|
|
829
|
+
for (const p of this.presenters.values()) {
|
|
830
|
+
if (!p.keyboard || !p.viewport || !p.raster || p.parked || p.suspended)
|
|
831
|
+
continue;
|
|
832
|
+
p.viewport = { ...p.viewport, y: sampleKeyboardTranslation(p.keyboard, at) };
|
|
833
|
+
p.raster.setViewport(p.viewport);
|
|
834
|
+
if (p.raster.backdropStale())
|
|
835
|
+
this.moved.add(p);
|
|
836
|
+
}
|
|
837
|
+
this.presentTrackedViewport(presentationAt);
|
|
838
|
+
this.presentShapeTracks(presentationAt);
|
|
839
|
+
for (const [id, thumb] of this.thumbs)
|
|
840
|
+
if (thumb.live(at)) {
|
|
841
|
+
thumb.sample(at, this.reducedMotion, this.appearance);
|
|
842
|
+
this.presentSelection(id, at);
|
|
843
|
+
}
|
|
844
|
+
for (const [id, motion] of this.fields) {
|
|
845
|
+
const p = this.presenters.get(id);
|
|
846
|
+
// A parked presenter has no setup to move: its field ended when it was held.
|
|
847
|
+
if (!p?.raster || p.parked) {
|
|
848
|
+
this.fields.delete(id);
|
|
849
|
+
continue;
|
|
850
|
+
}
|
|
851
|
+
const elapsed = at - motion.start;
|
|
852
|
+
const members = motion.to.shape.members.map((member, index) => sampleFieldMember(motion.from.shape.members[index] ?? member, member, motion.velocity[index] ?? ZERO_FIELD_VELOCITY, elapsed, this.reducedMotion).value);
|
|
853
|
+
p.raster.updateSpec({
|
|
854
|
+
...motion.to,
|
|
855
|
+
shape: { ...motion.to.shape, first: members[0], members },
|
|
856
|
+
});
|
|
857
|
+
if (this.visible && !this.deferred)
|
|
858
|
+
p.raster.draw(0);
|
|
859
|
+
if (this.reducedMotion || elapsed >= FIELD_MOTION_DURATION)
|
|
860
|
+
this.fields.delete(id);
|
|
861
|
+
}
|
|
862
|
+
if (this.visible && !this.deferred && this.moved.size) {
|
|
863
|
+
// A presenter is marked when its plane scrolls. Scroll-attached glass is marked then too,
|
|
864
|
+
// because the scroll reaches the renderer before the scene that moves the glass with it;
|
|
865
|
+
// once that scene is in, its backdrop is the one it last drew. Drawing it anyway re-encoded
|
|
866
|
+
// and presented every glass canvas of a scrolling page in every frame (10 canvases, 3.5 MP
|
|
867
|
+
// on the glass components page). With the renderer in the document that work is part of
|
|
868
|
+
// the page's own frame, and the page missed frames.
|
|
869
|
+
const installed = Backdrop.for(this.owner).installed;
|
|
870
|
+
for (const p of this.moved) {
|
|
871
|
+
if (installed && !p.raster?.backdropStale())
|
|
872
|
+
continue;
|
|
873
|
+
p.raster?.draw(0);
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
this.moved.clear();
|
|
877
|
+
for (const [id, state] of this.emerges) {
|
|
878
|
+
if (this.emergeRunning(state, at))
|
|
879
|
+
this.presentEmerge(id, at);
|
|
880
|
+
// A close morph at its end (or one whose panel never presented): the page may tear it down.
|
|
881
|
+
if (state.direction === 'close' &&
|
|
882
|
+
!state.frozen &&
|
|
883
|
+
!state.settled &&
|
|
884
|
+
(state.shown
|
|
885
|
+
? at >= state.start + menuMorphDuration('close', state.idiom)
|
|
886
|
+
: !this.emergeRunning(state, at))) {
|
|
887
|
+
state.settled = true;
|
|
888
|
+
this.notify({ type: 'emerge-settled', regionId: id, generation: state.generation });
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
for (const [id, light] of this.touchLights) {
|
|
892
|
+
if (!light.needsFrame(at))
|
|
893
|
+
continue;
|
|
894
|
+
const emerge = this.emerges.get(id);
|
|
895
|
+
if (emerge && this.emergeRunning(emerge, at))
|
|
896
|
+
continue;
|
|
897
|
+
const region = this.scene.regions.find(region => region.id === id);
|
|
898
|
+
if (!region || !this.visible || this.deferred)
|
|
899
|
+
continue;
|
|
900
|
+
for (const presenter of this.presenters.values()) {
|
|
901
|
+
if (!presenter.descriptor.regionIds.includes(id) ||
|
|
902
|
+
!presenter.raster?.spec ||
|
|
903
|
+
presenter.parked)
|
|
904
|
+
continue;
|
|
905
|
+
presenter.raster.updateSpec(this.illuminate(presenter.raster.spec, region, presenter.descriptor, at));
|
|
906
|
+
presenter.raster.draw(0);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
for (const [id, state] of this.motions) {
|
|
910
|
+
if (!this.interactionMoving(state, at))
|
|
911
|
+
continue;
|
|
912
|
+
if (state.motion.needsFrame)
|
|
913
|
+
state.motion.sample(at, this.reducedMotion);
|
|
914
|
+
state.press.sample(at, this.reducedMotion);
|
|
915
|
+
this.presentMotion(id, state, at);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
/** A selection the page changed slides its thumb from wherever it is now (SelectionThumb). */
|
|
919
|
+
retargetSelections(regions) {
|
|
920
|
+
const now = performance.timeOrigin + performance.now();
|
|
921
|
+
for (const region of regions) {
|
|
922
|
+
if (!region.selection) {
|
|
923
|
+
this.thumbs.delete(region.id);
|
|
924
|
+
continue;
|
|
925
|
+
}
|
|
926
|
+
const thumb = this.thumbs.get(region.id);
|
|
927
|
+
if (!thumb) {
|
|
928
|
+
this.thumbs.set(region.id, new SelectionThumb(region.frame, region.selection));
|
|
929
|
+
continue;
|
|
930
|
+
}
|
|
931
|
+
thumb.configure(region.frame, region.selection);
|
|
932
|
+
thumb.select(region.selection.index, now, this.reducedMotion);
|
|
933
|
+
}
|
|
934
|
+
for (const id of this.thumbs.keys())
|
|
935
|
+
if (!regions.some(region => region.id === id))
|
|
936
|
+
this.thumbs.delete(id);
|
|
937
|
+
}
|
|
938
|
+
/** The finger on a segmented region's thumb. */
|
|
939
|
+
selectionInput(region, input) {
|
|
940
|
+
let thumb = this.thumbs.get(region.id);
|
|
941
|
+
if (!thumb) {
|
|
942
|
+
thumb = new SelectionThumb(region.frame, region.selection);
|
|
943
|
+
this.thumbs.set(region.id, thumb);
|
|
944
|
+
}
|
|
945
|
+
thumb.input(input, this.reducedMotion);
|
|
946
|
+
this.presentSelection(region.id, performance.timeOrigin + performance.now());
|
|
947
|
+
}
|
|
948
|
+
/** The bar draws its pill where the thumb is; its lift overlay draws the lens over the bar. */
|
|
949
|
+
presentSelection(id, at) {
|
|
950
|
+
const thumb = this.thumbs.get(id);
|
|
951
|
+
const region = this.scene.regions.find(r => r.id === id);
|
|
952
|
+
if (!thumb || !region?.selection || this.deferred || !this.visible)
|
|
953
|
+
return;
|
|
954
|
+
thumb.sample(at, this.reducedMotion, this.appearance);
|
|
955
|
+
for (const p of this.presenters.values()) {
|
|
956
|
+
if (!p.descriptor.regionIds.includes(id) || p.descriptor.regionIds.length !== 1)
|
|
957
|
+
continue;
|
|
958
|
+
if (!p.raster?.spec || p.parked)
|
|
959
|
+
continue;
|
|
960
|
+
const base = selectionInk(regionSpec(region, p.descriptor, this.scene), this.sources.get(`foreground:${id}`)?.descriptor);
|
|
961
|
+
p.raster.updateSpec(this.posedSelection(base, region, p.descriptor, thumb, at));
|
|
962
|
+
if (p.descriptor.role === 'lift')
|
|
963
|
+
p.raster.setLift(thumb.lift());
|
|
964
|
+
p.raster.draw(0);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
illuminate(spec, region, presenter, at) {
|
|
968
|
+
const controller = this.touchLights.get(region.id);
|
|
969
|
+
if (!controller)
|
|
970
|
+
return spec;
|
|
971
|
+
const light = controller.sample(this.appearance, at);
|
|
972
|
+
const frame = glassViewportFrame(region, this.scene.scrolls);
|
|
973
|
+
return {
|
|
974
|
+
...spec,
|
|
975
|
+
touchLight: light
|
|
976
|
+
? {
|
|
977
|
+
...light,
|
|
978
|
+
x: light.x + frame.x - presenter.origin.x - spec.frameX,
|
|
979
|
+
y: light.y + frame.y - presenter.origin.y - spec.frameY,
|
|
980
|
+
}
|
|
981
|
+
: undefined,
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
/** A segmented region with its thumb where the motion has it, and its bar grown and lit while
|
|
985
|
+
* a finger presses it (the segmented row of GLASS_CONTACT_PHYSICS). */
|
|
986
|
+
posedSelection(spec, region, presenter, thumb, at) {
|
|
987
|
+
return this.pressLight(thumb.pose(spec, region.selection), region, presenter, {
|
|
988
|
+
kind: 'segmented',
|
|
989
|
+
light: thumb.touchLight,
|
|
990
|
+
memberIndex: undefined,
|
|
991
|
+
downAt: thumb.pressedAt,
|
|
992
|
+
upAt: thumb.releasedAt,
|
|
993
|
+
at,
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
presentMotion(id, { motion, press, downAt, upAt, lensCenter, contact, }, at = performance.timeOrigin + performance.now(), present = true) {
|
|
997
|
+
if (this.deferred || !this.visible)
|
|
998
|
+
return;
|
|
999
|
+
const region = this.scene.regions.find(r => r.id === id);
|
|
1000
|
+
if (!region)
|
|
1001
|
+
return;
|
|
1002
|
+
const control = region.material.control;
|
|
1003
|
+
if (control) {
|
|
1004
|
+
// The lens is the lift overlay's moving lens (the tab selection's optics: rim, refraction
|
|
1005
|
+
// bands, exterior shadow); the material presenter stays transparent under it.
|
|
1006
|
+
for (const p of this.presenters.values())
|
|
1007
|
+
if (p.descriptor.role === 'lift' &&
|
|
1008
|
+
p.descriptor.regionIds.includes(id) &&
|
|
1009
|
+
p.raster?.spec &&
|
|
1010
|
+
!p.parked) {
|
|
1011
|
+
const base = regionSpec(region, p.descriptor, this.scene);
|
|
1012
|
+
const lifted = press.amount > 0 || motion.lensMix > 0;
|
|
1013
|
+
const lens = controlLensSpec(base, control, press.amount, lensCenter ?? base.frameWidth / 2);
|
|
1014
|
+
p.raster.updateSpec({ ...lens, opacity: lifted ? 1 : 0 });
|
|
1015
|
+
p.raster.setLift({
|
|
1016
|
+
amount: lifted ? 1 : 0,
|
|
1017
|
+
source: 'transmissive',
|
|
1018
|
+
profile: 'control',
|
|
1019
|
+
glyphVibrancy: [0, 0],
|
|
1020
|
+
glyphLayout: 'content',
|
|
1021
|
+
capsule: lens.shape.first,
|
|
1022
|
+
optics: { ...motion.optics(), foregroundScaleX: 0, foregroundScaleY: 0 },
|
|
1023
|
+
});
|
|
1024
|
+
if (present)
|
|
1025
|
+
p.raster.draw(0);
|
|
1026
|
+
}
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1029
|
+
const kind = glassContactKind(region.material);
|
|
1030
|
+
const pose = contact?.pose(at, this.reducedMotion) ?? GLASS_CONTACT_REST;
|
|
1031
|
+
const light = contact?.light.sample(this.appearance, at);
|
|
1032
|
+
for (const p of this.presenters.values())
|
|
1033
|
+
if (p.descriptor.regionIds.includes(id) && p.raster?.spec && !p.parked) {
|
|
1034
|
+
const members = p.descriptor.regionIds
|
|
1035
|
+
.map(memberId => this.scene.regions.find(candidate => candidate.id === memberId))
|
|
1036
|
+
.filter((member) => member !== undefined);
|
|
1037
|
+
const memberIndex = members.findIndex(member => member.id === id);
|
|
1038
|
+
const base = members.length > 1
|
|
1039
|
+
? fieldRegionSpec(members, p.descriptor, this.scene)
|
|
1040
|
+
: this.posedRegionSpec(region, p.descriptor, regionSpec(region, p.descriptor, this.scene), at);
|
|
1041
|
+
const grown = pressedRegionSpec(base, press.amount, kind, memberIndex, pose);
|
|
1042
|
+
p.raster.updateSpec(this.pressLight(grown, region, p.descriptor, {
|
|
1043
|
+
kind,
|
|
1044
|
+
light,
|
|
1045
|
+
memberIndex: members.length > 1 ? memberIndex : undefined,
|
|
1046
|
+
downAt,
|
|
1047
|
+
upAt,
|
|
1048
|
+
at,
|
|
1049
|
+
}));
|
|
1050
|
+
if (present)
|
|
1051
|
+
p.raster.draw(0);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
/**
|
|
1055
|
+
* The light of a pressed interactive shape: the menu's touch light at the finger
|
|
1056
|
+
* (InteractiveContact). Each kind's light is a row of GLASS_CONTACT_PHYSICS: a button-like
|
|
1057
|
+
* glass whitens across its shape on its measured curve; a surface's light is the menu field
|
|
1058
|
+
* scaled to native, over a held floor across the whole shape. In a field of several members the light stays inside the
|
|
1059
|
+
* pressed member's aperture (one member's light never spills onto its neighbours).
|
|
1060
|
+
*/
|
|
1061
|
+
pressLight(spec, region, presenter, press) {
|
|
1062
|
+
const { kind, light, memberIndex, downAt, upAt, at } = press;
|
|
1063
|
+
const physics = GLASS_CONTACT_PHYSICS[kind].light;
|
|
1064
|
+
const surface = physics.kind === 'finger';
|
|
1065
|
+
if (downAt === undefined || (surface && !light))
|
|
1066
|
+
return { ...spec, touchLight: undefined };
|
|
1067
|
+
// The light's own envelope (rise, release fade) shapes a surface's floor too.
|
|
1068
|
+
const envelope = light && surface ? Math.min(1, light.intensity / physics.menuPeak) : 0;
|
|
1069
|
+
const floor = surface ? physics.floor * envelope : 0;
|
|
1070
|
+
const intensity = surface
|
|
1071
|
+
? (light?.intensity ?? 0) * physics.scale
|
|
1072
|
+
: interactiveHighlight(kind, this.appearance, downAt, upAt, at);
|
|
1073
|
+
// A press without a finger (a button driven only by its press state) whitens uniformly.
|
|
1074
|
+
if (!light)
|
|
1075
|
+
return memberIndex === undefined
|
|
1076
|
+
? {
|
|
1077
|
+
...spec,
|
|
1078
|
+
touchLight: undefined,
|
|
1079
|
+
tint: withHighlight(region.material.tint, Math.max(HIGHLIGHT_FLOOR, intensity)),
|
|
1080
|
+
}
|
|
1081
|
+
: {
|
|
1082
|
+
...spec,
|
|
1083
|
+
touchLight: undefined,
|
|
1084
|
+
memberHighlight: {
|
|
1085
|
+
member: memberIndex,
|
|
1086
|
+
amount: Math.max(0, intensity - HIGHLIGHT_FLOOR) / (1 - HIGHLIGHT_FLOOR),
|
|
1087
|
+
},
|
|
1088
|
+
};
|
|
1089
|
+
const frame = glassViewportFrame(region, this.scene.scrolls);
|
|
1090
|
+
const touchLight = {
|
|
1091
|
+
...light,
|
|
1092
|
+
intensity,
|
|
1093
|
+
x: light.x + frame.x - presenter.origin.x - spec.frameX,
|
|
1094
|
+
y: light.y + frame.y - presenter.origin.y - spec.frameY,
|
|
1095
|
+
};
|
|
1096
|
+
if (memberIndex !== undefined)
|
|
1097
|
+
return {
|
|
1098
|
+
...spec,
|
|
1099
|
+
touchLight: undefined,
|
|
1100
|
+
// The resting floor is already in the shared material. Composite only the remaining
|
|
1101
|
+
// floor and the touch light inside the pressed member's aperture.
|
|
1102
|
+
memberHighlight: {
|
|
1103
|
+
member: memberIndex,
|
|
1104
|
+
amount: Math.max(0, floor - HIGHLIGHT_FLOOR),
|
|
1105
|
+
light: touchLight,
|
|
1106
|
+
},
|
|
1107
|
+
};
|
|
1108
|
+
return {
|
|
1109
|
+
...spec,
|
|
1110
|
+
tint: withHighlight(region.material.tint, Math.max(HIGHLIGHT_FLOOR, floor)),
|
|
1111
|
+
touchLight,
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
setLensDebug(view) {
|
|
1115
|
+
for (const p of this.presenters.values()) {
|
|
1116
|
+
p.raster?.setLensDebug(view);
|
|
1117
|
+
p.raster?.draw(0);
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
deferred = false;
|
|
1121
|
+
visible = true;
|
|
1122
|
+
sourceRevision;
|
|
1123
|
+
sourcePending = false;
|
|
1124
|
+
topologyPending = false;
|
|
1125
|
+
/** A capture cannot draw retained geometry from the previous root/layout. Keep its
|
|
1126
|
+
* last submitted pixels until the matching scene arrives; do not reset any motion. */
|
|
1127
|
+
beginSourceChange(revision) {
|
|
1128
|
+
this.sourceRevision = revision;
|
|
1129
|
+
this.sourcePending = true;
|
|
1130
|
+
}
|
|
1131
|
+
submissionAllowed;
|
|
1132
|
+
owner;
|
|
1133
|
+
/** Worker-to-page events (a close morph that ended, so its panel can be torn down). */
|
|
1134
|
+
notify;
|
|
1135
|
+
constructor(owner, notify = () => { }, submissionAllowed = () => true) {
|
|
1136
|
+
this.submissionAllowed = () => !this.sourcePending && submissionAllowed();
|
|
1137
|
+
this.owner = owner;
|
|
1138
|
+
this.notify = notify;
|
|
1139
|
+
}
|
|
1140
|
+
async request(request) {
|
|
1141
|
+
switch (request.type) {
|
|
1142
|
+
case 'transaction': {
|
|
1143
|
+
this.deferred = true;
|
|
1144
|
+
try {
|
|
1145
|
+
for (const operation of request.requests)
|
|
1146
|
+
await this.request(operation);
|
|
1147
|
+
}
|
|
1148
|
+
finally {
|
|
1149
|
+
this.deferred = false;
|
|
1150
|
+
}
|
|
1151
|
+
await this.install();
|
|
1152
|
+
return this.presentationReceipt();
|
|
1153
|
+
}
|
|
1154
|
+
case 'initialize':
|
|
1155
|
+
if (request.protocol !== GLASS_RENDERER_PROTOCOL)
|
|
1156
|
+
throw Error('Unsupported glass renderer protocol');
|
|
1157
|
+
this.appearance = request.appearance;
|
|
1158
|
+
this.reducedMotion = request.reducedMotion;
|
|
1159
|
+
return;
|
|
1160
|
+
case 'update-presenter': {
|
|
1161
|
+
const p = this.presenters.get(request.presenter.id);
|
|
1162
|
+
if (!p)
|
|
1163
|
+
throw Error('Missing glass presenter');
|
|
1164
|
+
// Rebase retained shapes only when the envelope moves within its container.
|
|
1165
|
+
// Scrolling translates both origins together; rebasing that translation makes
|
|
1166
|
+
// the next scene start a false spring and redraw the entire union every frame.
|
|
1167
|
+
const from = p.descriptor.fieldOrigin;
|
|
1168
|
+
const to = request.presenter.fieldOrigin;
|
|
1169
|
+
// A resizing container (for example a bottom-anchored composer) moves its
|
|
1170
|
+
// origin as part of the layout change. Preserve its previous screen pose
|
|
1171
|
+
// for the morph; only a size-preserving translation is ordinary scrolling.
|
|
1172
|
+
const resized = p.descriptor.width !== request.presenter.width ||
|
|
1173
|
+
p.descriptor.height !== request.presenter.height;
|
|
1174
|
+
const translationX = from && to && !resized ? from.x - to.x : 0;
|
|
1175
|
+
const translationY = from && to && !resized ? from.y - to.y : 0;
|
|
1176
|
+
const offsetX = p.descriptor.origin.x - request.presenter.origin.x - translationX;
|
|
1177
|
+
const offsetY = p.descriptor.origin.y - request.presenter.origin.y - translationY;
|
|
1178
|
+
const previous = p.raster?.spec;
|
|
1179
|
+
if (previous?.shape.members && (offsetX || offsetY)) {
|
|
1180
|
+
const shift = (spec) => {
|
|
1181
|
+
const members = spec.shape.members.map(member => ({
|
|
1182
|
+
...member,
|
|
1183
|
+
centerX: member.centerX + offsetX,
|
|
1184
|
+
centerY: member.centerY + offsetY,
|
|
1185
|
+
}));
|
|
1186
|
+
return { ...spec, shape: { ...spec.shape, first: members[0], members } };
|
|
1187
|
+
};
|
|
1188
|
+
p.raster?.updateSpec(shift(previous));
|
|
1189
|
+
const motion = this.fields.get(request.presenter.id);
|
|
1190
|
+
if (motion) {
|
|
1191
|
+
motion.from = shift(motion.from);
|
|
1192
|
+
motion.to = shift(motion.to);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
p.descriptor = request.presenter;
|
|
1196
|
+
return;
|
|
1197
|
+
}
|
|
1198
|
+
case 'region-shape-track': {
|
|
1199
|
+
this.scene = {
|
|
1200
|
+
...this.scene,
|
|
1201
|
+
regions: this.scene.regions.map(region => region.id === request.regionId ? { ...region, shapeTrack: request.track } : region),
|
|
1202
|
+
};
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1205
|
+
case 'region-presentation-track': {
|
|
1206
|
+
// Late input from a previous modal show cannot move its replacement.
|
|
1207
|
+
this.scene = {
|
|
1208
|
+
...this.scene,
|
|
1209
|
+
regions: this.scene.regions.map(region => region.id === request.regionId &&
|
|
1210
|
+
region.presentationContentEpoch === request.contentEpoch
|
|
1211
|
+
? { ...region, presentationViewportTrack: request.track }
|
|
1212
|
+
: region),
|
|
1213
|
+
};
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
case 'presenter-viewport': {
|
|
1217
|
+
const presenter = this.presenters.get(request.id);
|
|
1218
|
+
// A queued geometry read can outlive its presenter during close/navigation.
|
|
1219
|
+
if (!presenter)
|
|
1220
|
+
return;
|
|
1221
|
+
const viewport = request.viewport;
|
|
1222
|
+
if (!Object.values(viewport).every(Number.isFinite) ||
|
|
1223
|
+
viewport.scaleX <= 0 ||
|
|
1224
|
+
viewport.scaleY <= 0)
|
|
1225
|
+
throw Error('Invalid glass viewport mapping');
|
|
1226
|
+
presenter.viewport = viewport;
|
|
1227
|
+
presenter.keyboard = request.keyboard;
|
|
1228
|
+
if (!presenter.raster || presenter.parked || presenter.suspended)
|
|
1229
|
+
return;
|
|
1230
|
+
// An observation queued before the transition cannot take viewport ownership
|
|
1231
|
+
// back from its sampling track, or publish one frame at the old position.
|
|
1232
|
+
if (this.scene.regions.some(region => (region.presentationViewportTrack ||
|
|
1233
|
+
region.accessoryViewportTrack ||
|
|
1234
|
+
region.accessoryTrack) &&
|
|
1235
|
+
presenter.descriptor.regionIds.includes(region.id)))
|
|
1236
|
+
return;
|
|
1237
|
+
if (request.keyboard) {
|
|
1238
|
+
// Draw from the worker frame after the backdrop has sampled this same clock.
|
|
1239
|
+
this.moved.add(presenter);
|
|
1240
|
+
return;
|
|
1241
|
+
}
|
|
1242
|
+
presenter.raster.setViewport(viewport);
|
|
1243
|
+
if (this.visible && !this.deferred && presenter.raster.backdropStale())
|
|
1244
|
+
presenter.raster.draw(0);
|
|
1245
|
+
return;
|
|
1246
|
+
}
|
|
1247
|
+
case 'attach-presenter': {
|
|
1248
|
+
if (this.presenters.has(request.presenter.id))
|
|
1249
|
+
throw Error('Duplicate glass presenter ' + request.presenter.id);
|
|
1250
|
+
if (request.presenter.width <= 0 ||
|
|
1251
|
+
request.presenter.height <= 0 ||
|
|
1252
|
+
!Number.isFinite(request.presenter.resolution) ||
|
|
1253
|
+
request.presenter.resolution <= 0)
|
|
1254
|
+
throw Error('Invalid glass presenter dimensions');
|
|
1255
|
+
this.presenters.set(request.presenter.id, {
|
|
1256
|
+
descriptor: request.presenter,
|
|
1257
|
+
canvas: request.canvas,
|
|
1258
|
+
});
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
case 'detach-presenter': {
|
|
1262
|
+
const p = this.presenters.get(request.id);
|
|
1263
|
+
p?.raster?.dispose();
|
|
1264
|
+
this.owner.detach(request.id);
|
|
1265
|
+
this.presenters.delete(request.id);
|
|
1266
|
+
this.fields.delete(request.id);
|
|
1267
|
+
return;
|
|
1268
|
+
}
|
|
1269
|
+
case 'scene': {
|
|
1270
|
+
if (this.sourceRevision !== undefined &&
|
|
1271
|
+
request.snapshot.sourceRevision !== this.sourceRevision)
|
|
1272
|
+
return { superseded: true, presented: [] };
|
|
1273
|
+
// Exclusions come from live DOM order; captured dialog planes retire later.
|
|
1274
|
+
// Never expose a departing plane by installing its close scene first. The
|
|
1275
|
+
// source batch and its matching scene release this hold without a timed delay.
|
|
1276
|
+
// Extra live owners may still be preparing their first plane. A reparented
|
|
1277
|
+
// region can change exclusions immediately when the captured order agrees.
|
|
1278
|
+
const owners = request.snapshot.backdropOwners;
|
|
1279
|
+
if (owners) {
|
|
1280
|
+
const captured = [
|
|
1281
|
+
...new Set(Backdrop.for(this.owner).layers.flatMap(layer => (layer.owner ? [layer.owner] : []))),
|
|
1282
|
+
];
|
|
1283
|
+
const order = owners.filter(owner => captured.includes(owner));
|
|
1284
|
+
if (captured.length !== order.length ||
|
|
1285
|
+
captured.some((owner, index) => owner !== order[index])) {
|
|
1286
|
+
this.topologyPending = true;
|
|
1287
|
+
return { superseded: true, presented: [] };
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
this.topologyPending = false;
|
|
1291
|
+
if (request.snapshot.revision < this.scene.revision)
|
|
1292
|
+
throw Error('Stale glass scene revision');
|
|
1293
|
+
const budget = validateGlassRegions(request.snapshot.regions, Math.max(1, ...[...this.presenters.values()].map(p => p.descriptor.resolution)));
|
|
1294
|
+
glassTiming('sample-pixel-budget', glassNow(), String(budget.samplePixels));
|
|
1295
|
+
for (const region of request.snapshot.regions) {
|
|
1296
|
+
if (!request.snapshot.layers.some(l => JSON.stringify(l) === JSON.stringify(region.layer)))
|
|
1297
|
+
throw Error('Undeclared glass region layer ' + region.layer.id);
|
|
1298
|
+
const presentation = region.material.treatment;
|
|
1299
|
+
if (presentation) {
|
|
1300
|
+
const requiredVariant = presentation === 'sheet' ||
|
|
1301
|
+
presentation === 'expandedSheet' ||
|
|
1302
|
+
presentation === 'contextBackdrop' ||
|
|
1303
|
+
presentation === 'contextPreview'
|
|
1304
|
+
? 'clear'
|
|
1305
|
+
: 'regular';
|
|
1306
|
+
if (region.material.tint ||
|
|
1307
|
+
region.material.variant !== requiredVariant ||
|
|
1308
|
+
region.material.interactive)
|
|
1309
|
+
throw Error('Unsupported named presentation material');
|
|
1310
|
+
}
|
|
1311
|
+
if (typeof region.shape === 'object' &&
|
|
1312
|
+
region.shape.cornerStyle === 'continuous' &&
|
|
1313
|
+
!presentation)
|
|
1314
|
+
throw Error('Unsupported continuous region corner profile');
|
|
1315
|
+
}
|
|
1316
|
+
for (const [id, state] of this.motions) {
|
|
1317
|
+
const before = this.scene.regions.find(r => r.id === id), after = request.snapshot.regions.find(r => r.id === id);
|
|
1318
|
+
if (after &&
|
|
1319
|
+
before &&
|
|
1320
|
+
(before.frame.width !== after.frame.width || before.frame.height !== after.frame.height)) {
|
|
1321
|
+
state.motion.configure([after.frame.width / 2], after.frame.width, after.frame.height, after.frame.width);
|
|
1322
|
+
state.motion.reset(0);
|
|
1323
|
+
state.press.reset();
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
this.retargetSelections(request.snapshot.regions);
|
|
1327
|
+
this.sourcePending = false;
|
|
1328
|
+
this.scene = request.snapshot;
|
|
1329
|
+
this.samplePixels = budget.samplePixels;
|
|
1330
|
+
for (const id of this.touchLights.keys())
|
|
1331
|
+
if (!this.scene.regions.some(region => region.id === id && region.material.touchLight))
|
|
1332
|
+
this.touchLights.delete(id);
|
|
1333
|
+
for (const [id] of this.motions)
|
|
1334
|
+
if (!this.scene.regions.some(r => r.id === id))
|
|
1335
|
+
this.motions.delete(id);
|
|
1336
|
+
const now = performance.timeOrigin + performance.now();
|
|
1337
|
+
for (const [id] of this.emerges)
|
|
1338
|
+
if (!this.scene.regions.some(r => r.id === id && r.emerge))
|
|
1339
|
+
this.emerges.delete(id);
|
|
1340
|
+
for (const region of this.scene.regions) {
|
|
1341
|
+
if (!region.emerge)
|
|
1342
|
+
continue;
|
|
1343
|
+
const state = this.emerges.get(region.id), direction = region.emerge.closing ? 'close' : 'open';
|
|
1344
|
+
const generation = region.emerge.generation, held = direction === 'open' && region.emerge.ready === false;
|
|
1345
|
+
if (state && direction === 'close' && region.emerge.fading) {
|
|
1346
|
+
// Freeze the last frame actually presented, including its field/ink clock.
|
|
1347
|
+
state.frozen = true;
|
|
1348
|
+
continue;
|
|
1349
|
+
}
|
|
1350
|
+
if (!state)
|
|
1351
|
+
this.emerges.set(region.id, {
|
|
1352
|
+
direction,
|
|
1353
|
+
start: now,
|
|
1354
|
+
from: direction === 'open' ? { ...MENU_MORPH_ZERO } : menuMorphEndpoint('open'),
|
|
1355
|
+
velocity: { ...MENU_MORPH_ZERO },
|
|
1356
|
+
progress: direction === 'open' ? { ...MENU_MORPH_ZERO } : menuMorphEndpoint('open'),
|
|
1357
|
+
cornerFrom: { rounding: 0 },
|
|
1358
|
+
cornerVelocity: { rounding: 0 },
|
|
1359
|
+
corner: { rounding: 0 },
|
|
1360
|
+
generation,
|
|
1361
|
+
held,
|
|
1362
|
+
idiom: emergeProfile(region.emerge),
|
|
1363
|
+
});
|
|
1364
|
+
else if (state.frozen || state.direction !== direction || state.generation !== generation)
|
|
1365
|
+
this.emerges.set(region.id, {
|
|
1366
|
+
direction,
|
|
1367
|
+
// The phone close table's zero is the tap +45 ms; its scene arrives about 10 ms
|
|
1368
|
+
// sooner (Catalyst, native measurements), so the menu holds that long, as native does.
|
|
1369
|
+
start: direction === 'close' &&
|
|
1370
|
+
region.emerge.idiom === 'phone' &&
|
|
1371
|
+
region.emerge.sourceKind === undefined
|
|
1372
|
+
? now + MENU_PHONE_CLOSE_HOLD
|
|
1373
|
+
: now,
|
|
1374
|
+
from: this.emergeSample(state, now).pose,
|
|
1375
|
+
velocity: this.emergeSample(state, now).velocity,
|
|
1376
|
+
// Continue from the material that was actually presented, using the same measured
|
|
1377
|
+
// curve over the remaining distance. Uninterrupted tracks retain their native fit.
|
|
1378
|
+
materialFrom: state.field?.recipeMix ??
|
|
1379
|
+
emergeRecipeMix(region.emerge.sourceKind, state.direction, state.field?.ms ?? 0, state.materialFrom),
|
|
1380
|
+
cornerFrom: state.corner,
|
|
1381
|
+
cornerVelocity: sampleMenuCorner(state.direction, now - state.start, state.cornerFrom, state.cornerVelocity).velocity,
|
|
1382
|
+
corner: state.corner,
|
|
1383
|
+
source: state.source,
|
|
1384
|
+
progress: state.progress,
|
|
1385
|
+
// A context close starts its clock at its first presented frame. The page hides its
|
|
1386
|
+
// live rows only after that frame (emerge-ink), so the one frame that shows both must
|
|
1387
|
+
// draw the ink exactly where the rows are; timed from the scene's arrival, it had
|
|
1388
|
+
// already moved and the rows showed twice (Music Home tile, 2026-10-14).
|
|
1389
|
+
shown: held || (direction === 'close' && region.emerge.sourceKind === 'context')
|
|
1390
|
+
? false
|
|
1391
|
+
: state.shown,
|
|
1392
|
+
generation,
|
|
1393
|
+
held,
|
|
1394
|
+
idiom: emergeProfile(region.emerge),
|
|
1395
|
+
});
|
|
1396
|
+
else if (state.held !== held) {
|
|
1397
|
+
state.held = held;
|
|
1398
|
+
state.shown = false;
|
|
1399
|
+
state.start = now;
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
for (const [id, source] of this.sources)
|
|
1403
|
+
if (source.descriptor.role === 'foreground' &&
|
|
1404
|
+
!this.scene.regions.some(r => r.id === source.descriptor.regionId)) {
|
|
1405
|
+
source.lease.release();
|
|
1406
|
+
this.sources.delete(id);
|
|
1407
|
+
}
|
|
1408
|
+
await this.install();
|
|
1409
|
+
return this.presentationReceipt();
|
|
1410
|
+
}
|
|
1411
|
+
case 'source': {
|
|
1412
|
+
if (request.source.role !== 'transmitted-backdrop' &&
|
|
1413
|
+
request.source.role !== 'foreground') {
|
|
1414
|
+
request.bitmap.close();
|
|
1415
|
+
throw Error('Unsupported glass source role ' + request.source.role);
|
|
1416
|
+
}
|
|
1417
|
+
if (request.bitmap.width <= 0 ||
|
|
1418
|
+
request.bitmap.height <= 0 ||
|
|
1419
|
+
request.source.width * request.source.resolution !== request.bitmap.width ||
|
|
1420
|
+
request.source.height * request.source.resolution !== request.bitmap.height) {
|
|
1421
|
+
request.bitmap.close();
|
|
1422
|
+
throw Error('Invalid glass source bitmap dimensions');
|
|
1423
|
+
}
|
|
1424
|
+
const previous = this.sources.get(request.source.id);
|
|
1425
|
+
if (previous && previous.descriptor.revision > request.source.revision) {
|
|
1426
|
+
request.bitmap.close();
|
|
1427
|
+
throw Error(`Stale glass source revision: ${request.source.revision} after ${previous.descriptor.revision}`);
|
|
1428
|
+
}
|
|
1429
|
+
const source = request.source, key = source.role === 'foreground'
|
|
1430
|
+
? `${source.id}:${source.revision}`
|
|
1431
|
+
: (source.id === 'root-backdrop'
|
|
1432
|
+
? `production:${source.revision}`
|
|
1433
|
+
: `region:${source.id}:${source.revision}`) + ':front';
|
|
1434
|
+
const lease = FrameGraph.for(this.owner).image(key, request.bitmap);
|
|
1435
|
+
previous?.lease.release();
|
|
1436
|
+
this.sources.set(source.id, { descriptor: source, bitmap: lease.value, lease });
|
|
1437
|
+
this.sourcesChanged = true;
|
|
1438
|
+
// The settled capture after a navigation replaces its route atlas.
|
|
1439
|
+
if (source.id === 'root-backdrop')
|
|
1440
|
+
this.navigation = undefined;
|
|
1441
|
+
await this.install();
|
|
1442
|
+
return this.presentationReceipt();
|
|
1443
|
+
}
|
|
1444
|
+
case 'release-source':
|
|
1445
|
+
this.drop(request.id);
|
|
1446
|
+
for (const p of this.presenters.values())
|
|
1447
|
+
if (p.sourceId === request.id) {
|
|
1448
|
+
p.raster?.clear();
|
|
1449
|
+
p.raster?.setCovered(true);
|
|
1450
|
+
p.covered = true;
|
|
1451
|
+
p.key = undefined;
|
|
1452
|
+
p.storageKey = undefined;
|
|
1453
|
+
}
|
|
1454
|
+
return;
|
|
1455
|
+
case 'interaction': {
|
|
1456
|
+
const input = request.interaction, region = this.scene.regions.find(r => r.id === input.regionId);
|
|
1457
|
+
if (!region)
|
|
1458
|
+
return;
|
|
1459
|
+
if (region.selection) {
|
|
1460
|
+
this.selectionInput(region, input);
|
|
1461
|
+
return;
|
|
1462
|
+
}
|
|
1463
|
+
if (region.material.touchLight) {
|
|
1464
|
+
let light = this.touchLights.get(region.id);
|
|
1465
|
+
if (!light) {
|
|
1466
|
+
light = new MenuTouchLight();
|
|
1467
|
+
this.touchLights.set(region.id, light);
|
|
1468
|
+
}
|
|
1469
|
+
light.input(input);
|
|
1470
|
+
return;
|
|
1471
|
+
}
|
|
1472
|
+
if (!region.material.interactive)
|
|
1473
|
+
return;
|
|
1474
|
+
if (!Number.isFinite(input.epoch))
|
|
1475
|
+
throw Error('Invalid glass interaction epoch');
|
|
1476
|
+
let state = this.motions.get(region.id);
|
|
1477
|
+
if (state && input.epoch < state.epoch)
|
|
1478
|
+
return;
|
|
1479
|
+
if (!state) {
|
|
1480
|
+
const motion = new TabMotion(TAB_INTERACTIVE_MOTION);
|
|
1481
|
+
motion.configure([region.frame.width / 2], region.frame.width, region.frame.height, region.frame.width);
|
|
1482
|
+
motion.reset(0);
|
|
1483
|
+
const profile = region.material.control
|
|
1484
|
+
? controlLiftProfile(region.material.control)
|
|
1485
|
+
: undefined;
|
|
1486
|
+
state = { motion, press: new InteractivePress(profile), epoch: 0 };
|
|
1487
|
+
this.motions.set(region.id, state);
|
|
1488
|
+
}
|
|
1489
|
+
state.epoch = input.epoch;
|
|
1490
|
+
const { motion, press } = state;
|
|
1491
|
+
if (region.material.control) {
|
|
1492
|
+
// The page owns activation and the thumb's position; the lens follows both. The tab
|
|
1493
|
+
// motion runs only for its lens optics: the control's own springs size the lens.
|
|
1494
|
+
if (input.contact)
|
|
1495
|
+
state.lensCenter = input.contact.x;
|
|
1496
|
+
const x = state.lensCenter ?? region.frame.width / 2;
|
|
1497
|
+
if (input.phase === 'rest' || this.reducedMotion) {
|
|
1498
|
+
press.reset();
|
|
1499
|
+
motion.reset(0);
|
|
1500
|
+
}
|
|
1501
|
+
else if (input.phase === 'pressed') {
|
|
1502
|
+
press.down(input.epoch);
|
|
1503
|
+
motion.down(x, 0, input.epoch);
|
|
1504
|
+
}
|
|
1505
|
+
else if (input.phase === 'dragging')
|
|
1506
|
+
motion.move(x);
|
|
1507
|
+
else if (input.phase === 'releasing') {
|
|
1508
|
+
press.up(input.epoch);
|
|
1509
|
+
motion.up(input.epoch);
|
|
1510
|
+
}
|
|
1511
|
+
this.presentMotion(region.id, state);
|
|
1512
|
+
return;
|
|
1513
|
+
}
|
|
1514
|
+
// Each kind of interactive glass presses with its own row of the contact table; a region
|
|
1515
|
+
// whose kind changed (a toolbar item swapped in place) starts a contact of the new kind.
|
|
1516
|
+
const contactKind = glassContactKind(region.material);
|
|
1517
|
+
if (!state.contact || state.contactKind !== contactKind) {
|
|
1518
|
+
state.contact = new InteractiveContact(GLASS_CONTACT_PHYSICS[contactKind]);
|
|
1519
|
+
state.contactKind = contactKind;
|
|
1520
|
+
}
|
|
1521
|
+
// A press without a finger position (chrome buttons driven by their press state) lights
|
|
1522
|
+
// from the shape's centre.
|
|
1523
|
+
state.contact.input(input.phase === 'pressed' && !input.contact
|
|
1524
|
+
? { ...input, contact: { x: region.frame.width / 2, y: region.frame.height / 2 } }
|
|
1525
|
+
: input);
|
|
1526
|
+
if (input.phase === 'rest') {
|
|
1527
|
+
motion.reset(0);
|
|
1528
|
+
press.reset();
|
|
1529
|
+
state.downAt = undefined;
|
|
1530
|
+
state.upAt = undefined;
|
|
1531
|
+
this.presentMotion(region.id, state);
|
|
1532
|
+
}
|
|
1533
|
+
else if (this.reducedMotion) {
|
|
1534
|
+
motion.reset(0);
|
|
1535
|
+
press.reset();
|
|
1536
|
+
if (input.phase === 'pressed') {
|
|
1537
|
+
state.downAt = input.epoch;
|
|
1538
|
+
state.upAt = undefined;
|
|
1539
|
+
}
|
|
1540
|
+
else if (input.phase !== 'dragging')
|
|
1541
|
+
state.upAt = input.epoch;
|
|
1542
|
+
this.presentMotion(region.id, state);
|
|
1543
|
+
}
|
|
1544
|
+
else if (input.phase === 'pressed') {
|
|
1545
|
+
motion.down(region.frame.width / 2, 0, input.epoch);
|
|
1546
|
+
press.down(input.epoch);
|
|
1547
|
+
state.downAt = input.epoch;
|
|
1548
|
+
state.upAt = undefined;
|
|
1549
|
+
}
|
|
1550
|
+
else if (input.phase === 'dragging')
|
|
1551
|
+
motion.move(input.contact?.x ?? region.frame.width / 2);
|
|
1552
|
+
else {
|
|
1553
|
+
motion.up(input.epoch);
|
|
1554
|
+
press.up(input.epoch);
|
|
1555
|
+
state.upAt = input.epoch;
|
|
1556
|
+
}
|
|
1557
|
+
return;
|
|
1558
|
+
}
|
|
1559
|
+
case 'morph':
|
|
1560
|
+
throw Error('Unsupported glass morph: worker morph material pending');
|
|
1561
|
+
case 'appearance':
|
|
1562
|
+
if (this.appearance === request.value && this.reducedMotion === request.reducedMotion)
|
|
1563
|
+
return;
|
|
1564
|
+
this.appearance = request.value;
|
|
1565
|
+
this.reducedMotion = request.reducedMotion;
|
|
1566
|
+
if (this.reducedMotion)
|
|
1567
|
+
for (const [id, state] of this.motions) {
|
|
1568
|
+
state.motion.reset(0);
|
|
1569
|
+
state.press.reset();
|
|
1570
|
+
this.presentMotion(id, state);
|
|
1571
|
+
}
|
|
1572
|
+
await this.install(true);
|
|
1573
|
+
return;
|
|
1574
|
+
case 'visibility':
|
|
1575
|
+
if (request.visible) {
|
|
1576
|
+
await this.prepareResume();
|
|
1577
|
+
this.present();
|
|
1578
|
+
}
|
|
1579
|
+
else {
|
|
1580
|
+
this.visible = false;
|
|
1581
|
+
// The last frame's time is not a time to pose anything at after a resume.
|
|
1582
|
+
this.sampledAt = undefined;
|
|
1583
|
+
this.touchLights.clear();
|
|
1584
|
+
for (const p of this.presenters.values()) {
|
|
1585
|
+
if (p.raster?.spec?.touchLight)
|
|
1586
|
+
p.raster.updateSpec({ ...p.raster.spec, touchLight: undefined });
|
|
1587
|
+
p.raster?.setVisible(false);
|
|
1588
|
+
if (p.descriptor.role === 'lift' && p.raster?.spec)
|
|
1589
|
+
p.raster.setLift({
|
|
1590
|
+
amount: 0,
|
|
1591
|
+
source: 'transmissive',
|
|
1592
|
+
optics: new TabMotion().optics(),
|
|
1593
|
+
});
|
|
1594
|
+
}
|
|
1595
|
+
for (const state of this.motions.values()) {
|
|
1596
|
+
state.motion.reset(0);
|
|
1597
|
+
state.press.reset();
|
|
1598
|
+
state.downAt = undefined;
|
|
1599
|
+
state.upAt = undefined;
|
|
1600
|
+
state.contact = undefined;
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
return;
|
|
1604
|
+
case 'purge':
|
|
1605
|
+
return this.request({ type: 'visibility', visible: false });
|
|
1606
|
+
case 'restore':
|
|
1607
|
+
return this.request({ type: 'visibility', visible: true });
|
|
1608
|
+
case 'diagnostics':
|
|
1609
|
+
return this.diagnostics();
|
|
1610
|
+
case 'dispose':
|
|
1611
|
+
for (const [id, p] of this.presenters) {
|
|
1612
|
+
p.raster?.dispose();
|
|
1613
|
+
this.owner.detach(id);
|
|
1614
|
+
}
|
|
1615
|
+
this.presenters.clear();
|
|
1616
|
+
this.touchLights.clear();
|
|
1617
|
+
for (const source of this.sources.values())
|
|
1618
|
+
source.lease.release();
|
|
1619
|
+
this.sources.clear();
|
|
1620
|
+
this.emptyForeground?.release();
|
|
1621
|
+
this.emptyForeground = undefined;
|
|
1622
|
+
return;
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
/** New source images (uploads) since the last install: only those need a completion fence. */
|
|
1626
|
+
presentationReceipt() {
|
|
1627
|
+
if (this.topologyPending)
|
|
1628
|
+
return { superseded: true, presented: [] };
|
|
1629
|
+
return {
|
|
1630
|
+
navigation: this.navigation?.key,
|
|
1631
|
+
presented: (this.submissionAllowed() ? [...this.presenters.values()] : [])
|
|
1632
|
+
.filter(p => p.descriptor.role === 'material' && p.raster?.spec && !p.covered && this.visible)
|
|
1633
|
+
.flatMap(p => p.descriptor.regionIds.filter(id => {
|
|
1634
|
+
const region = this.scene.regions.find(region => region.id === id);
|
|
1635
|
+
const sheetEpoch = region?.presentationContentEpoch;
|
|
1636
|
+
const accessoryEpoch = region?.accessoryViewportTrack?.epoch;
|
|
1637
|
+
return ((sheetEpoch === undefined || p.presentationContentEpoch === sheetEpoch) &&
|
|
1638
|
+
(accessoryEpoch === undefined || p.accessoryContentEpoch === accessoryEpoch));
|
|
1639
|
+
})),
|
|
1640
|
+
};
|
|
1641
|
+
}
|
|
1642
|
+
sourcesChanged = false;
|
|
1643
|
+
async install(force = false) {
|
|
1644
|
+
if (!this.appearance)
|
|
1645
|
+
throw Error('Glass material requires a presented appearance');
|
|
1646
|
+
if (this.deferred || this.sourcePending)
|
|
1647
|
+
return;
|
|
1648
|
+
let fence = force || this.sourcesChanged;
|
|
1649
|
+
this.sourcesChanged = false;
|
|
1650
|
+
const installed = [];
|
|
1651
|
+
for (const p of this.presenters.values()) {
|
|
1652
|
+
const members = this.scene.regions.filter(r => p.descriptor.regionIds.includes(r.id));
|
|
1653
|
+
const region = this.scene.regions.find(r => r.id === p.descriptor.regionIds[0]);
|
|
1654
|
+
if (!region) {
|
|
1655
|
+
if (!p.covered)
|
|
1656
|
+
p.raster?.clear();
|
|
1657
|
+
p.raster?.setCovered(true);
|
|
1658
|
+
p.covered = true;
|
|
1659
|
+
continue;
|
|
1660
|
+
}
|
|
1661
|
+
if (region.layer.id !== p.descriptor.layerId)
|
|
1662
|
+
throw Error('Glass presenter/region layer mismatch');
|
|
1663
|
+
const resumed = !!p.suspended && !region.suspended;
|
|
1664
|
+
p.suspended = !!region.suspended;
|
|
1665
|
+
// The root transmitted capture is in viewport/content coordinates. A layer
|
|
1666
|
+
// may instead provide its own capture; no frosted source is substituted.
|
|
1667
|
+
const source = [...this.sources.values()].find(s => s.descriptor.role === 'transmitted-backdrop' &&
|
|
1668
|
+
s.descriptor.layerId === region.layer.id) ??
|
|
1669
|
+
[...this.sources.values()].find(s => s.descriptor.role === 'transmitted-backdrop' && s.descriptor.layerId === 'chrome');
|
|
1670
|
+
if (!source)
|
|
1671
|
+
continue;
|
|
1672
|
+
const frame = glassViewportFrame(region, this.scene.scrolls);
|
|
1673
|
+
const inset = contextSampleInset(region.material.treatment);
|
|
1674
|
+
const reach = region.accessoryReach;
|
|
1675
|
+
const held = !!region.accessoryTrack?.held;
|
|
1676
|
+
const bounds = region.emerge
|
|
1677
|
+
? glassPresenterBounds(frame, inset, region.emerge)
|
|
1678
|
+
: glassPresenterBounds(reach ? frameUnion(frame, reach) : frame, inset);
|
|
1679
|
+
if (!region.container)
|
|
1680
|
+
p.descriptor = {
|
|
1681
|
+
...p.descriptor,
|
|
1682
|
+
origin: { x: bounds.x, y: bounds.y },
|
|
1683
|
+
width: bounds.width,
|
|
1684
|
+
height: bounds.height,
|
|
1685
|
+
};
|
|
1686
|
+
const viewportTrack = region.presentationViewportTrack ?? region.accessoryViewportTrack ?? region.accessoryTrack;
|
|
1687
|
+
const accessoryTranslation = viewportTrack && !held ? accessoryOffset(viewportTrack, this.trackClock()) : undefined;
|
|
1688
|
+
const viewport = held
|
|
1689
|
+
? { x: p.descriptor.origin.x, y: p.descriptor.origin.y, scaleX: 1, scaleY: 1 }
|
|
1690
|
+
: accessoryTranslation
|
|
1691
|
+
? {
|
|
1692
|
+
x: p.descriptor.origin.x + accessoryTranslation.x,
|
|
1693
|
+
y: p.descriptor.origin.y + accessoryTranslation.y,
|
|
1694
|
+
scaleX: 1,
|
|
1695
|
+
scaleY: 1,
|
|
1696
|
+
}
|
|
1697
|
+
: p.viewport;
|
|
1698
|
+
// installRetained can draw immediately. Its first sample must use the same
|
|
1699
|
+
// track-owned viewport as later frames, before the ink handoff is acknowledged.
|
|
1700
|
+
if (!p.suspended && viewport)
|
|
1701
|
+
p.raster?.setViewport(viewport);
|
|
1702
|
+
const emerging = this.emerges.get(region.id);
|
|
1703
|
+
let spec = region.container
|
|
1704
|
+
? fieldRegionSpec(members, p.descriptor, this.scene)
|
|
1705
|
+
: emerging
|
|
1706
|
+
? emergeRegionSpec(regionSpec(region, p.descriptor, this.scene), region, p.descriptor, emerging.progress, emerging.corner, emerging.source, emerging.field ?? {
|
|
1707
|
+
direction: emerging.direction,
|
|
1708
|
+
ms: 0,
|
|
1709
|
+
pressHighlight: 0,
|
|
1710
|
+
settled: false,
|
|
1711
|
+
recipeMix: emerging.materialFrom,
|
|
1712
|
+
}, this.appearance)
|
|
1713
|
+
: regionSpec(region, p.descriptor, this.scene);
|
|
1714
|
+
const memberIds = region.container ? members.map(member => member.id) : undefined;
|
|
1715
|
+
if (memberIds && p.raster?.spec?.shape.members) {
|
|
1716
|
+
const previous = p.raster.spec;
|
|
1717
|
+
const running = this.fields.get(p.descriptor.id);
|
|
1718
|
+
const previousIds = running?.memberIds ?? p.fieldMemberIds ?? memberIds;
|
|
1719
|
+
const membershipChanged = previousIds.length !== memberIds.length ||
|
|
1720
|
+
memberIds.some((id, index) => id !== previousIds[index]);
|
|
1721
|
+
if (membershipChanged ||
|
|
1722
|
+
JSON.stringify((running?.to ?? previous).shape.members) !==
|
|
1723
|
+
JSON.stringify(spec.shape.members)) {
|
|
1724
|
+
const start = performance.timeOrigin + performance.now();
|
|
1725
|
+
const previousIndexes = memberIds.map(id => previousIds.indexOf(id));
|
|
1726
|
+
if (region.container?.continuous) {
|
|
1727
|
+
if (running) {
|
|
1728
|
+
const followed = spec.shape.members.map((target, index) => followFieldTarget(running.from.shape.members[previousIndexes[index]] ?? target, running.to.shape.members[previousIndexes[index]] ?? target, target));
|
|
1729
|
+
running.from = {
|
|
1730
|
+
...spec,
|
|
1731
|
+
shape: { ...spec.shape, first: followed[0], members: followed },
|
|
1732
|
+
};
|
|
1733
|
+
running.to = spec;
|
|
1734
|
+
running.velocity = previousIndexes.map(index => running.velocity[index] ?? ZERO_FIELD_VELOCITY);
|
|
1735
|
+
running.memberIds = memberIds;
|
|
1736
|
+
const sampled = spec.shape.members.map((target, index) => sampleFieldMember(followed[index], target, running.velocity[index] ?? ZERO_FIELD_VELOCITY, start - running.start, this.reducedMotion).value);
|
|
1737
|
+
spec = { ...spec, shape: { ...spec.shape, first: sampled[0], members: sampled } };
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
else {
|
|
1741
|
+
const samples = spec.shape.members.map((target, index) => running
|
|
1742
|
+
? sampleFieldMember(running.from.shape.members[previousIndexes[index]] ?? target, running.to.shape.members[previousIndexes[index]] ?? target, running.velocity[previousIndexes[index]] ?? ZERO_FIELD_VELOCITY, start - running.start, this.reducedMotion)
|
|
1743
|
+
: {
|
|
1744
|
+
value: previous.shape.members[previousIndexes[index]] ?? target,
|
|
1745
|
+
velocity: ZERO_FIELD_VELOCITY,
|
|
1746
|
+
});
|
|
1747
|
+
const fromMembers = samples.map(sample => sample.value);
|
|
1748
|
+
const fromSpec = {
|
|
1749
|
+
...spec,
|
|
1750
|
+
shape: { ...spec.shape, first: fromMembers[0], members: fromMembers },
|
|
1751
|
+
};
|
|
1752
|
+
this.fields.set(p.descriptor.id, {
|
|
1753
|
+
start,
|
|
1754
|
+
from: fromSpec,
|
|
1755
|
+
to: spec,
|
|
1756
|
+
velocity: samples.map(sample => sample.velocity),
|
|
1757
|
+
memberIds,
|
|
1758
|
+
});
|
|
1759
|
+
glassTiming('field-morph-start', start, p.descriptor.id);
|
|
1760
|
+
members.forEach((member, index) => {
|
|
1761
|
+
const from = fromMembers[index];
|
|
1762
|
+
const to = spec.shape.members?.[index];
|
|
1763
|
+
if (!from || !to)
|
|
1764
|
+
return;
|
|
1765
|
+
const velocity = samples[index].velocity;
|
|
1766
|
+
this.notify({
|
|
1767
|
+
type: 'field-motion',
|
|
1768
|
+
regionId: member.id,
|
|
1769
|
+
start,
|
|
1770
|
+
duration: this.reducedMotion ? 0 : FIELD_MOTION_DURATION,
|
|
1771
|
+
offsetX: from.centerX - to.centerX,
|
|
1772
|
+
offsetY: from.centerY - to.centerY,
|
|
1773
|
+
scaleX: from.halfWidth / to.halfWidth,
|
|
1774
|
+
scaleY: from.halfHeight / to.halfHeight,
|
|
1775
|
+
velocityX: velocity.centerX,
|
|
1776
|
+
velocityY: velocity.centerY,
|
|
1777
|
+
velocityScaleX: velocity.halfWidth / to.halfWidth,
|
|
1778
|
+
velocityScaleY: velocity.halfHeight / to.halfHeight,
|
|
1779
|
+
});
|
|
1780
|
+
});
|
|
1781
|
+
spec = fromSpec;
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
else if (running)
|
|
1785
|
+
spec = { ...spec, shape: previous.shape };
|
|
1786
|
+
}
|
|
1787
|
+
if (memberIds)
|
|
1788
|
+
p.fieldMemberIds = memberIds;
|
|
1789
|
+
const sourceScroll = source.descriptor.layerId === 'chrome'
|
|
1790
|
+
? this.scene.scrolls.find(s => s.id === 'webgpu-source-root')
|
|
1791
|
+
: undefined;
|
|
1792
|
+
const baseSourceOrigin = {
|
|
1793
|
+
x: p.descriptor.origin.x - source.descriptor.origin.x + (sourceScroll?.offsetX ?? 0),
|
|
1794
|
+
y: p.descriptor.origin.y - source.descriptor.origin.y + (sourceScroll?.offsetY ?? 0),
|
|
1795
|
+
};
|
|
1796
|
+
p.accessorySourceOrigin = viewportTrack && !held ? baseSourceOrigin : undefined;
|
|
1797
|
+
const sourceOrigin = region.presentationViewportTrack && accessoryTranslation
|
|
1798
|
+
? {
|
|
1799
|
+
x: baseSourceOrigin.x + accessoryTranslation.x,
|
|
1800
|
+
y: baseSourceOrigin.y + accessoryTranslation.y,
|
|
1801
|
+
}
|
|
1802
|
+
: baseSourceOrigin;
|
|
1803
|
+
const foreground = this.sources.get(`foreground:${region.id}`);
|
|
1804
|
+
if (region.accessoryTrack && foreground)
|
|
1805
|
+
spec = accessoryPose(spec, region.accessoryTrack, this.trackClock(), foreground.descriptor);
|
|
1806
|
+
if (region.shapeTrack)
|
|
1807
|
+
spec = this.shapeAt(region, spec, region.shapeTrack, this.trackClock());
|
|
1808
|
+
if (region.selection)
|
|
1809
|
+
spec = selectionInk(spec, foreground?.descriptor);
|
|
1810
|
+
const thumb = this.thumbs.get(region.id);
|
|
1811
|
+
if (region.selection && thumb)
|
|
1812
|
+
spec = this.posedSelection(spec, region, p.descriptor, thumb, performance.timeOrigin + performance.now());
|
|
1813
|
+
spec = this.illuminate(spec, region, p.descriptor, performance.timeOrigin + performance.now());
|
|
1814
|
+
const materialKey = JSON.stringify([spec, foreground?.descriptor, this.appearance]);
|
|
1815
|
+
if (p.suspended) {
|
|
1816
|
+
// Held: backdrop changes do not redraw it. Its canvas keeps its pixels until the region's
|
|
1817
|
+
// own inputs change (or its canvas lost them): then it is repainted below exactly as an
|
|
1818
|
+
// unheld region would be, never left showing an old appearance or size.
|
|
1819
|
+
this.moved.delete(p);
|
|
1820
|
+
// Never drawn (it mounted while its tab was hidden, or its only draw failed): the planes
|
|
1821
|
+
// installed now are another tab's. It stays empty and parked; its own tab's commit is its
|
|
1822
|
+
// first sample.
|
|
1823
|
+
if (!p.materialKey) {
|
|
1824
|
+
p.parked = true;
|
|
1825
|
+
continue;
|
|
1826
|
+
}
|
|
1827
|
+
if (p.materialKey === materialKey && !p.repaint)
|
|
1828
|
+
continue;
|
|
1829
|
+
}
|
|
1830
|
+
// The composed screen backdrop is sampled at the presenter's current viewport position.
|
|
1831
|
+
p.raster?.setViewportOrigin(p.descriptor.origin);
|
|
1832
|
+
if (p.descriptor.role === 'lift' &&
|
|
1833
|
+
!foreground &&
|
|
1834
|
+
region.material.press !== 'button' &&
|
|
1835
|
+
region.material.press !== 'bar' &&
|
|
1836
|
+
!region.material.control)
|
|
1837
|
+
continue;
|
|
1838
|
+
// Over the settled chrome backdrop a running navigation's atlas is the source (viewport space).
|
|
1839
|
+
// A held presenter belongs to a tab that is not on screen: the navigation is not under it, and
|
|
1840
|
+
// its repaint must be presented now (the atlas path defers that), before it is parked again.
|
|
1841
|
+
// The DOM removes its zoom registration at settlement before the fresh source
|
|
1842
|
+
// batch arrives. Keep this presenter's accepted route until that batch retires
|
|
1843
|
+
// the atlas; dropping it here briefly samples the previous page's plane.
|
|
1844
|
+
const zoomRoute = region.zoomRoute ?? (this.navigation ? p.zoomRoute : undefined);
|
|
1845
|
+
const navigation = (source.descriptor.layerId === 'chrome' || zoomRoute !== undefined) && !p.suspended
|
|
1846
|
+
? this.navigation
|
|
1847
|
+
: undefined;
|
|
1848
|
+
const key = JSON.stringify([
|
|
1849
|
+
spec,
|
|
1850
|
+
sourceOrigin,
|
|
1851
|
+
source.descriptor,
|
|
1852
|
+
foreground?.descriptor,
|
|
1853
|
+
this.appearance,
|
|
1854
|
+
navigation?.key,
|
|
1855
|
+
zoomRoute,
|
|
1856
|
+
]), storageKey = JSON.stringify([
|
|
1857
|
+
spec,
|
|
1858
|
+
source.descriptor,
|
|
1859
|
+
foreground?.descriptor,
|
|
1860
|
+
this.appearance,
|
|
1861
|
+
navigation?.key,
|
|
1862
|
+
zoomRoute,
|
|
1863
|
+
]);
|
|
1864
|
+
// Releasing a prewarmed pose changes no texture key. Start and submit its
|
|
1865
|
+
// timeline before a scene receipt can release the DOM rows.
|
|
1866
|
+
if (emerging && !emerging.held && !emerging.shown && p.raster?.spec && !navigation)
|
|
1867
|
+
this.presentEmerge(region.id, performance.timeOrigin + performance.now());
|
|
1868
|
+
// A retained scene key is not a submission for a new motion epoch. Install material
|
|
1869
|
+
// once for each sheet entrance or accessory run before acknowledging that run. The
|
|
1870
|
+
// two epochs stay independent when an accessory is nested inside a presentation.
|
|
1871
|
+
const presentationDraw = (region.presentationContentEpoch !== undefined &&
|
|
1872
|
+
p.presentationContentEpoch !== region.presentationContentEpoch) ||
|
|
1873
|
+
(region.accessoryViewportTrack !== undefined &&
|
|
1874
|
+
p.accessoryContentEpoch !== region.accessoryViewportTrack.epoch);
|
|
1875
|
+
if (presentationDraw)
|
|
1876
|
+
fence = true;
|
|
1877
|
+
if (!force &&
|
|
1878
|
+
!presentationDraw &&
|
|
1879
|
+
p.key !== key &&
|
|
1880
|
+
p.storageKey === storageKey &&
|
|
1881
|
+
p.raster?.spec &&
|
|
1882
|
+
!p.covered) {
|
|
1883
|
+
p.raster.setSourceOrigin(navigation ? { x: p.descriptor.origin.x, y: p.descriptor.origin.y } : sourceOrigin);
|
|
1884
|
+
p.key = key;
|
|
1885
|
+
if (p.raster.backdropStale() || !Backdrop.for(this.owner).installed)
|
|
1886
|
+
this.moved.add(p);
|
|
1887
|
+
continue;
|
|
1888
|
+
}
|
|
1889
|
+
if (!force && !presentationDraw && p.key === key) {
|
|
1890
|
+
p.raster?.setCovered(false);
|
|
1891
|
+
if (p.covered)
|
|
1892
|
+
p.raster?.draw(0);
|
|
1893
|
+
// The backdrop may have moved while the region was held (nothing redrew it then).
|
|
1894
|
+
else if (resumed && p.raster?.spec && p.raster.backdropStale())
|
|
1895
|
+
this.moved.add(p);
|
|
1896
|
+
p.covered = false;
|
|
1897
|
+
continue;
|
|
1898
|
+
}
|
|
1899
|
+
if (!p.raster) {
|
|
1900
|
+
p.raster = new WebGPURaster();
|
|
1901
|
+
if (viewport)
|
|
1902
|
+
p.raster.setViewport(viewport);
|
|
1903
|
+
p.raster.setSubmissionGate(this.submissionAllowed);
|
|
1904
|
+
await p.raster.init(p.canvas, this.owner, region.layer, p.descriptor);
|
|
1905
|
+
p.raster.setVisible(this.visible);
|
|
1906
|
+
fence = true;
|
|
1907
|
+
}
|
|
1908
|
+
if (!foreground && !this.emptyForeground)
|
|
1909
|
+
this.emptyForeground = FrameGraph.for(this.owner).image('region:empty', await createImageBitmap(new ImageData(1, 1)));
|
|
1910
|
+
const glyphs = foreground?.lease ?? this.emptyForeground;
|
|
1911
|
+
p.raster.setCovered(false);
|
|
1912
|
+
p.raster.installRetained({
|
|
1913
|
+
viewportOrigin: p.descriptor.origin,
|
|
1914
|
+
spec,
|
|
1915
|
+
liftOverlay: p.descriptor.role === 'lift',
|
|
1916
|
+
lift: p.descriptor.role === 'lift'
|
|
1917
|
+
? { amount: 0, source: 'transmissive', optics: new TabMotion().optics() }
|
|
1918
|
+
: undefined,
|
|
1919
|
+
navigationAtlas: navigation ? navigation.routes : 1,
|
|
1920
|
+
...(navigation
|
|
1921
|
+
? {
|
|
1922
|
+
contentLayer: navigation.contentLayer,
|
|
1923
|
+
navigationCoverage: {
|
|
1924
|
+
width: navigation.width,
|
|
1925
|
+
height: navigation.height,
|
|
1926
|
+
rows: navigation.rows ?? [{ top: 0, bottom: navigation.height }],
|
|
1927
|
+
},
|
|
1928
|
+
}
|
|
1929
|
+
: {}),
|
|
1930
|
+
appearance: this.appearance,
|
|
1931
|
+
sourceWidth: navigation ? navigation.width : source.descriptor.width,
|
|
1932
|
+
sourceOrigin: navigation
|
|
1933
|
+
? { x: p.descriptor.origin.x, y: p.descriptor.origin.y }
|
|
1934
|
+
: sourceOrigin,
|
|
1935
|
+
sourceKey: navigation
|
|
1936
|
+
? navigation.key
|
|
1937
|
+
: source.descriptor.id === 'root-backdrop'
|
|
1938
|
+
? `production:${source.descriptor.revision}`
|
|
1939
|
+
: `region:${source.descriptor.id}:${source.descriptor.revision}`,
|
|
1940
|
+
glyphKey: foreground
|
|
1941
|
+
? `${foreground.descriptor.id}:${foreground.descriptor.revision}`
|
|
1942
|
+
: 'region:empty',
|
|
1943
|
+
width: p.descriptor.width,
|
|
1944
|
+
height: p.descriptor.height,
|
|
1945
|
+
resolution: p.descriptor.resolution,
|
|
1946
|
+
}, navigation ? navigation.lease : source.lease, glyphs, !navigation);
|
|
1947
|
+
p.navigation = navigation?.key;
|
|
1948
|
+
p.zoomRoute = zoomRoute;
|
|
1949
|
+
// A running track draws every presenter in its next worker frame: no extra draw here.
|
|
1950
|
+
if (navigation?.pose)
|
|
1951
|
+
p.raster.setNavigation(this.presenterNavigationPose(navigation.pose, p.zoomRoute));
|
|
1952
|
+
installed.push(p);
|
|
1953
|
+
p.key = key;
|
|
1954
|
+
p.storageKey = storageKey;
|
|
1955
|
+
p.materialKey = materialKey;
|
|
1956
|
+
p.repaint = false;
|
|
1957
|
+
this.moved.delete(p);
|
|
1958
|
+
p.covered = false;
|
|
1959
|
+
p.sourceId = source.descriptor.id;
|
|
1960
|
+
const state = this.motions.get(region.id);
|
|
1961
|
+
if (state)
|
|
1962
|
+
this.presentMotion(region.id, state, performance.timeOrigin + performance.now(), !navigation);
|
|
1963
|
+
if (this.emerges.has(region.id))
|
|
1964
|
+
this.presentEmerge(region.id, performance.timeOrigin + performance.now(), !navigation);
|
|
1965
|
+
if (navigation && !navigation.deferPresent && this.visible)
|
|
1966
|
+
p.raster.draw(0);
|
|
1967
|
+
// A parked presenter repainted while still held goes back to holding pixels only. A held
|
|
1968
|
+
// presenter whose draw could not be submitted (the renderer presents an appearance change
|
|
1969
|
+
// in one step, later) still shows the old appearance: it keeps its setup until `present`
|
|
1970
|
+
// has drawn it, and is parked then.
|
|
1971
|
+
if (region.accessoryTrack &&
|
|
1972
|
+
p.accessoryEpoch !== region.accessoryTrack.epoch &&
|
|
1973
|
+
foreground &&
|
|
1974
|
+
this.visible &&
|
|
1975
|
+
!this.deferred &&
|
|
1976
|
+
this.submissionAllowed()) {
|
|
1977
|
+
// A held-still canvas already samples where it is (see the install viewport above).
|
|
1978
|
+
const offset = region.accessoryTrack.held
|
|
1979
|
+
? { x: 0, y: 0 }
|
|
1980
|
+
: accessoryOffset(region.accessoryTrack, this.trackClock());
|
|
1981
|
+
// The DOM viewport observer is paused for this sampling track. Update the
|
|
1982
|
+
// sampling viewport itself: an older liveViewport overrides viewportOrigin.
|
|
1983
|
+
p.raster.setViewport({
|
|
1984
|
+
x: p.descriptor.origin.x + offset.x,
|
|
1985
|
+
y: p.descriptor.origin.y + offset.y,
|
|
1986
|
+
scaleX: 1,
|
|
1987
|
+
scaleY: 1,
|
|
1988
|
+
});
|
|
1989
|
+
p.raster.setSourceOrigin({ x: sourceOrigin.x + offset.x, y: sourceOrigin.y + offset.y });
|
|
1990
|
+
p.raster.draw(0);
|
|
1991
|
+
p.accessoryEpoch = region.accessoryTrack.epoch;
|
|
1992
|
+
this.notify({
|
|
1993
|
+
type: 'accessory-ink',
|
|
1994
|
+
regionId: region.id,
|
|
1995
|
+
epoch: region.accessoryTrack.epoch,
|
|
1996
|
+
});
|
|
1997
|
+
}
|
|
1998
|
+
const submitted = this.submissionAllowed();
|
|
1999
|
+
if (submitted && this.visible && !navigation?.deferPresent) {
|
|
2000
|
+
p.presentationContentEpoch = region.presentationContentEpoch;
|
|
2001
|
+
p.accessoryContentEpoch = region.accessoryViewportTrack?.epoch;
|
|
2002
|
+
}
|
|
2003
|
+
const holdsPixelsOnly = !!p.suspended && (!!p.parked || !!p.parkAfterPresent);
|
|
2004
|
+
if (holdsPixelsOnly && submitted)
|
|
2005
|
+
this.park(p);
|
|
2006
|
+
else {
|
|
2007
|
+
p.parkAfterPresent = !!p.suspended && (holdsPixelsOnly || !submitted);
|
|
2008
|
+
p.parked = false;
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
// Every presenter uses this one ordered device queue. One completion fence covers a
|
|
2012
|
+
// transaction that uploaded sources or created presenters; a geometry-only update (a
|
|
2013
|
+
// presentation moving or resizing) is not fenced, so it never blocks the worker's queue
|
|
2014
|
+
// behind GPU completion. Its GPU errors still surface through the draw scopes.
|
|
2015
|
+
if (!fence)
|
|
2016
|
+
return;
|
|
2017
|
+
try {
|
|
2018
|
+
await installed.at(-1)?.raster?.settled();
|
|
2019
|
+
}
|
|
2020
|
+
catch (error) {
|
|
2021
|
+
// The draws of this install are not known to have completed: none of it counts as drawn.
|
|
2022
|
+
for (const p of installed) {
|
|
2023
|
+
p.key = undefined;
|
|
2024
|
+
p.storageKey = undefined;
|
|
2025
|
+
p.materialKey = undefined;
|
|
2026
|
+
p.repaint = true;
|
|
2027
|
+
p.presentationContentEpoch = undefined;
|
|
2028
|
+
p.accessoryContentEpoch = undefined;
|
|
2029
|
+
}
|
|
2030
|
+
throw error;
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
async prepareResume() {
|
|
2034
|
+
// Retained viewport and accessory tracks are posed where they are now, before anything is
|
|
2035
|
+
// drawn: the first resumed frame showed the pose sampled before the hide (an accessory
|
|
2036
|
+
// halfway between its rows). Not visible yet, so this only poses.
|
|
2037
|
+
this.sampledAt = undefined;
|
|
2038
|
+
this.presentTrackedViewport(performance.timeOrigin + performance.now());
|
|
2039
|
+
this.visible = true;
|
|
2040
|
+
for (const p of this.presenters.values())
|
|
2041
|
+
p.raster?.setVisible(true, true);
|
|
2042
|
+
// After an app resume or a device restore (the page's fresh appearance reply arrives here for
|
|
2043
|
+
// both): a parked presenter of a region that is on screen has nothing to draw its pixels from,
|
|
2044
|
+
// so it is installed again. A hidden tab's parked presenter stays parked: the only planes
|
|
2045
|
+
// installed are another tab's, and its own tab's commit is its next draw.
|
|
2046
|
+
if (this.repaintVisibleParked())
|
|
2047
|
+
await this.install();
|
|
2048
|
+
}
|
|
2049
|
+
present() {
|
|
2050
|
+
if (!this.visible || this.sourcePending)
|
|
2051
|
+
return;
|
|
2052
|
+
// A parked presenter holds pixels only: it has nothing to draw until it is installed again.
|
|
2053
|
+
for (const p of this.presenters.values()) {
|
|
2054
|
+
if (p.parked)
|
|
2055
|
+
continue;
|
|
2056
|
+
p.raster?.draw(0);
|
|
2057
|
+
// Held, and repainted while nothing could be submitted: drawn now, it holds pixels again.
|
|
2058
|
+
if (p.parkAfterPresent && this.submissionAllowed())
|
|
2059
|
+
this.park(p);
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
navigationRasters() {
|
|
2063
|
+
return [...this.presenters.values()]
|
|
2064
|
+
.filter(p => p.navigation === this.navigation?.key && !!p.navigation && p.raster?.spec && !p.covered)
|
|
2065
|
+
.map(p => p.raster);
|
|
2066
|
+
}
|
|
2067
|
+
async snapshot(id) {
|
|
2068
|
+
const raster = this.presenters.get(id)?.raster;
|
|
2069
|
+
if (!raster)
|
|
2070
|
+
throw Error('Unknown glass presenter snapshot ' + id);
|
|
2071
|
+
return raster.snapshot();
|
|
2072
|
+
}
|
|
2073
|
+
diagnostics() {
|
|
2074
|
+
return {
|
|
2075
|
+
navigation: this.navigation
|
|
2076
|
+
? { key: this.navigation.key, pose: this.navigation.pose }
|
|
2077
|
+
: undefined,
|
|
2078
|
+
drawableCosts: drawableCosts(),
|
|
2079
|
+
samplePixels: this.samplePixels,
|
|
2080
|
+
presenterCosts: [...this.presenters.values()].map(p => ({
|
|
2081
|
+
id: p.descriptor.id,
|
|
2082
|
+
regions: p.descriptor.regionIds,
|
|
2083
|
+
role: p.descriptor.role,
|
|
2084
|
+
zoomRoute: p.zoomRoute,
|
|
2085
|
+
width: p.canvas.width,
|
|
2086
|
+
height: p.canvas.height,
|
|
2087
|
+
stats: p.raster ? { ...p.raster.stats } : null,
|
|
2088
|
+
presentation: p.raster?.presentation() ?? null,
|
|
2089
|
+
})),
|
|
2090
|
+
presenterOrigins: [...this.presenters.values()].map(p => [
|
|
2091
|
+
p.descriptor.regionIds[0],
|
|
2092
|
+
p.descriptor.role,
|
|
2093
|
+
Math.round(p.descriptor.origin.x),
|
|
2094
|
+
Math.round(p.descriptor.origin.y),
|
|
2095
|
+
Math.round(p.descriptor.width),
|
|
2096
|
+
(p.raster?.composedBackdropKey ?? '').slice(0, 160),
|
|
2097
|
+
p.key ? JSON.parse(p.key)[2]?.revision : undefined,
|
|
2098
|
+
p.covered,
|
|
2099
|
+
]),
|
|
2100
|
+
// What each presenter last drew: where its glass frame sits in the canvas, how wide the
|
|
2101
|
+
// capsule is and how many ink layers the renderer drew over it (0: the page owns the ink).
|
|
2102
|
+
regionPoses: [...this.presenters.values()].map(p => {
|
|
2103
|
+
const spec = p.raster?.spec;
|
|
2104
|
+
const id = p.descriptor.regionIds[0];
|
|
2105
|
+
const region = this.scene.regions.find(candidate => candidate.id === id);
|
|
2106
|
+
return {
|
|
2107
|
+
region: id,
|
|
2108
|
+
accessoryTrack: region?.accessoryTrack
|
|
2109
|
+
? { epoch: region.accessoryTrack.epoch, held: !!region.accessoryTrack.held }
|
|
2110
|
+
: null,
|
|
2111
|
+
foreground: this.sources.has(`foreground:${id}`),
|
|
2112
|
+
interaction: this.motions.has(id),
|
|
2113
|
+
role: p.descriptor.role,
|
|
2114
|
+
canvas: { x: p.descriptor.origin.x, y: p.descriptor.origin.y, width: p.descriptor.width },
|
|
2115
|
+
frame: spec ? { x: spec.frameX, y: spec.frameY } : null,
|
|
2116
|
+
capsuleWidth: spec ? spec.shape.first.halfWidth * 2 : null,
|
|
2117
|
+
inkLayers: spec?.ink?.layers.length ?? 0,
|
|
2118
|
+
covered: p.covered,
|
|
2119
|
+
parked: !!p.parked,
|
|
2120
|
+
};
|
|
2121
|
+
}),
|
|
2122
|
+
interactions: [...this.motions].map(([id, s]) => ({
|
|
2123
|
+
id,
|
|
2124
|
+
epoch: s.epoch,
|
|
2125
|
+
active: s.motion.active,
|
|
2126
|
+
pending: s.motion.needsFrame || s.press.needsFrame,
|
|
2127
|
+
amount: s.motion.lensMix,
|
|
2128
|
+
press: s.press.amount,
|
|
2129
|
+
model: 'retained-material',
|
|
2130
|
+
})),
|
|
2131
|
+
backend: 'webgpu',
|
|
2132
|
+
canvasContexts: this.owner.stats?.canvasContexts ?? 0,
|
|
2133
|
+
devices: this.owner.liveDevice ? 1 : 0,
|
|
2134
|
+
draws: [...this.presenters.values()].reduce((n, p) => n + (p.raster?.stats.draws ?? 0), 0),
|
|
2135
|
+
submissions: [...this.presenters.values()].reduce((n, p) => n + (p.raster?.stats.submissions ?? 0), 0),
|
|
2136
|
+
presenters: this.presenters.size,
|
|
2137
|
+
regions: this.scene.regions.length,
|
|
2138
|
+
sources: this.sources.size,
|
|
2139
|
+
retainedSourceBytes: [...this.sources.values()].reduce((n, s) => n + s.bitmap.width * s.bitmap.height * 4, 0),
|
|
2140
|
+
visible: this.visible,
|
|
2141
|
+
scene: this.scene,
|
|
2142
|
+
};
|
|
2143
|
+
}
|
|
2144
|
+
}
|