@satinhq/kit 0.1.0-beta.1 → 0.1.0-beta.3
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/NOTICE +2 -2
- package/README.md +1 -1
- package/dist/acknowledgements/inventory.json +5 -5
- package/dist/actions/RowActions.d.ts +2 -2
- package/dist/actions/RowActions.js +2 -2
- package/dist/app/deployment.js +8 -2
- package/dist/app/installCheck.js +4 -3
- package/dist/app/launchWatchdog.js +6 -1
- package/dist/controls/civilDate.d.ts +1 -1
- package/dist/controls/civilDate.js +1 -1
- package/dist/docs/components/file-importer.md +1 -1
- package/dist/docs/components/paste-button.md +1 -1
- package/dist/docs/components/photos-picker.md +1 -1
- package/dist/docs/components/share-link.md +1 -1
- package/dist/docs/limitations.md +1 -1
- package/dist/docs/push.md +3 -3
- package/dist/docs/reference.md +1870 -0
- package/dist/glass/GlassMaterialRoot.d.ts +3 -1
- package/dist/glass/GlassMaterialRoot.js +2 -2
- package/dist/glass/MaterialEffectRoot.js +5 -1
- package/dist/glass/errors.d.ts +3 -1
- package/dist/glass/errors.js +49 -3
- package/dist/glass/fallback/canvasOwnership.d.ts +7 -0
- package/dist/glass/fallback/canvasOwnership.js +23 -0
- package/dist/glass/fallback/material.js +3 -2
- package/dist/glass/fallback/regions.d.ts +1 -0
- package/dist/glass/fallback/regions.js +9 -12
- package/dist/glass/fallback/runtime.d.ts +2 -0
- package/dist/glass/fallback/runtime.js +15 -4
- package/dist/glass/lens/controlLift.d.ts +1 -1
- package/dist/glass/lens/controlLift.js +1 -1
- package/dist/glass/lens/interactivePress.d.ts +2 -2
- package/dist/glass/lens/interactivePress.js +2 -2
- package/dist/glass/lens/menu-opening-native-fits.json +1 -1
- package/dist/glass/lens/menuContextFit.json +1 -1
- package/dist/glass/lens/menuDropFit.json +1 -1
- package/dist/glass/lens/menuField.d.ts +2 -2
- package/dist/glass/lens/menuField.js +3 -3
- package/dist/glass/lens/menuGlyphDropFit.json +1 -1
- package/dist/glass/lens/menuMorphFit.json +1 -1
- package/dist/glass/lens/menuMorphPhoneFit.json +1 -1
- package/dist/glass/lens/tabAccessoryMotion.d.ts +1 -1
- package/dist/glass/lens/tabSearchMember.d.ts +1 -1
- package/dist/glass/lens/tabSearchMember.js +1 -1
- package/dist/glass/menuPresenter.js +1 -1
- package/dist/glass/regions.d.ts +1 -1
- package/dist/glass/regions.js +1 -1
- package/dist/glass/shapeTrack.d.ts +1 -1
- package/dist/glass/submenuMotion.d.ts +1 -1
- package/dist/glass/submenuMotion.js +1 -1
- package/dist/glass/webgpu/client.d.ts +3 -0
- package/dist/glass/webgpu/client.js +57 -9
- package/dist/glass/webgpu/entry.worker.js +3 -6
- package/dist/glass/webgpu/errorCode.js +3 -2
- package/dist/glass/webgpu/frameClock.d.ts +6 -2
- package/dist/glass/webgpu/frameClock.js +9 -2
- package/dist/glass/webgpu/frameProbe.d.ts +1 -1
- package/dist/glass/webgpu/frameProbe.js +3 -3
- package/dist/glass/webgpu/layers.d.ts +5 -1
- package/dist/glass/webgpu/layers.js +61 -6
- package/dist/glass/webgpu/minimize.js +20 -2
- package/dist/glass/webgpu/production.js +13 -1
- package/dist/glass/webgpu/raster.d.ts +18 -0
- package/dist/glass/webgpu/raster.js +105 -15
- package/dist/glass/webgpu/rasterMaterial.d.ts +4 -0
- package/dist/glass/webgpu/rasterMaterial.js +17 -1
- package/dist/glass/webgpu/regionScene.d.ts +4 -1
- package/dist/glass/webgpu/regionScene.js +30 -1
- package/dist/glass/webgpu/renderer.js +23 -7
- package/dist/glass/webgpu/sourcePixels.d.ts +4 -4
- package/dist/glass/webgpu/sourcePixels.js +48 -8
- package/dist/keyboard/adapter.js +2 -2
- package/dist/keyboard/avoidance.d.ts +1 -1
- package/dist/keyboard/avoidance.js +1 -1
- package/dist/keyboard/motion.d.ts +1 -1
- package/dist/keyboard/motion.js +1 -1
- package/dist/list-form/ListForm.d.ts +2 -2
- package/dist/list-form/ListForm.js +1 -1
- package/dist/list-form/ListItem.js +1 -1
- package/dist/list-form/RowLabel.d.ts +1 -1
- package/dist/list-form/RowLabel.js +1 -1
- package/dist/list-form/Slider.js +1 -1
- package/dist/list-form/editMotion.js +1 -1
- package/dist/list-form/editing.d.ts +5 -5
- package/dist/list-form/editing.js +1 -1
- package/dist/motion/animationValue.d.ts +2 -2
- package/dist/motion/animationValue.js +9 -10
- package/dist/motion/matchedGeometry.d.ts +4 -4
- package/dist/motion/matchedGeometry.js +7 -8
- package/dist/motion/springTrack.d.ts +1 -2
- package/dist/motion/springTrack.js +10 -5
- package/dist/navigation/ScreenContainer.js +1 -1
- package/dist/navigation/chromeCurves.js +1 -1
- package/dist/navigation/types.d.ts +1 -1
- package/dist/navigation/zoomGeometry.js +1 -1
- package/dist/navigation/zoomMotion.d.ts +1 -1
- package/dist/navigation/zoomMotion.js +2 -2
- package/dist/navigation/zoomTransition.js +3 -4
- package/dist/presentation/Presentation.d.ts +2 -2
- package/dist/presentation/Presentation.js +1 -1
- package/dist/presentation/presentationSource.js +1 -1
- package/dist/public/controls.d.ts +4 -4
- package/dist/public/controls.js +7 -7
- package/dist/public/glass.d.ts +3 -4
- package/dist/public/glass.js +1 -2
- package/dist/public/navigationTransition.d.ts +1 -1
- package/dist/public/navigationTransition.js +1 -1
- package/dist/public/scroll.d.ts +1 -1
- package/dist/public/scroll.js +1 -1
- package/dist/public/toolbar.js +1 -1
- package/dist/root/SatinRoot.js +9 -5
- package/dist/safe-area/SafeAreaInset.js +3 -3
- package/dist/safe-area/modifiers.js +2 -2
- package/dist/safe-area/useSafeAreaMetrics.js +2 -2
- package/dist/scroll/ScrollSurface.d.ts +5 -5
- package/dist/scroll/ScrollSurface.js +1 -1
- package/dist/scroll/position.js +1 -1
- package/dist/scroll/screenGeometry.d.ts +1 -1
- package/dist/scroll/screenGeometry.js +1 -1
- package/dist/search/SearchDrawer.js +1 -1
- package/dist/search/searchMotion.d.ts +1 -1
- package/dist/search/searchMotion.js +1 -1
- package/dist/search/useSearchPresentation.js +1 -1
- package/dist/shapes/corners.js +1 -1
- package/dist/styles.css +55 -34
- package/dist/sw/renderServiceWorker.js +12 -3
- package/dist/text-input/TextEditor.js +7 -3
- package/package.json +4 -4
- package/dist/docs/swiftui-reference.md +0 -1895
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { type PropsWithChildren } from 'react';
|
|
2
2
|
export type GlassDisplacementPolicy = 'auto' | 'disabled';
|
|
3
3
|
/** The app paint layer remains untouched. Liquid regions own their retained image. */
|
|
4
|
-
export declare function GlassMaterialRoot({ children, displacement, onLaunchReady, }: PropsWithChildren<{
|
|
4
|
+
export declare function GlassMaterialRoot({ children, displacement, prepared, onLaunchReady, }: PropsWithChildren<{
|
|
5
5
|
displacement: GlassDisplacementPolicy;
|
|
6
|
+
/** Lazy renderer modules wait for the verified offline bundle on first install. */
|
|
7
|
+
prepared?: boolean;
|
|
6
8
|
onLaunchReady?: (ready: boolean) => void;
|
|
7
9
|
}>): import("react").JSX.Element;
|
|
@@ -12,7 +12,7 @@ function readAccessibilityPreferences() {
|
|
|
12
12
|
};
|
|
13
13
|
}
|
|
14
14
|
/** The app paint layer remains untouched. Liquid regions own their retained image. */
|
|
15
|
-
export function GlassMaterialRoot({ children, displacement, onLaunchReady, }) {
|
|
15
|
+
export function GlassMaterialRoot({ children, displacement, prepared = true, onLaunchReady, }) {
|
|
16
16
|
const scene = useRef(null);
|
|
17
17
|
const [chromeLayer, setChromeLayer] = useState(null);
|
|
18
18
|
const renderTest = 'passed';
|
|
@@ -35,5 +35,5 @@ export function GlassMaterialRoot({ children, displacement, onLaunchReady, }) {
|
|
|
35
35
|
const registerRegion = useCallback((_registration) => () => { }, []);
|
|
36
36
|
const diagnostics = useMemo(() => ({ accessibility, capability, renderTest, lensBoost: 1, mapRegenerationCount: 0 }), [accessibility, capability.reason, capability.state, renderTest]);
|
|
37
37
|
const context = useMemo(() => ({ chromeLayer, diagnostics, registerRegion }), [chromeLayer, diagnostics, registerRegion]);
|
|
38
|
-
return (_jsxs(GlassMaterialContext, { value: context, children: [_jsx("div", { ref: scene, "data-satin-glass-capability": capability.state, "data-satin-scene-paint-layer": "true", "data-satin-diagnostic-glass-off": import.meta.env.SATIN_DIAGNOSTIC_GLASS_OFF || undefined, children: _jsx(MaterialEffectRoot, { scene: scene, chrome: chromeLayer, enabled: capability.state === 'displacement', capabilitySettled:
|
|
38
|
+
return (_jsxs(GlassMaterialContext, { value: context, children: [_jsx("div", { ref: scene, "data-satin-glass-capability": capability.state, "data-satin-scene-paint-layer": "true", "data-satin-diagnostic-glass-off": import.meta.env.SATIN_DIAGNOSTIC_GLASS_OFF || undefined, children: _jsx(MaterialEffectRoot, { scene: scene, chrome: chromeLayer, enabled: prepared && capability.state === 'displacement', capabilitySettled: prepared, onLaunchReady: onLaunchReady, children: children }) }), _jsx("div", { ref: setChromeLayer, "data-satin-glass-capability": capability.state, "data-satin-glass-chrome-layer": "true", "data-satin-glass-contrast": accessibility.increasedContrast ? 'more' : 'standard', "data-satin-glass-motion": accessibility.reducedMotion ? 'reduced' : 'full', "data-satin-glass-transparency": accessibility.reducedTransparency ? 'reduced' : 'full' })] }));
|
|
39
39
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { fallbackGlassRequested } from './fallback/selection.js';
|
|
3
|
-
import { glassFallbackRequired, reportGlassError, subscribeGlassFallback } from './errors.js';
|
|
3
|
+
import { glassFallbackRequired, reportGlassError, subscribeGlassFallback, watchGlassTransportRecovery, } from './errors.js';
|
|
4
4
|
import { GlassRegionRegistry } from './regionRegistry.js';
|
|
5
5
|
import { observeTabSources } from '../tabs/sourceRevision.js';
|
|
6
6
|
import { useCallback, useEffect, useMemo, useState, useSyncExternalStore, } from 'react';
|
|
@@ -11,6 +11,10 @@ import { isSupportedTabCount } from './tabBarModel.js';
|
|
|
11
11
|
/** One renderer per app. Registration is semantic; backdrop discovery is framework-owned. */
|
|
12
12
|
export function MaterialEffectRoot({ children, scene, chrome, enabled, capabilitySettled, onLaunchReady, }) {
|
|
13
13
|
const automaticFallback = useSyncExternalStore(subscribeGlassFallback, glassFallbackRequired, () => false);
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (automaticFallback)
|
|
16
|
+
return watchGlassTransportRecovery();
|
|
17
|
+
}, [automaticFallback]);
|
|
14
18
|
const [requestedFallback] = useState(fallbackGlassRequested);
|
|
15
19
|
const backend = requestedFallback || automaticFallback ? 'fallback' : 'webgpu';
|
|
16
20
|
const [regions] = useState(() => new GlassRegionRegistry());
|
package/dist/glass/errors.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export type GlassError = Readonly<{
|
|
|
12
12
|
recovering?: true;
|
|
13
13
|
}>;
|
|
14
14
|
export declare function subscribeGlassFallback(listener: () => void): () => void;
|
|
15
|
-
/**
|
|
15
|
+
/** Retire a device outage only after resource reconstruction and presentation settle. */
|
|
16
16
|
export declare function setGlassAdapterRecovery(pending: boolean): void;
|
|
17
17
|
export declare function glassFallbackRequired(): boolean;
|
|
18
18
|
export declare function reportGlassError(code: GlassErrorCode, cause: unknown, stage: string, reason?: string, recovery?: {
|
|
@@ -20,3 +20,5 @@ export declare function reportGlassError(code: GlassErrorCode, cause: unknown, s
|
|
|
20
20
|
}): GlassError;
|
|
21
21
|
export declare function subscribeGlassErrors(listener: (error: GlassError) => void): () => void;
|
|
22
22
|
export declare function getGlassErrors(): readonly GlassError[];
|
|
23
|
+
/** Retry a failed transport on a visible page; device reconstruction keeps its own owner. */
|
|
24
|
+
export declare function watchGlassTransportRecovery(): () => void;
|
package/dist/glass/errors.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { automaticGlassFallbackAllowed, strictGlassRequested } from './failurePolicy.js';
|
|
2
2
|
import { recordGraphicsEvent } from '../graphicsProbe.js';
|
|
3
3
|
const history = [];
|
|
4
|
-
//
|
|
4
|
+
// Diagnostic history survives recovery; it is not the fallback state.
|
|
5
5
|
let fallbackRequired = false;
|
|
6
6
|
let permanentFallback = false;
|
|
7
7
|
let adapterRecoveryPending = false;
|
|
8
|
+
let transportRecoveryPending = false;
|
|
9
|
+
let nextTransportRetry = 0;
|
|
10
|
+
let transportFailures = 0;
|
|
8
11
|
const fallbackListeners = new Set();
|
|
9
12
|
export function subscribeGlassFallback(listener) {
|
|
10
13
|
fallbackListeners.add(listener);
|
|
@@ -12,7 +15,7 @@ export function subscribeGlassFallback(listener) {
|
|
|
12
15
|
fallbackListeners.delete(listener);
|
|
13
16
|
};
|
|
14
17
|
}
|
|
15
|
-
/**
|
|
18
|
+
/** Retire a device outage only after resource reconstruction and presentation settle. */
|
|
16
19
|
export function setGlassAdapterRecovery(pending) {
|
|
17
20
|
adapterRecoveryPending = pending;
|
|
18
21
|
if (!pending && fallbackRequired && !permanentFallback) {
|
|
@@ -50,7 +53,17 @@ export function reportGlassError(code, cause, stage, reason, recovery) {
|
|
|
50
53
|
history.shift();
|
|
51
54
|
const release = automaticGlassFallbackAllowed();
|
|
52
55
|
if (release && !error.recovering) {
|
|
53
|
-
|
|
56
|
+
const transportFailure = code === 'worker-startup' ||
|
|
57
|
+
(!adapterRecoveryPending &&
|
|
58
|
+
['adapter-unavailable', 'device-request', 'readiness-timeout'].includes(code)) ||
|
|
59
|
+
(code === 'presentation' &&
|
|
60
|
+
/import.*module|module.*script|loading.*chunk|load failed/i.test(message));
|
|
61
|
+
if (transportFailure) {
|
|
62
|
+
transportRecoveryPending = true;
|
|
63
|
+
nextTransportRetry = Date.now() + Math.min(3000 * 2 ** transportFailures++, 30_000);
|
|
64
|
+
}
|
|
65
|
+
else if (!adapterRecoveryPending ||
|
|
66
|
+
['gpu-unavailable', 'unsupported-configuration', 'shader-compilation', 'validation'].includes(code))
|
|
54
67
|
permanentFallback = true;
|
|
55
68
|
fallbackRequired = true;
|
|
56
69
|
for (const listener of fallbackListeners)
|
|
@@ -76,3 +89,36 @@ export function subscribeGlassErrors(listener) {
|
|
|
76
89
|
export function getGlassErrors() {
|
|
77
90
|
return history.slice();
|
|
78
91
|
}
|
|
92
|
+
/** Retry a failed transport on a visible page; device reconstruction keeps its own owner. */
|
|
93
|
+
export function watchGlassTransportRecovery() {
|
|
94
|
+
let timer;
|
|
95
|
+
const retry = () => {
|
|
96
|
+
clearTimeout(timer);
|
|
97
|
+
if (!fallbackRequired ||
|
|
98
|
+
permanentFallback ||
|
|
99
|
+
!transportRecoveryPending ||
|
|
100
|
+
adapterRecoveryPending)
|
|
101
|
+
return;
|
|
102
|
+
if (document.visibilityState !== 'visible')
|
|
103
|
+
return;
|
|
104
|
+
const delay = nextTransportRetry - Date.now();
|
|
105
|
+
if (delay > 0) {
|
|
106
|
+
timer = setTimeout(retry, delay);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
transportRecoveryPending = false;
|
|
110
|
+
fallbackRequired = false;
|
|
111
|
+
for (const listener of fallbackListeners)
|
|
112
|
+
listener();
|
|
113
|
+
};
|
|
114
|
+
document.addEventListener('visibilitychange', retry);
|
|
115
|
+
window.addEventListener('pageshow', retry);
|
|
116
|
+
window.addEventListener('focus', retry);
|
|
117
|
+
retry();
|
|
118
|
+
return () => {
|
|
119
|
+
clearTimeout(timer);
|
|
120
|
+
document.removeEventListener('visibilitychange', retry);
|
|
121
|
+
window.removeEventListener('pageshow', retry);
|
|
122
|
+
window.removeEventListener('focus', retry);
|
|
123
|
+
};
|
|
124
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Parking WebGPU retains its last pixels. CSS ownership must hide those pixels,
|
|
2
|
+
* including lift/selection canvases, without destroying the renderer's reusable canvas. */
|
|
3
|
+
export declare class FallbackCanvasOwnership {
|
|
4
|
+
private visibility;
|
|
5
|
+
update(canvases: Iterable<HTMLCanvasElement>): void;
|
|
6
|
+
dispose(): void;
|
|
7
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Parking WebGPU retains its last pixels. CSS ownership must hide those pixels,
|
|
2
|
+
* including lift/selection canvases, without destroying the renderer's reusable canvas. */
|
|
3
|
+
export class FallbackCanvasOwnership {
|
|
4
|
+
visibility = new Map();
|
|
5
|
+
update(canvases) {
|
|
6
|
+
const live = new Set(canvases);
|
|
7
|
+
for (const [canvas, visibility] of this.visibility) {
|
|
8
|
+
if (live.has(canvas))
|
|
9
|
+
continue;
|
|
10
|
+
canvas.style.visibility = visibility;
|
|
11
|
+
this.visibility.delete(canvas);
|
|
12
|
+
}
|
|
13
|
+
for (const canvas of live) {
|
|
14
|
+
if (this.visibility.has(canvas))
|
|
15
|
+
continue;
|
|
16
|
+
this.visibility.set(canvas, canvas.style.visibility);
|
|
17
|
+
canvas.style.visibility = 'hidden';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
dispose() {
|
|
21
|
+
this.update([]);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { animateElement } from '../../motion/animate.js';
|
|
1
|
+
import { animateElement, timelineStart } from '../../motion/animate.js';
|
|
2
2
|
/** CSS owns backdrop sampling, including video that cannot be read into a GPU texture. */
|
|
3
3
|
export class FallbackMaterial {
|
|
4
4
|
element = document.createElement('div');
|
|
@@ -61,6 +61,7 @@ export class FallbackMaterial {
|
|
|
61
61
|
this.paint(specs[specs.length - 1], appearance);
|
|
62
62
|
const count = Math.max(...specs.map(spec => this.shapes(spec).length));
|
|
63
63
|
this.ensure(count, appearance, specs[specs.length - 1]);
|
|
64
|
+
const start = timelineStart(at);
|
|
64
65
|
this.parts.forEach((part, index) => {
|
|
65
66
|
const frames = specs.map((spec, sample) => {
|
|
66
67
|
const shape = this.shapes(spec)[index];
|
|
@@ -70,7 +71,7 @@ export class FallbackMaterial {
|
|
|
70
71
|
};
|
|
71
72
|
});
|
|
72
73
|
const animation = animateElement(part, frames, { duration, easing: 'linear', fill: 'both' });
|
|
73
|
-
animation.startTime =
|
|
74
|
+
animation.startTime = start;
|
|
74
75
|
this.animations.push(animation);
|
|
75
76
|
});
|
|
76
77
|
return Promise.all(this.animations.map(animation => animation.finished)).then(() => { }, () => { });
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { GlassRegionRegistry } from '../regionRegistry.js';
|
|
2
2
|
/** Registered DOM owners already follow scroll, sheets and navigation on the compositor. */
|
|
3
3
|
export declare class FallbackRegions {
|
|
4
|
+
private canvases;
|
|
4
5
|
private entries;
|
|
5
6
|
private unsubscribe;
|
|
6
7
|
private frame;
|
|
@@ -4,11 +4,13 @@ import { sampleMenuCorner } from '../lens/menuCornerMotion.js';
|
|
|
4
4
|
import { confirmHeldSources, heldPresentations } from '../presentationFrame.js';
|
|
5
5
|
import { animateElement, timelineStart } from '../../motion/animate.js';
|
|
6
6
|
import { FallbackMaterial } from './material.js';
|
|
7
|
+
import { FallbackCanvasOwnership } from './canvasOwnership.js';
|
|
7
8
|
import { GLASS_SHAPE_TRACK_EVENT, glassShapeTrack } from '../shapeTrackRegistry.js';
|
|
8
9
|
import { trackedShapeSpec } from '../shapeTrack.js';
|
|
9
10
|
import { glassRadius } from '../regions.js';
|
|
10
11
|
/** Registered DOM owners already follow scroll, sheets and navigation on the compositor. */
|
|
11
12
|
export class FallbackRegions {
|
|
13
|
+
canvases = new FallbackCanvasOwnership();
|
|
12
14
|
entries = new Map();
|
|
13
15
|
unsubscribe = [];
|
|
14
16
|
frame = 0;
|
|
@@ -43,12 +45,15 @@ export class FallbackRegions {
|
|
|
43
45
|
}
|
|
44
46
|
refresh() {
|
|
45
47
|
const registrations = this.registry?.values() ?? [];
|
|
48
|
+
this.canvases.update([
|
|
49
|
+
...registrations.flatMap(entry => [entry.canvas, ...(entry.overlay ? [entry.overlay] : [])]),
|
|
50
|
+
...[...(this.registry?.containers.values() ?? [])].map(entry => entry.canvas),
|
|
51
|
+
]);
|
|
46
52
|
const live = new Set(registrations.map(entry => entry.id));
|
|
47
53
|
for (const [id, entry] of this.entries) {
|
|
48
54
|
if (!live.has(id)) {
|
|
49
55
|
entry.ink?.cancel();
|
|
50
56
|
entry.material.dispose();
|
|
51
|
-
entry.restoreCanvas();
|
|
52
57
|
this.entries.delete(id);
|
|
53
58
|
}
|
|
54
59
|
}
|
|
@@ -90,18 +95,10 @@ export class FallbackRegions {
|
|
|
90
95
|
const signature = JSON.stringify([region, this.appearance]);
|
|
91
96
|
let entry = this.entries.get(id);
|
|
92
97
|
if (!entry) {
|
|
93
|
-
// A live demotion can leave the last GPU frame on this canvas. Retain the
|
|
94
|
-
// canvas for presentation receipts, but let only the CSS material paint.
|
|
95
|
-
const canvas = registration.canvas;
|
|
96
|
-
const visibility = canvas.style.visibility;
|
|
97
|
-
canvas.style.visibility = 'hidden';
|
|
98
98
|
entry = {
|
|
99
99
|
material: new FallbackMaterial(element),
|
|
100
100
|
signature: '',
|
|
101
101
|
generation: 0,
|
|
102
|
-
restoreCanvas: () => {
|
|
103
|
-
canvas.style.visibility = visibility;
|
|
104
|
-
},
|
|
105
102
|
};
|
|
106
103
|
this.entries.set(id, entry);
|
|
107
104
|
}
|
|
@@ -129,7 +126,7 @@ export class FallbackRegions {
|
|
|
129
126
|
if (track.epoch === Number.MAX_VALUE || frames.length === 1)
|
|
130
127
|
entry.material.paint(frames[0], this.appearance);
|
|
131
128
|
else
|
|
132
|
-
void entry.material.play(frames, track.duration,
|
|
129
|
+
void entry.material.play(frames, track.duration, track.epoch - performance.timeOrigin, this.appearance);
|
|
133
130
|
continue;
|
|
134
131
|
}
|
|
135
132
|
if (!emerge || emerge.ready === false) {
|
|
@@ -204,7 +201,7 @@ export class FallbackRegions {
|
|
|
204
201
|
};
|
|
205
202
|
});
|
|
206
203
|
entry.ink = animateElement(ink, frames, { duration, easing: 'linear', fill: 'both' });
|
|
207
|
-
entry.ink.startTime = at;
|
|
204
|
+
entry.ink.startTime = timelineStart(at);
|
|
208
205
|
}
|
|
209
206
|
if (emerge.closing)
|
|
210
207
|
this.registry?.emergeInk({
|
|
@@ -242,8 +239,8 @@ export class FallbackRegions {
|
|
|
242
239
|
this.entries.forEach(entry => {
|
|
243
240
|
entry.ink?.cancel();
|
|
244
241
|
entry.material.dispose();
|
|
245
|
-
entry.restoreCanvas();
|
|
246
242
|
});
|
|
247
243
|
this.entries.clear();
|
|
244
|
+
this.canvases.dispose();
|
|
248
245
|
}
|
|
249
246
|
}
|
|
@@ -3,6 +3,7 @@ import type { RestorePath } from '../lens/tabRestoreMotion.js';
|
|
|
3
3
|
import type { ScreenId } from '../lens/Scene.js';
|
|
4
4
|
/** Material replacement only: semantic controls and measured motion keep their existing owners. */
|
|
5
5
|
export declare class FallbackGlassRuntime {
|
|
6
|
+
private canvases;
|
|
6
7
|
private regions;
|
|
7
8
|
private dock;
|
|
8
9
|
private thumb;
|
|
@@ -25,6 +26,7 @@ export declare class FallbackGlassRuntime {
|
|
|
25
26
|
private scroll;
|
|
26
27
|
private dockSpec;
|
|
27
28
|
private minimizeTrack;
|
|
29
|
+
private playMinimizeTrack;
|
|
28
30
|
restoreMinimizingTabBar(path?: RestorePath): boolean;
|
|
29
31
|
private thumbSpec;
|
|
30
32
|
private paintTabs;
|
|
@@ -6,8 +6,10 @@ import { tabHeldGeometry } from '../webgpu/interactionProfiles.js';
|
|
|
6
6
|
import { WorkerMinimize } from '../webgpu/minimize.js';
|
|
7
7
|
import { FallbackMaterial } from './material.js';
|
|
8
8
|
import { FallbackRegions } from './regions.js';
|
|
9
|
+
import { FallbackCanvasOwnership } from './canvasOwnership.js';
|
|
9
10
|
/** Material replacement only: semantic controls and measured motion keep their existing owners. */
|
|
10
11
|
export class FallbackGlassRuntime {
|
|
12
|
+
canvases = new FallbackCanvasOwnership();
|
|
11
13
|
regions;
|
|
12
14
|
dock;
|
|
13
15
|
thumb;
|
|
@@ -25,18 +27,23 @@ export class FallbackGlassRuntime {
|
|
|
25
27
|
Object.assign(globalThis, {
|
|
26
28
|
__SATIN_GLASS_RENDERER__: { backend: 'fallback', errors: getGlassErrors },
|
|
27
29
|
});
|
|
30
|
+
const band = options.elements.band;
|
|
31
|
+
const shell = band.canvas.closest('[data-satin-liquid]');
|
|
32
|
+
this.canvases.update([
|
|
33
|
+
band.canvas,
|
|
34
|
+
band.rimCanvas,
|
|
35
|
+
...(shell?.querySelectorAll('canvas[data-satin-webgpu],canvas[data-satin-glass-lift]') ?? []),
|
|
36
|
+
]);
|
|
28
37
|
this.regions = new FallbackRegions(options.regions, options.appearance);
|
|
29
38
|
this.dock = new FallbackMaterial(options.elements.chrome.dock);
|
|
30
39
|
this.thumb = new FallbackMaterial(options.elements.chrome.dock);
|
|
31
40
|
this.dock.element.style.zIndex = '-2';
|
|
32
41
|
this.thumb.element.style.zIndex = '-1';
|
|
33
42
|
this.minimizeDom = new WorkerMinimize(options, path => this.restoreMinimizingTabBar(path), {
|
|
34
|
-
begin: x =>
|
|
35
|
-
this.tabPointerDown(x, this.options.geometry.tabBarSelectedIndex ?? 0);
|
|
36
|
-
},
|
|
43
|
+
begin: x => this.tabPointerDown(x, this.options.geometry.tabBarSelectedIndex ?? 0),
|
|
37
44
|
move: x => this.tabPointerMove(x),
|
|
38
45
|
end: cancelled => this.tabPointerUp(cancelled),
|
|
39
|
-
});
|
|
46
|
+
}, track => this.playMinimizeTrack(track));
|
|
40
47
|
this.bindScroll();
|
|
41
48
|
this.unsubscribeAppearance = options.subscribeAppearance?.(() => this.commitAppearance(options.currentAppearance?.() ?? options.appearance));
|
|
42
49
|
this.updateGeometry({});
|
|
@@ -90,6 +97,9 @@ export class FallbackGlassRuntime {
|
|
|
90
97
|
}
|
|
91
98
|
minimizeTrack(direction, path = 'tap') {
|
|
92
99
|
const track = this.minimizeDom.start(direction, this.dockSpec(), path);
|
|
100
|
+
this.playMinimizeTrack(track);
|
|
101
|
+
}
|
|
102
|
+
playMinimizeTrack(track) {
|
|
93
103
|
void this.dock.play(track.specs, track.duration, track.at, this.options.appearance);
|
|
94
104
|
void this.thumb.play(track.specs.map(spec => this.thumbSpec(spec)), track.duration, track.at, this.options.appearance);
|
|
95
105
|
}
|
|
@@ -272,6 +282,7 @@ export class FallbackGlassRuntime {
|
|
|
272
282
|
this.unsubscribeAppearance?.();
|
|
273
283
|
this.minimizeDom.dispose();
|
|
274
284
|
this.regions.dispose();
|
|
285
|
+
this.canvases.dispose();
|
|
275
286
|
this.dock.dispose();
|
|
276
287
|
this.thumb.dispose();
|
|
277
288
|
}
|
|
@@ -2,7 +2,7 @@ import type { InteractivePressProfile } from './interactivePress.js';
|
|
|
2
2
|
import { type TabHeldGeometry } from './tabMotion.js';
|
|
3
3
|
export { SCOPE_THUMB } from './scopeSegments.js';
|
|
4
4
|
/**
|
|
5
|
-
* The held glass lens of a Toggle or Slider thumb. Measured 2026-10-09 on
|
|
5
|
+
* The held glass lens of a Toggle or Slider thumb. Measured 2026-10-09 on native controls
|
|
6
6
|
* (iPhone 17 Pro Max Simulator, iOS 27.0, dark; counter-retimed idb contacts; `pooled-response-models.json`): a stationary hold keeps the 37 × 24 pt
|
|
7
7
|
* white thumb for about 333 ms, then the thumb becomes a clear lens that springs to its held size.
|
|
8
8
|
* Release restores the size with a slower spring. Mass is 1; width and height share one response.
|
|
@@ -2,7 +2,7 @@ import { TAB_INTERACTIVE_MOTION } from './tabMotion.js';
|
|
|
2
2
|
import { SCOPE_THUMB } from './scopeSegments.js';
|
|
3
3
|
export { SCOPE_THUMB } from './scopeSegments.js';
|
|
4
4
|
/**
|
|
5
|
-
* The held glass lens of a Toggle or Slider thumb. Measured 2026-10-09 on
|
|
5
|
+
* The held glass lens of a Toggle or Slider thumb. Measured 2026-10-09 on native controls
|
|
6
6
|
* (iPhone 17 Pro Max Simulator, iOS 27.0, dark; counter-retimed idb contacts; `pooled-response-models.json`): a stationary hold keeps the 37 × 24 pt
|
|
7
7
|
* white thumb for about 333 ms, then the thumb becomes a clear lens that springs to its held size.
|
|
8
8
|
* Release restores the size with a slower spring. Mass is 1; width and height share one response.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type GlassContactKind } from '../contactPhysics.js';
|
|
2
2
|
/**
|
|
3
|
-
* The one press response of interactive
|
|
4
|
-
* back button, Glass.interactive() surfaces). Fitted 2026-09-25 to
|
|
3
|
+
* The one press response of interactive glass (buttons, bar and toolbar buttons, the
|
|
4
|
+
* back button, Glass.interactive() surfaces). Fitted 2026-09-25 to native controls on the iOS
|
|
5
5
|
* 27.0 Simulator (ParityReference `--glass-press`, idb presses at human speed: 0.6 s holds and
|
|
6
6
|
* 0.12 s taps, light and dark; glass edges measured on the centre row and column):
|
|
7
7
|
*
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { MENU_MORPH_DURATION, sampleMenuMorph } from './menuMorphMotion.js';
|
|
2
2
|
import { GLASS_CONTACT_PHYSICS } from '../contactPhysics.js';
|
|
3
3
|
/**
|
|
4
|
-
* The one press response of interactive
|
|
5
|
-
* back button, Glass.interactive() surfaces). Fitted 2026-09-25 to
|
|
4
|
+
* The one press response of interactive glass (buttons, bar and toolbar buttons, the
|
|
5
|
+
* back button, Glass.interactive() surfaces). Fitted 2026-09-25 to native controls on the iOS
|
|
6
6
|
* 27.0 Simulator (ParityReference `--glass-press`, idb presses at human speed: 0.6 s holds and
|
|
7
7
|
* 0.12 s taps, light and dark; glass edges measured on the centre row and column):
|
|
8
8
|
*
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"centerX": 364,
|
|
6
6
|
"centerY": 156,
|
|
7
7
|
"diameter": 58,
|
|
8
|
-
"measurement": "Fresh native button versus glass-off control: 58 x 58.333 pt at 90-level threshold. The
|
|
8
|
+
"measurement": "Fresh native button versus glass-off control: 58 x 58.333 pt at 90-level threshold. The label frame is 44 pt."
|
|
9
9
|
},
|
|
10
10
|
"panel": {
|
|
11
11
|
"centerX": 267.5,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"scene": "reference iOS 27 Simulator Notes, light and dark touch-clocked recordings",
|
|
4
4
|
"source": "native opening measurements/curve fit and native-samples.json; recognition = touch +750ms",
|
|
5
5
|
"limits": "Translucent threshold contours are estimates; same-device optical acceptance remains pending.",
|
|
6
|
-
"content": "openContent: native iPad Pro 11 (2nd gen) iOS 27.0.1
|
|
6
|
+
"content": "openContent: native iPad Pro 11 (2nd gen) iOS 27.0.1 .contextMenu on a chat bubble, warm-light-chat-tall (3 counter-qualified trials, native menu measurements curve fit, width RMS 0.028, height RMS 0.017) on the recognition clock (content settle start = this epoch + 55 ms). Centres follow their size axis (native's first resolved frame is already at 89% width). Dark uses the light fit (dark windows were rejected)."
|
|
7
7
|
},
|
|
8
8
|
"open": {
|
|
9
9
|
"dark": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"provenance": {
|
|
3
|
-
"scene": "
|
|
3
|
+
"scene": "Native controls Menu(\"Menu selection\") in a List Section (native reference app), iPadOS 27.0, light",
|
|
4
4
|
"device": "iPad Pro 12.9 (6th gen), real HID taps via WDA",
|
|
5
5
|
"capture": "Native ReplayKit recording in a native reference app with a 60 Hz refresh counter (3.31% skipped refreshes, max skip 1: fit-valid, not playback-valid)",
|
|
6
6
|
"method": "Platter box per refresh (blurred frame difference against the closed frame, the label's text masked), timed by the counter. Open: two cycles, normalised from a point at the drop's first centre (Satin's plain drop grows from a point) to the settled panel; frames with at least 60% of the settled mask area, the 230-450 ms settle weighted 3x. Close: platter edges read per refresh from 60 Hz tiles (+-8 px), normalised from the panel to the drop that pours back into the label.",
|
|
@@ -10,7 +10,7 @@ import type { MenuSourceKind } from '../regions.js';
|
|
|
10
10
|
* The trigger's glyph and the menu's rows are ink drawn inside that glass (the ink kernel), so
|
|
11
11
|
* the growing rim lenses them and no page element is scaled, blurred or clipped.
|
|
12
12
|
*
|
|
13
|
-
* Phone reference: iOS 27.0 iPhone 17 Pro Max Simulator,
|
|
13
|
+
* Phone reference: iOS 27.0 iPhone 17 Pro Max Simulator, native toolbar menu
|
|
14
14
|
* (native recording menu measurements). Times are ms after the morph's time zero:
|
|
15
15
|
* native release +43 ms on open (regionScene anchors it on the trigger's release), close tap
|
|
16
16
|
* +41 ms on close. Measured (native release/tap times): the pressed 48 pt button holds, dips to
|
|
@@ -25,7 +25,7 @@ import type { MenuSourceKind } from '../regions.js';
|
|
|
25
25
|
* shape passes its button. Rows fade from the first close frame (246 → 96 in about 15 ms of
|
|
26
26
|
* motion, a faint blurred copy until about +140 ms).
|
|
27
27
|
*
|
|
28
|
-
* Triggers that are not a circle (same Simulator,
|
|
28
|
+
* Triggers that are not a circle (same Simulator, native menu from a labelled capsule
|
|
29
29
|
* 129 x 50, a full-width button 400 x 50 and a toolbar group 152 x 44; phase-morph-shapes-notes.md):
|
|
30
30
|
* the drop does not start as the trigger. The whole trigger glass contracts in about three frames
|
|
31
31
|
* (its width to 0.71 and 0.47 of rest while its height is at 0.90 and 0.74: it shortens faster
|
|
@@ -11,7 +11,7 @@ import { menuSpring } from './menuSpring.js';
|
|
|
11
11
|
* The trigger's glyph and the menu's rows are ink drawn inside that glass (the ink kernel), so
|
|
12
12
|
* the growing rim lenses them and no page element is scaled, blurred or clipped.
|
|
13
13
|
*
|
|
14
|
-
* Phone reference: iOS 27.0 iPhone 17 Pro Max Simulator,
|
|
14
|
+
* Phone reference: iOS 27.0 iPhone 17 Pro Max Simulator, native toolbar menu
|
|
15
15
|
* (native recording menu measurements). Times are ms after the morph's time zero:
|
|
16
16
|
* native release +43 ms on open (regionScene anchors it on the trigger's release), close tap
|
|
17
17
|
* +41 ms on close. Measured (native release/tap times): the pressed 48 pt button holds, dips to
|
|
@@ -26,7 +26,7 @@ import { menuSpring } from './menuSpring.js';
|
|
|
26
26
|
* shape passes its button. Rows fade from the first close frame (246 → 96 in about 15 ms of
|
|
27
27
|
* motion, a faint blurred copy until about +140 ms).
|
|
28
28
|
*
|
|
29
|
-
* Triggers that are not a circle (same Simulator,
|
|
29
|
+
* Triggers that are not a circle (same Simulator, native menu from a labelled capsule
|
|
30
30
|
* 129 x 50, a full-width button 400 x 50 and a toolbar group 152 x 44; phase-morph-shapes-notes.md):
|
|
31
31
|
* the drop does not start as the trigger. The whole trigger glass contracts in about three frames
|
|
32
32
|
* (its width to 0.71 and 0.47 of rest while its height is at 0.90 and 0.74: it shortens faster
|
|
@@ -456,7 +456,7 @@ const MENU_LABEL_INK = {
|
|
|
456
456
|
lens: [[0, 0]],
|
|
457
457
|
},
|
|
458
458
|
};
|
|
459
|
-
/** A plain glyph's drop (a Menu labelled with a bare symbol, no glass at rest):
|
|
459
|
+
/** A plain glyph's drop (a Menu labelled with a bare symbol, no glass at rest): the measured platform control
|
|
460
460
|
* in the iOS 27.0 Simulator, counter-retimed (menuGlyphDropFit.json). Open: the glyph vanishes on
|
|
461
461
|
* the tap; the body's first frame is already 94 x 82 pt with the rows inside it, small and
|
|
462
462
|
* blurred; they reach full size by +120 ms and are sharp by +154 ms. Close: the rows blur and
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"provenance": {
|
|
3
|
-
"source": "plain-menu-morph native/PlainMenuRef.swift:
|
|
3
|
+
"source": "plain-menu-morph native/PlainMenuRef.swift: native controls Menu { … } label: { Image(systemName: \"ellipsis\") } (no glass), iOS 27.0 Simulator, iPhone 17 Pro Max, light; raw/pmm-light-1.mp4",
|
|
4
4
|
"cadence": "refresh counter 58.47 Hz, skips 0.23 %, max skip 1, 0 unreadable: valid for fitting (duplicates removed by counter, times = refresh index x 17.1 ms)",
|
|
5
5
|
"openOnset": "counter 1512, the refresh where the glyph disappears (the tap)",
|
|
6
6
|
"closeOnset": "counter 1562, the last refresh before the open menu moves",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"provenance": {
|
|
3
|
-
"scene": "ParityReference --menu-morph,
|
|
3
|
+
"scene": "ParityReference --menu-morph, native controls toolbar Menu, black fixture",
|
|
4
4
|
"capture": "native menu native recording",
|
|
5
5
|
"contoursSha256": "548f8629dda83afa8467ced2bb0533db024468359f35f602e54febb6ba918310",
|
|
6
6
|
"units": "milliseconds and normalized centerX, centerY, width, height",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"provenance": {
|
|
3
|
-
"scene": "ParityReference toolbar-ellipsis,
|
|
3
|
+
"scene": "ParityReference toolbar-ellipsis, native controls toolbar Menu over black (iPhone 17 Pro Max Simulator, iOS 27.0)",
|
|
4
4
|
"capture": "morph2 native recording menu measurements.mp4",
|
|
5
5
|
"model": "One damped spring per axis, each a displacement from 1 to 0: released after `delay` ms with initial velocity `velocity` (per second; negative moves toward the target), natural frequency `omega` (rad/s), damping ratio `zeta`. centerX, centerY, width and height are the travelling member, normalized between the trigger (0) and the panel (1).",
|
|
6
6
|
"open": "ms after the trigger release +43 ms. Fitted to the native outline bounds (union of trigger and drop), RMS 0.018-0.041 per axis (native measurements).",
|
|
@@ -3,7 +3,7 @@ import { type FieldSpring } from '../fieldMotion.js';
|
|
|
3
3
|
* Motion of the TabView bottom accessory between its expanded row (above the tab bar) and its
|
|
4
4
|
* inline row (beside the minimized tab circle).
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* Measured bottom accessory motion during tab-bar minimization
|
|
7
7
|
* with three Music-style tabs, iPhone 17 Pro Max, iOS 27.0 Simulator, counter-retimed
|
|
8
8
|
* (RMS 2.6 pt collapse, 1.1 pt
|
|
9
9
|
* expand). Each edge follows an underdamped spring (damping ratio about 0.72), and the two axes
|
|
@@ -2,7 +2,7 @@ import type { CapsuleShape } from '../renderSpecs.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* The trailing Search member of a minimizing tab bar (`Tab(role: .search)` on iPhone).
|
|
4
4
|
*
|
|
5
|
-
* Native reference:
|
|
5
|
+
* Native reference: native controls on iOS 27.0, iPhone 17 Pro Max (search-tab-split, Simulator
|
|
6
6
|
* tracks at 60 Hz) and the owner's Apple Music recording on a physical iPhone. On collapse the
|
|
7
7
|
* bar's trailing end separates into its own capsule within one or two frames: at the split it
|
|
8
8
|
* spans x = 339–413 of the 21–419 bar, full height. It then narrows from its leading edge into
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The trailing Search member of a minimizing tab bar (`Tab(role: .search)` on iPhone).
|
|
3
3
|
*
|
|
4
|
-
* Native reference:
|
|
4
|
+
* Native reference: native controls on iOS 27.0, iPhone 17 Pro Max (search-tab-split, Simulator
|
|
5
5
|
* tracks at 60 Hz) and the owner's Apple Music recording on a physical iPhone. On collapse the
|
|
6
6
|
* bar's trailing end separates into its own capsule within one or two frames: at the split it
|
|
7
7
|
* spans x = 339–413 of the 21–419 bar, full height. It then narrows from its leading edge into
|
|
@@ -58,7 +58,7 @@ const MENU_GLYPH_RETURN_MS = 400;
|
|
|
58
58
|
/** Safety margin past the close morph for the renderer's settle acknowledgement. */
|
|
59
59
|
const MENU_SETTLE_TIMEOUT_MS = 700;
|
|
60
60
|
/** The order of a menu that opens upward: the first action nearest the trigger, so the list and
|
|
61
|
-
* each section in it read bottom to top (
|
|
61
|
+
* each section in it read bottom to top (native menu, iPhone, a trigger near the bottom of
|
|
62
62
|
* the screen). A submenu's own rows open separately and keep their order. */
|
|
63
63
|
function nearestFirst(actions) {
|
|
64
64
|
return [...actions]
|
package/dist/glass/regions.d.ts
CHANGED
|
@@ -225,7 +225,7 @@ export declare function glassPresenterBounds(frame: GlassRegion['frame'], inset:
|
|
|
225
225
|
* The 12M guard permits that stack plus chrome; it is not a memory benchmark. */
|
|
226
226
|
export declare const GLASS_REGION_LIMIT = 16;
|
|
227
227
|
export declare const GLASS_REGION_PIXEL_LIMIT = 12000000;
|
|
228
|
-
/** Immutable
|
|
228
|
+
/** Immutable glass material values. RGBA channels use the range 0...1. */
|
|
229
229
|
export declare class Glass {
|
|
230
230
|
static readonly regular: Glass;
|
|
231
231
|
static readonly clear: Glass;
|
package/dist/glass/regions.js
CHANGED
|
@@ -26,7 +26,7 @@ export function glassPresenterBounds(frame, inset, emerge) {
|
|
|
26
26
|
* The 12M guard permits that stack plus chrome; it is not a memory benchmark. */
|
|
27
27
|
export const GLASS_REGION_LIMIT = 16;
|
|
28
28
|
export const GLASS_REGION_PIXEL_LIMIT = 12_000_000;
|
|
29
|
-
/** Immutable
|
|
29
|
+
/** Immutable glass material values. RGBA channels use the range 0...1. */
|
|
30
30
|
export class Glass {
|
|
31
31
|
static regular = new Glass('regular');
|
|
32
32
|
static clear = new Glass('clear');
|
|
@@ -8,7 +8,7 @@ import type { SurfaceRenderSpec } from './renderSpecs.js';
|
|
|
8
8
|
* seems to slide and smear while nothing behind it moves (owner, Oct 13: "it looks like content is
|
|
9
9
|
* moving underneath as the menu grows").
|
|
10
10
|
*
|
|
11
|
-
* Native
|
|
11
|
+
* Native glass keeps the content behind it in place: only the edge moves. A shape track
|
|
12
12
|
* does the same. The element keeps one layout box for the whole run (the envelope of every frame
|
|
13
13
|
* the glass passes through), so its canvas never resizes or moves and samples the page where it
|
|
14
14
|
* really is. The worker draws the glass at the track's frame for each frame's presentation time,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type SpringParameters } from '../motion/springTrack.js';
|
|
2
2
|
/**
|
|
3
|
-
* Nested menus, measured from
|
|
3
|
+
* Nested menus, measured from native controls on iOS 27.0 (submenus/SPEC.md).
|
|
4
4
|
*
|
|
5
5
|
* iPhone stacks each submenu over its parent: the parent shrinks to 0.97 about its top edge and
|
|
6
6
|
* its rows dim to half ink, and the child grows out of the row that opened it into a full-width
|
|
@@ -4,7 +4,7 @@ import { setGlassShapeTrack } from './shapeTrackRegistry.js';
|
|
|
4
4
|
import { shapeTrackClock, shapeTrackEnvelope, shapeTrackFrame, } from './shapeTrack.js';
|
|
5
5
|
import { springMotion, springTrack, sampledKeyframes, } from '../motion/springTrack.js';
|
|
6
6
|
/**
|
|
7
|
-
* Nested menus, measured from
|
|
7
|
+
* Nested menus, measured from native controls on iOS 27.0 (submenus/SPEC.md).
|
|
8
8
|
*
|
|
9
9
|
* iPhone stacks each submenu over its parent: the parent shrinks to 0.97 about its top edge and
|
|
10
10
|
* its rows dim to half ink, and the child grows out of the row that opened it into a full-width
|
|
@@ -21,6 +21,7 @@ export declare class WebGPUClient {
|
|
|
21
21
|
};
|
|
22
22
|
private adapterDeadline?;
|
|
23
23
|
private failed?;
|
|
24
|
+
private startingTransport;
|
|
24
25
|
private frames;
|
|
25
26
|
private webgpu;
|
|
26
27
|
private currentAppearance;
|
|
@@ -45,5 +46,7 @@ export declare class WebGPUClient {
|
|
|
45
46
|
private focus;
|
|
46
47
|
private bindLifecycle;
|
|
47
48
|
resume(): void;
|
|
49
|
+
/** Dispose a failed transport before constructing its replacement. */
|
|
50
|
+
close(): void;
|
|
48
51
|
park(): Promise<unknown>;
|
|
49
52
|
}
|