@satinhq/kit 0.1.0-beta.2 → 0.1.0-beta.4
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 +2 -3
- package/dist/acknowledgements/inventory.json +5 -5
- package/dist/actions/RowActions.d.ts +4 -4
- package/dist/actions/RowActions.js +93 -27
- package/dist/actions/fullSwipeContact.d.ts +73 -0
- package/dist/actions/fullSwipeContact.js +186 -0
- package/dist/actions/rowScrub.d.ts +7 -4
- package/dist/actions/rowScrub.js +39 -37
- package/dist/actions/types.d.ts +13 -7
- package/dist/app/InstallComponents.d.ts +4 -7
- package/dist/app/InstallComponents.js +7 -7
- package/dist/app/InstallationDiagnostics.d.ts +2 -1
- package/dist/app/InstallationDiagnostics.js +2 -1
- package/dist/app/appearance.d.ts +5 -3
- package/dist/app/appearance.js +5 -3
- package/dist/app/appearanceStore.d.ts +3 -2
- package/dist/app/buildInfo.d.ts +4 -4
- package/dist/app/buildInfo.js +2 -2
- package/dist/app/definition.d.ts +11 -3
- package/dist/app/definition.js +2 -0
- package/dist/app/head.d.ts +2 -1
- package/dist/app/head.js +13 -16
- package/dist/app/inlineCSP.d.ts +6 -0
- package/dist/app/inlineCSP.js +25 -0
- package/dist/app/install.d.ts +1 -0
- package/dist/app/install.js +5 -2
- package/dist/app/installCheck.js +32 -86
- package/dist/app/installContext.d.ts +10 -5
- package/dist/app/installContext.js +2 -0
- package/dist/app/installCustom.js +1 -0
- package/dist/app/installExperience.js +0 -60
- package/dist/app/installationFailure.d.ts +3 -2
- package/dist/app/installationFailure.js +1 -1
- package/dist/app/launchScreen.d.ts +6 -2
- package/dist/app/launchScreen.js +104 -1
- package/dist/app/launchScreenRuntime.d.ts +31 -4
- package/dist/app/launchScreenRuntime.js +150 -22
- package/dist/app/launchWatchdog.js +25 -65
- package/dist/app/platformScripts.js +11 -0
- package/dist/app/styles.d.ts +3 -0
- package/dist/app/styles.js +42 -0
- package/dist/app/viewportValue.d.ts +2 -1
- package/dist/app/vite.d.ts +4 -0
- package/dist/app/vite.js +22 -24
- package/dist/content/content.d.ts +35 -30
- package/dist/content/content.js +19 -16
- package/dist/content/layout.d.ts +10 -2
- package/dist/content/layout.js +4 -2
- package/dist/content/scaledMetric.d.ts +2 -1
- package/dist/content/scaledMetric.js +2 -1
- package/dist/controls/ColorPicker.d.ts +2 -4
- package/dist/controls/ColorPicker.js +2 -4
- package/dist/controls/DatePicker.d.ts +4 -4
- package/dist/controls/DatePicker.js +2 -2
- package/dist/controls/ProgressView.d.ts +4 -4
- package/dist/controls/ProgressView.js +1 -2
- package/dist/controls/civilDate.d.ts +1 -1
- package/dist/controls/civilDate.js +1 -1
- package/dist/data/database.d.ts +9 -8
- package/dist/data/database.js +4 -4
- 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 +1888 -0
- package/dist/docs/tailwind.md +76 -0
- package/dist/docs/tokens.md +1 -1
- package/dist/gestures/GestureView.d.ts +3 -1
- package/dist/gestures/GestureView.js +2 -1
- package/dist/gestures/gesture.d.ts +22 -2
- package/dist/gestures/gesture.js +7 -2
- package/dist/glass/GlassEffect.d.ts +2 -1
- package/dist/glass/GlassEffect.js +2 -1
- package/dist/glass/GlassPresentation.d.ts +1 -0
- package/dist/glass/GlassPresentation.js +21 -4
- package/dist/glass/TabBar.js +23 -1
- package/dist/glass/errorAlert.js +2 -1
- package/dist/glass/errors.d.ts +3 -1
- package/dist/glass/fallback/runtime.d.ts +1 -0
- package/dist/glass/fallback/runtime.js +8 -1
- 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/liquidContext.d.ts +2 -0
- package/dist/glass/menuPresenter.d.ts +2 -0
- package/dist/glass/menuPresenter.js +1 -1
- package/dist/glass/regions.d.ts +3 -2
- package/dist/glass/regions.js +1 -1
- package/dist/glass/renderSpecs.d.ts +1 -0
- package/dist/glass/renderSpecs.js +1 -0
- 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/controlRim.d.ts +29 -0
- package/dist/glass/webgpu/controlRim.js +68 -0
- package/dist/glass/webgpu/entry.worker.js +3 -6
- 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/passes.d.ts +2 -2
- package/dist/glass/webgpu/production.d.ts +1 -0
- package/dist/glass/webgpu/production.js +31 -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/rasterUniforms.js +9 -0
- 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/shaders.d.ts +2 -2
- package/dist/glass/webgpu/shaders.js +5 -1
- package/dist/haptics/SensoryButton.d.ts +4 -4
- package/dist/haptics/SensoryButton.js +3 -3
- package/dist/image/Image.d.ts +6 -3
- package/dist/image/Image.js +4 -3
- package/dist/image/asset.d.ts +2 -1
- package/dist/keyboard/adapter.d.ts +3 -4
- 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/geometry.d.ts +1 -1
- package/dist/keyboard/motion.d.ts +1 -1
- package/dist/keyboard/motion.js +1 -1
- package/dist/list-form/ListForm.d.ts +10 -9
- package/dist/list-form/ListForm.js +3 -3
- 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 +10 -11
- package/dist/list-form/editing.js +5 -6
- package/dist/list-form/offsets.d.ts +2 -2
- package/dist/list-form/offsets.js +2 -2
- package/dist/media/react.d.ts +2 -1
- package/dist/media/react.js +2 -1
- package/dist/media/types.d.ts +6 -3
- package/dist/motion/TransitionView.d.ts +4 -2
- package/dist/motion/TransitionView.js +3 -2
- package/dist/motion/animationValue.d.ts +2 -2
- package/dist/motion/animationValue.js +9 -10
- package/dist/motion/matchedGeometry.d.ts +10 -9
- package/dist/motion/matchedGeometry.js +9 -9
- package/dist/motion/springTrack.d.ts +1 -2
- package/dist/motion/springTrack.js +10 -5
- package/dist/motion/transition.d.ts +4 -1
- package/dist/motion/transition.js +2 -0
- package/dist/navigation/DetailList.d.ts +1 -1
- package/dist/navigation/DetailList.js +1 -1
- package/dist/navigation/ScreenContainer.js +1 -1
- package/dist/navigation/Sidebar.d.ts +3 -1
- package/dist/navigation/Sidebar.js +1 -1
- package/dist/navigation/SidebarSymbol.d.ts +2 -1
- package/dist/navigation/SidebarSymbol.js +2 -1
- package/dist/navigation/chromeCurves.js +1 -1
- package/dist/navigation/navigationContext.d.ts +2 -1
- package/dist/navigation/navigationContext.js +2 -1
- package/dist/navigation/splitContext.d.ts +2 -1
- package/dist/navigation/splitContext.js +2 -1
- package/dist/navigation/toolbarMinimization.d.ts +4 -3
- package/dist/navigation/types.d.ts +8 -6
- 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 +10 -9
- package/dist/presentation/Presentation.js +4 -3
- package/dist/presentation/model.d.ts +2 -1
- package/dist/presentation/presentationSource.js +1 -1
- package/dist/press/Pressable.d.ts +5 -1
- package/dist/press/Pressable.js +2 -0
- package/dist/press/stateMachine.d.ts +1 -1
- package/dist/public/Label.d.ts +2 -1
- package/dist/public/Label.js +2 -1
- package/dist/public/Screen.d.ts +4 -6
- package/dist/public/actions.d.ts +2 -1
- package/dist/public/actions.js +2 -1
- package/dist/public/buttonBorderShape.d.ts +2 -1
- package/dist/public/colors.d.ts +7 -4
- package/dist/public/colors.js +3 -2
- package/dist/public/controlStyle.d.ts +17 -14
- package/dist/public/controls.d.ts +29 -28
- package/dist/public/controls.js +25 -24
- package/dist/public/deviceActions.d.ts +6 -4
- package/dist/public/deviceActions.js +4 -2
- package/dist/public/environment.d.ts +16 -13
- package/dist/public/environment.js +11 -9
- package/dist/public/filePickers.d.ts +6 -4
- package/dist/public/filePickers.js +3 -2
- package/dist/public/glass.d.ts +8 -6
- package/dist/public/glass.js +2 -2
- package/dist/public/glassValue.d.ts +3 -4
- package/dist/public/glassValue.js +3 -4
- package/dist/public/keyboard.d.ts +4 -1
- package/dist/public/keyboard.js +2 -1
- package/dist/public/navigationTransition.d.ts +6 -6
- package/dist/public/navigationTransition.js +3 -3
- package/dist/public/presentation.d.ts +8 -7
- package/dist/public/presentation.js +5 -5
- package/dist/public/runtime.d.ts +9 -8
- package/dist/public/runtime.js +7 -7
- package/dist/public/scroll.d.ts +6 -3
- package/dist/public/scroll.js +3 -1
- package/dist/public/structure.d.ts +2 -2
- package/dist/public/toolbar.d.ts +15 -11
- package/dist/public/toolbar.js +13 -8
- package/dist/reachability/model.d.ts +4 -3
- package/dist/reachability/react.d.ts +4 -1
- package/dist/reachability/react.js +2 -1
- package/dist/root/SatinRoot.d.ts +3 -3
- package/dist/root/SatinRoot.js +12 -36
- package/dist/root/mountSatin.d.ts +1 -1
- package/dist/root/mountSatin.js +26 -35
- package/dist/root/runtimeContext.d.ts +2 -1
- package/dist/root/runtimeContext.js +2 -1
- package/dist/safe-area/Overlay.d.ts +3 -2
- package/dist/safe-area/Overlay.js +2 -1
- package/dist/safe-area/SafeAreaInset.d.ts +11 -7
- package/dist/safe-area/SafeAreaInset.js +10 -9
- package/dist/safe-area/context.d.ts +3 -3
- package/dist/safe-area/context.js +1 -1
- package/dist/safe-area/displayCorners.d.ts +4 -2
- package/dist/safe-area/displayCorners.js +4 -2
- package/dist/safe-area/modifiers.d.ts +14 -10
- package/dist/safe-area/modifiers.js +10 -6
- package/dist/safe-area/useSafeAreaMetrics.js +2 -2
- package/dist/scroll/ScrollSurface.d.ts +5 -5
- package/dist/scroll/ScrollSurface.js +6 -2
- package/dist/scroll/ScrollTarget.d.ts +1 -1
- package/dist/scroll/ScrollTarget.js +1 -1
- package/dist/scroll/edgePolicy.d.ts +2 -2
- package/dist/scroll/position.d.ts +5 -3
- package/dist/scroll/position.js +1 -1
- package/dist/scroll/screenGeometry.d.ts +1 -1
- package/dist/scroll/screenGeometry.js +1 -1
- package/dist/scroll/types.d.ts +1 -1
- package/dist/search/PadSearchDrawer.d.ts +7 -0
- package/dist/search/PadSearchDrawer.js +8 -0
- package/dist/search/PadSearchPresentationContext.d.ts +6 -0
- package/dist/search/PadSearchPresentationContext.js +4 -0
- package/dist/search/SearchDrawer.d.ts +1 -1
- package/dist/search/SearchDrawer.js +7 -2
- package/dist/search/SearchSuggestionsPopover.d.ts +6 -0
- package/dist/search/SearchSuggestionsPopover.js +42 -0
- package/dist/search/SearchToolbarField.d.ts +2 -1
- package/dist/search/SearchToolbarField.js +28 -7
- package/dist/search/padSearchBarMotion.d.ts +69 -0
- package/dist/search/padSearchBarMotion.js +73 -0
- package/dist/search/padSearchFieldSamples.json +491 -0
- package/dist/search/searchMotion.d.ts +1 -1
- package/dist/search/searchMotion.js +1 -1
- package/dist/search/searchPlacement.d.ts +3 -0
- package/dist/search/searchPlacement.js +7 -0
- package/dist/search/searchable.d.ts +14 -12
- package/dist/search/usePadSearchFieldMotion.d.ts +4 -0
- package/dist/search/usePadSearchFieldMotion.js +100 -0
- package/dist/search/useSearchPresentation.d.ts +2 -1
- package/dist/search/useSearchPresentation.js +24 -6
- package/dist/server.d.ts +4 -4
- package/dist/server.js +4 -4
- package/dist/shapes/corners.d.ts +3 -3
- package/dist/shapes/corners.js +2 -3
- package/dist/styles.css +8302 -8236
- package/dist/sw/vite.d.ts +4 -0
- package/dist/sw/vite.js +2 -0
- package/dist/symbols/Symbol.d.ts +7 -5
- package/dist/symbols/Symbol.js +5 -3
- package/dist/symbols/catalogue.d.ts +1 -1
- package/dist/symbols/types.d.ts +6 -4
- package/dist/tabs/TabSection.d.ts +3 -2
- package/dist/tabs/TabSection.js +2 -1
- package/dist/tabs/TabView.js +54 -51
- package/dist/tasks/useTask.d.ts +5 -4
- package/dist/tasks/useTask.js +4 -3
- package/dist/testing.d.ts +3 -2
- package/dist/testing.js +3 -2
- package/dist/text-input/TextEditor.js +7 -3
- package/dist/text-input/focusChain.d.ts +4 -3
- package/dist/text-input/focusChain.js +3 -2
- package/dist/text-input/kinds.d.ts +1 -1
- package/dist/web/WebView.d.ts +10 -3
- package/dist/web/WebView.js +3 -2
- package/dist/web/authentication.d.ts +12 -1
- package/dist/web/authentication.js +2 -1
- package/dist/web/openURL.d.ts +10 -4
- package/dist/web/openURL.js +4 -2
- package/dist/web/useWebAuthentication.d.ts +2 -1
- package/dist/web/useWebAuthentication.js +2 -1
- package/dist/zoom/Zoomable.d.ts +3 -3
- package/dist/zoom/Zoomable.js +2 -2
- package/package.json +4 -4
- package/dist/docs/swiftui-reference.md +0 -1895
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Using Tailwind with Satin
|
|
2
|
+
|
|
3
|
+
Add Tailwind v4 to a Satin starter:
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npm install -D tailwindcss@4 @tailwindcss/vite@4
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Keep `satinApp()` in `vite.config.ts` and add the Tailwind plugin:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import react from '@vitejs/plugin-react'
|
|
13
|
+
import tailwindcss from '@tailwindcss/vite'
|
|
14
|
+
import { defineApp, satinApp } from '@satinhq/kit/vite'
|
|
15
|
+
import { defineConfig } from 'vite'
|
|
16
|
+
import { app } from './src/app'
|
|
17
|
+
|
|
18
|
+
export default defineConfig({
|
|
19
|
+
plugins: [react(), tailwindcss(), satinApp(defineApp(app))],
|
|
20
|
+
})
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Put this in your app's `src/styles.css`, which the starter already imports:
|
|
24
|
+
|
|
25
|
+
```css
|
|
26
|
+
@layer theme, base, satin, components, utilities;
|
|
27
|
+
@import 'tailwindcss/theme.css' layer(theme);
|
|
28
|
+
@import 'tailwindcss/utilities.css' layer(utilities);
|
|
29
|
+
|
|
30
|
+
@custom-variant dark (&:where([data-satin-appearance='dark'], [data-satin-appearance='dark'] *));
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This follows Tailwind's [Preflight opt-out](https://tailwindcss.com/docs/preflight#disabling-preflight).
|
|
34
|
+
Importing all of `tailwindcss` also installs a global reset. Omit that reset:
|
|
35
|
+
Satin supplies control defaults, and Preflight changes plain headings, images
|
|
36
|
+
and other app content. Keep app resets out of the framework's document and controls.
|
|
37
|
+
|
|
38
|
+
`satinApp()` includes the framework stylesheet automatically, before app CSS.
|
|
39
|
+
Satin reserves the layer order shown above and puts its rules in `satin`.
|
|
40
|
+
Utilities therefore win over framework stylesheet declarations; ordinary unlayered
|
|
41
|
+
app CSS wins over both. Inline component styles and explicit component props
|
|
42
|
+
still take precedence over ordinary utility classes. Prefer the component's
|
|
43
|
+
props for control appearance, sizing and row geometry.
|
|
44
|
+
|
|
45
|
+
Use utilities on app content or components that accept `className`:
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import { Button, ListRow, Text } from '@satinhq/kit'
|
|
49
|
+
|
|
50
|
+
export function Example() {
|
|
51
|
+
return (
|
|
52
|
+
<ListRow>
|
|
53
|
+
<Text className="text-fuchsia-600">A colored label</Text>
|
|
54
|
+
<Button className="text-fuchsia-600" onPress={() => {}}>Save</Button>
|
|
55
|
+
</ListRow>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`ListRow` does not accept `className`. Use `listRowBackground`, `listRowInsets`
|
|
61
|
+
and `listRowSeparator`, or apply utilities to content inside the row.
|
|
62
|
+
|
|
63
|
+
The custom `dark` variant follows Satin's effective appearance, including an
|
|
64
|
+
in-app override. Tailwind's default media-query variant follows only the system.
|
|
65
|
+
For safe areas, continue using Satin containers and geometry tokens. Do not add
|
|
66
|
+
viewport-height, raw scrolling or `env(safe-area-inset-*)` utilities to the shell.
|
|
67
|
+
|
|
68
|
+
Run your app's typecheck, fidelity lint and build after adding classes.
|
|
69
|
+
Fidelity lint does not expand dependency imports or utility names, so a passing
|
|
70
|
+
lint cannot approve a reset or a geometry utility.
|
|
71
|
+
`satin check <url>` checks a deployment's files and headers; it does not inspect
|
|
72
|
+
the CSS cascade or certify the appearance of utility classes.
|
|
73
|
+
|
|
74
|
+
For a custom build integration that does not use `satinApp()`, the stylesheet
|
|
75
|
+
fallback is `import '@satinhq/kit/styles.css'` once, before app styles. Apps using
|
|
76
|
+
`satinApp()` need no framework CSS import; existing imports remain compatible.
|
package/dist/docs/tokens.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Geometry and appearance tokens
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`satinApp()` includes framework styles automatically. Satin supplies these properties inside its mounted root and may override geometry in adaptive pane descendants. Consume the nearest value; do not copy root measurements into a sidebar or detail pane. Unknown custom-property names do not produce a TypeScript error and can leave a CSS declaration invalid.
|
|
4
4
|
|
|
5
5
|
| Property | Value | Use |
|
|
6
6
|
| --- | --- | --- |
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import type { Gesture } from './gesture.js';
|
|
3
|
+
/** The gestures a `GestureView` recognizes: a normal, a simultaneous and a high-priority gesture. */
|
|
3
4
|
export type GestureViewProps = Readonly<{
|
|
4
5
|
gesture?: Gesture;
|
|
5
6
|
simultaneousGesture?: Gesture;
|
|
6
7
|
highPriorityGesture?: Gesture;
|
|
7
8
|
children: ReactNode;
|
|
8
9
|
}>;
|
|
9
|
-
/** A
|
|
10
|
+
/** A region that recognizes custom gestures on its children. Scrolling and the back swipe still
|
|
11
|
+
* win the touches they own.
|
|
10
12
|
* @experimental Real-touch arbitration and native thresholds remain unqualified.
|
|
11
13
|
*/
|
|
12
14
|
export declare function GestureView({ gesture, simultaneousGesture, highPriorityGesture, children, }: GestureViewProps): import("react").JSX.Element;
|
|
@@ -6,7 +6,8 @@ import { cancelPointerPressForOwnedGesture, observePointerPressCancellation, } f
|
|
|
6
6
|
import { createGestureArena, failGesture, reduceGestureArena, } from './arena.js';
|
|
7
7
|
import { interactivePopRecognizer, nativeScrollRecognizer } from './existingCompetitors.js';
|
|
8
8
|
const regions = new WeakMap();
|
|
9
|
-
/** A
|
|
9
|
+
/** A region that recognizes custom gestures on its children. Scrolling and the back swipe still
|
|
10
|
+
* win the touches they own.
|
|
10
11
|
* @experimental Real-touch arbitration and native thresholds remain unqualified.
|
|
11
12
|
*/
|
|
12
13
|
export function GestureView({ gesture, simultaneousGesture, highPriorityGesture, children, }) {
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import { type GestureDecision, type GestureRecognizerContext } from './arena.js';
|
|
2
|
+
/** A point in CSS pixels. */
|
|
2
3
|
export type GesturePoint = Readonly<{
|
|
3
4
|
x: number;
|
|
4
5
|
y: number;
|
|
5
6
|
}>;
|
|
7
|
+
/** A drag's current and start locations, its translation and its velocity. */
|
|
6
8
|
export type DragGestureValue = Readonly<{
|
|
7
9
|
location: GesturePoint;
|
|
8
10
|
startLocation: GesturePoint;
|
|
9
11
|
translation: GesturePoint;
|
|
10
12
|
velocity: GesturePoint;
|
|
11
13
|
}>;
|
|
14
|
+
/** A pinch's scale relative to its start, and the point it started around. */
|
|
12
15
|
export type MagnifyGestureValue = Readonly<{
|
|
13
16
|
magnification: number;
|
|
14
17
|
startAnchor: GesturePoint;
|
|
15
18
|
}>;
|
|
19
|
+
/** The binding `useGestureState` returns. A gesture updates it while active and resets it when it
|
|
20
|
+
* ends. */
|
|
16
21
|
export type GestureState<Value> = Readonly<{
|
|
17
22
|
update(value: Value): void;
|
|
18
23
|
reset(): void;
|
|
@@ -27,6 +32,8 @@ export type GestureSample = Readonly<{
|
|
|
27
32
|
drag: DragGestureValue;
|
|
28
33
|
magnify: MagnifyGestureValue | undefined;
|
|
29
34
|
}>;
|
|
35
|
+
/** A gesture made by `DragGesture`, `LongPressGesture`, `TapGesture` or `MagnifyGesture`, for a
|
|
36
|
+
* `GestureView`. */
|
|
30
37
|
export type Gesture = Readonly<{
|
|
31
38
|
kind: 'drag' | 'longPress' | 'tap' | 'magnify';
|
|
32
39
|
axis: 'horizontal' | 'vertical' | 'any';
|
|
@@ -37,26 +44,39 @@ export type Gesture = Readonly<{
|
|
|
37
44
|
ended(sample: GestureSample): void;
|
|
38
45
|
cancelled(): void;
|
|
39
46
|
}>;
|
|
40
|
-
/**
|
|
41
|
-
*
|
|
47
|
+
/** Returns a value that a gesture drives, and the binding to pass as the gesture's `state`. The
|
|
48
|
+
* value goes back to `initialValue` when the gesture ends or is cancelled. */
|
|
42
49
|
export declare function useGestureState<Value>(initialValue: Value): readonly [Value, GestureState<Value>];
|
|
50
|
+
/** A drag's callbacks and state, the distance it must travel before it starts (10 by default) and
|
|
51
|
+
* the axis it accepts. */
|
|
43
52
|
export type DragGestureOptions = Callbacks<DragGestureValue> & Readonly<{
|
|
44
53
|
minimumDistance?: number;
|
|
45
54
|
axis?: Gesture['axis'];
|
|
46
55
|
}>;
|
|
56
|
+
/** A drag that starts after the finger moves `minimumDistance` along the accepted axis. */
|
|
47
57
|
export declare function DragGesture({ minimumDistance, axis, ...options }?: DragGestureOptions): Gesture;
|
|
58
|
+
/** A long press's callbacks and state, how long it must be held (500 ms by default) and how far
|
|
59
|
+
* the finger may move (10 by default). */
|
|
48
60
|
export type LongPressGestureOptions = Callbacks<boolean> & Readonly<{
|
|
49
61
|
minimumDurationMs?: number;
|
|
50
62
|
maximumDistance?: number;
|
|
51
63
|
}>;
|
|
64
|
+
/** A press held for `minimumDurationMs` without moving more than `maximumDistance`. */
|
|
52
65
|
export declare function LongPressGesture({ minimumDurationMs, maximumDistance, ...options }?: LongPressGestureOptions): Gesture;
|
|
66
|
+
/** A tap's callbacks and state, the number of taps (1 by default) and how far the finger may
|
|
67
|
+
* move (10 by default). */
|
|
53
68
|
export type TapGestureOptions = Callbacks<GesturePoint> & Readonly<{
|
|
54
69
|
count?: number;
|
|
55
70
|
maximumDistance?: number;
|
|
56
71
|
}>;
|
|
72
|
+
/** A tap, or `count` taps, that ends without moving more than `maximumDistance`. It reports where
|
|
73
|
+
* the finger lifted. */
|
|
57
74
|
export declare function TapGesture({ count, maximumDistance, ...options }?: TapGestureOptions): Gesture;
|
|
75
|
+
/** A pinch's callbacks and state, and how far the scale must change before it starts (0.01 by
|
|
76
|
+
* default). */
|
|
58
77
|
export type MagnifyGestureOptions = Callbacks<MagnifyGestureValue> & Readonly<{
|
|
59
78
|
minimumScaleDelta?: number;
|
|
60
79
|
}>;
|
|
80
|
+
/** A two-finger pinch that starts once the scale changes by `minimumScaleDelta`. */
|
|
61
81
|
export declare function MagnifyGesture({ minimumScaleDelta, ...options }?: MagnifyGestureOptions): Gesture;
|
|
62
82
|
export {};
|
package/dist/gestures/gesture.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useMemo, useRef, useState } from 'react';
|
|
2
2
|
import { deferGesture, failGesture, recognizeGesture, } from './arena.js';
|
|
3
|
-
/**
|
|
4
|
-
*
|
|
3
|
+
/** Returns a value that a gesture drives, and the binding to pass as the gesture's `state`. The
|
|
4
|
+
* value goes back to `initialValue` when the gesture ends or is cancelled. */
|
|
5
5
|
export function useGestureState(initialValue) {
|
|
6
6
|
const [value, setValue] = useState(initialValue);
|
|
7
7
|
const initial = useRef(initialValue);
|
|
@@ -38,6 +38,7 @@ function distance(value, name) {
|
|
|
38
38
|
throw new Error(`${name} must be finite and non-negative.`);
|
|
39
39
|
return value;
|
|
40
40
|
}
|
|
41
|
+
/** A drag that starts after the finger moves `minimumDistance` along the accepted axis. */
|
|
41
42
|
export function DragGesture({ minimumDistance = 10, axis = 'any', ...options } = {}) {
|
|
42
43
|
distance(minimumDistance, 'Drag minimumDistance');
|
|
43
44
|
return Object.freeze({
|
|
@@ -56,6 +57,7 @@ export function DragGesture({ minimumDistance = 10, axis = 'any', ...options } =
|
|
|
56
57
|
},
|
|
57
58
|
});
|
|
58
59
|
}
|
|
60
|
+
/** A press held for `minimumDurationMs` without moving more than `maximumDistance`. */
|
|
59
61
|
export function LongPressGesture({ minimumDurationMs = 500, maximumDistance = 10, ...options } = {}) {
|
|
60
62
|
distance(minimumDurationMs, 'Long press minimumDurationMs');
|
|
61
63
|
distance(maximumDistance, 'Long press maximumDistance');
|
|
@@ -74,6 +76,8 @@ export function LongPressGesture({ minimumDurationMs = 500, maximumDistance = 10
|
|
|
74
76
|
},
|
|
75
77
|
});
|
|
76
78
|
}
|
|
79
|
+
/** A tap, or `count` taps, that ends without moving more than `maximumDistance`. It reports where
|
|
80
|
+
* the finger lifted. */
|
|
77
81
|
export function TapGesture({ count = 1, maximumDistance = 10, ...options } = {}) {
|
|
78
82
|
if (!Number.isInteger(count) || count < 1)
|
|
79
83
|
throw new Error('Tap count must be a positive integer.');
|
|
@@ -90,6 +94,7 @@ export function TapGesture({ count = 1, maximumDistance = 10, ...options } = {})
|
|
|
90
94
|
},
|
|
91
95
|
});
|
|
92
96
|
}
|
|
97
|
+
/** A two-finger pinch that starts once the scale changes by `minimumScaleDelta`. */
|
|
93
98
|
export function MagnifyGesture({ minimumScaleDelta = 0.01, ...options } = {}) {
|
|
94
99
|
distance(minimumScaleDelta, 'Magnify minimumScaleDelta');
|
|
95
100
|
return Object.freeze({
|
|
@@ -28,7 +28,8 @@ export type GlassEffectProps = HTMLAttributes<HTMLDivElement> & Readonly<{
|
|
|
28
28
|
}>;
|
|
29
29
|
/** Local content remains DOM-owned; only the material canvas is painted by reference. */
|
|
30
30
|
export declare function GlassEffect({ glass, shape, touchLight, glassEffectID, namespace, union, transition, press, emerge, onEmergeSettled, onEmergeInk, children, style, onPointerDown, onPointerUp, onClick, ...props }: GlassEffectProps): import("react").JSX.Element;
|
|
31
|
-
/**
|
|
31
|
+
/** Groups nearby glass effects so they render together and blend or morph into one another.
|
|
32
|
+
* `spacing`, 20 pt by default, sets how close effects must be to merge. */
|
|
32
33
|
export declare function GlassEffectContainer({ spacing, children, ...props }: HTMLAttributes<HTMLDivElement> & {
|
|
33
34
|
spacing?: number;
|
|
34
35
|
}): import("react").JSX.Element;
|
|
@@ -245,7 +245,8 @@ export function GlassEffect({ glass = Glass.regular, shape = 'capsule', touchLig
|
|
|
245
245
|
justifyContent: style?.justifyContent,
|
|
246
246
|
}, children: children })) : (children)] }));
|
|
247
247
|
}
|
|
248
|
-
/**
|
|
248
|
+
/** Groups nearby glass effects so they render together and blend or morph into one another.
|
|
249
|
+
* `spacing`, 20 pt by default, sets how close effects must be to merge. */
|
|
249
250
|
export function GlassEffectContainer({ spacing = 20, children, ...props }) {
|
|
250
251
|
const id = useId(), element = useRef(null), canvas = useRef(null);
|
|
251
252
|
const liquid = useContext(MaterialEffectContext);
|
|
@@ -14,6 +14,7 @@ export type GlassPresentationConsumer = Readonly<{
|
|
|
14
14
|
intrinsicWidth?: boolean;
|
|
15
15
|
placement?: 'bottom' | 'top';
|
|
16
16
|
itemFrames?: readonly TabItemFrame[];
|
|
17
|
+
searchProgress?: number;
|
|
17
18
|
items: readonly TabBarItem[];
|
|
18
19
|
selected: string;
|
|
19
20
|
onSelect: (id: string) => void;
|
|
@@ -182,6 +182,8 @@ export function GlassPresentation({ consumer }) {
|
|
|
182
182
|
? consumer.tabs.itemFrames
|
|
183
183
|
: undefined;
|
|
184
184
|
const tabBarPlacement = consumer?.tabs?.placement;
|
|
185
|
+
const searchProgress = tabBarPlacement === 'top' ? (consumer?.tabs?.searchProgress ?? 0) : 0;
|
|
186
|
+
const tabBarOpacity = 1 - searchProgress;
|
|
185
187
|
const compactTabs = readDeviceIdiom() === 'phone' && width > height;
|
|
186
188
|
const intrinsicTabs = Boolean(consumer?.tabs?.intrinsicWidth) && !compactTabs;
|
|
187
189
|
const intrinsicGeometry = useMemo(() => (intrinsicTabs ? intrinsicTabGeometry(dockWidth, tabCount) : undefined), [intrinsicTabs, dockWidth, tabCount]);
|
|
@@ -232,8 +234,11 @@ export function GlassPresentation({ consumer }) {
|
|
|
232
234
|
: 7;
|
|
233
235
|
// The measured TabView material occupies the bottom safe-area band; its
|
|
234
236
|
// glyphs remain above the Home indicator inside that material.
|
|
235
|
-
const dockY = insetFrame?.y ??
|
|
236
|
-
(isDragPage
|
|
237
|
+
const dockY = (insetFrame?.y ??
|
|
238
|
+
(isDragPage
|
|
239
|
+
? dragFrames.capsule.y
|
|
240
|
+
: height - GLASS_DOCK_BOTTOM_GAP_PX - fallbackDockHeight)) -
|
|
241
|
+
64 * searchProgress;
|
|
237
242
|
const dockRadius = isDragPage
|
|
238
243
|
? dragFrames.capsule.height / 2
|
|
239
244
|
: topFrames
|
|
@@ -276,6 +281,7 @@ export function GlassPresentation({ consumer }) {
|
|
|
276
281
|
tabBarPillWidth,
|
|
277
282
|
tabBarPillWidths,
|
|
278
283
|
tabBarPlacement,
|
|
284
|
+
tabBarOpacity,
|
|
279
285
|
tabBarPillHeight,
|
|
280
286
|
tabBarPillCenterY,
|
|
281
287
|
tabBarSelectedIndex: tabBarVisible ? selectedTab : undefined,
|
|
@@ -654,12 +660,13 @@ export function GlassPresentation({ consumer }) {
|
|
|
654
660
|
// DOM, selected state and renderer-neutral region registrations mounted.
|
|
655
661
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
656
662
|
}, [consumer?.backend]);
|
|
657
|
-
|
|
663
|
+
const synchronizeGeometry = useCallback(() => {
|
|
658
664
|
runtimeRef.current?.updateGeometry({
|
|
659
665
|
tabBarCentres,
|
|
660
666
|
tabBarPillWidth,
|
|
661
667
|
tabBarPillWidths,
|
|
662
668
|
tabBarPlacement,
|
|
669
|
+
tabBarOpacity,
|
|
663
670
|
tabBarMinimizedOffsetX,
|
|
664
671
|
tabBarPillHeight,
|
|
665
672
|
tabBarPillCenterY,
|
|
@@ -683,6 +690,7 @@ export function GlassPresentation({ consumer }) {
|
|
|
683
690
|
tabBarCentres,
|
|
684
691
|
tabBarPillWidths,
|
|
685
692
|
tabBarPlacement,
|
|
693
|
+
tabBarOpacity,
|
|
686
694
|
tabBarMinimizedOffsetX,
|
|
687
695
|
tabBarPillWidth,
|
|
688
696
|
tabBarPillHeight,
|
|
@@ -703,6 +711,14 @@ export function GlassPresentation({ consumer }) {
|
|
|
703
711
|
rightX,
|
|
704
712
|
width,
|
|
705
713
|
]);
|
|
714
|
+
useLayoutEffect(() => {
|
|
715
|
+
if (readDeviceIdiom() === 'pad')
|
|
716
|
+
synchronizeGeometry();
|
|
717
|
+
}, [synchronizeGeometry]);
|
|
718
|
+
useEffect(() => {
|
|
719
|
+
if (readDeviceIdiom() !== 'pad')
|
|
720
|
+
synchronizeGeometry();
|
|
721
|
+
}, [synchronizeGeometry]);
|
|
706
722
|
useEffect(() => runtimeRef.current?.setTabSelection(tabBarVisible ? selectedTab : undefined), [selectedTab, tabBarVisible]);
|
|
707
723
|
useLayoutEffect(() => runtimeRef.current?.commitAppearance(appearance), [appearance]);
|
|
708
724
|
// Settings and reference backgrounds are different DOM roots. Hand the newly
|
|
@@ -864,12 +880,13 @@ export function GlassPresentation({ consumer }) {
|
|
|
864
880
|
height: liquidEnvelope.frameHeight,
|
|
865
881
|
}
|
|
866
882
|
: {
|
|
883
|
+
opacity: tabBarOpacity,
|
|
867
884
|
borderRadius: dockRadius,
|
|
868
885
|
left: dockX,
|
|
869
886
|
top: dockY,
|
|
870
887
|
width: dockWidth,
|
|
871
888
|
height: dockHeight,
|
|
872
|
-
}, "data-satin-tab-layout": topFrames ? 'top' : compactTabs ? 'compact' : undefined, role: liquidMotion !== 'none' ? 'presentation' : tabBarVisible ? 'tablist' : undefined, "aria-label": liquidMotion !== 'none'
|
|
889
|
+
}, inert: searchProgress > 0 || undefined, "aria-hidden": searchProgress > 0 || undefined, "data-satin-tab-layout": topFrames ? 'top' : compactTabs ? 'compact' : undefined, role: liquidMotion !== 'none' ? 'presentation' : tabBarVisible ? 'tablist' : undefined, "aria-label": liquidMotion !== 'none'
|
|
873
890
|
? `Liquid ${liquidMotion} surface`
|
|
874
891
|
: tabBarVisible
|
|
875
892
|
? probe
|
package/dist/glass/TabBar.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { PadSearchBarMotion } from '../search/padSearchBarMotion.js';
|
|
3
|
+
import { PadSearchPresentationContext } from '../search/PadSearchPresentationContext.js';
|
|
2
4
|
import { useControlStyle } from '../public/controlStyle.js';
|
|
3
5
|
import { orderedTabItems, TopTabLabel } from '../tabs/presentation.js';
|
|
4
6
|
import { handleTabKeyDown } from '../tabs/keyboard.js';
|
|
@@ -20,6 +22,8 @@ import { useGlassMaterialContext } from './context.js';
|
|
|
20
22
|
import { resolveTabBarModel, TabBarAccessoryContext } from './tabBarModel.js';
|
|
21
23
|
import {} from './tokens.js';
|
|
22
24
|
export function TabBar({ className, adaptive = true, onShowSidebar, items: declaredItems, onSelect, selected, style, variant = 'regular', ...props }) {
|
|
25
|
+
const searchPresentation = useContext(PadSearchPresentationContext);
|
|
26
|
+
const searchProgress = searchPresentation?.owner === selected ? 1 : 0;
|
|
23
27
|
const { tint } = useControlStyle();
|
|
24
28
|
const liquid = useContext(MaterialEffectContext);
|
|
25
29
|
const layoutClass = useLayoutClass();
|
|
@@ -37,6 +41,19 @@ export function TabBar({ className, adaptive = true, onShowSidebar, items: decla
|
|
|
37
41
|
const { chromeLayer, registerRegion } = useGlassMaterialContext();
|
|
38
42
|
const regionId = useId();
|
|
39
43
|
const elementRef = useRef(null);
|
|
44
|
+
const padBarMotion = useRef(null);
|
|
45
|
+
const previousSearchProgress = useRef(searchProgress);
|
|
46
|
+
useLayoutEffect(() => {
|
|
47
|
+
if (!sidebar || liquidTop || previousSearchProgress.current === searchProgress)
|
|
48
|
+
return;
|
|
49
|
+
previousSearchProgress.current = searchProgress;
|
|
50
|
+
const element = elementRef.current;
|
|
51
|
+
if (!element)
|
|
52
|
+
return;
|
|
53
|
+
padBarMotion.current ??= new PadSearchBarMotion();
|
|
54
|
+
padBarMotion.current.start(element, searchProgress, undefined);
|
|
55
|
+
}, [sidebar, liquidTop, searchProgress]);
|
|
56
|
+
useLayoutEffect(() => () => padBarMotion.current?.dispose(), []);
|
|
40
57
|
const hasAccessory = useContext(TabBarAccessoryContext);
|
|
41
58
|
const compact = readDeviceIdiom() === 'phone' && metrics.layoutViewport.width > metrics.layoutViewport.height;
|
|
42
59
|
const intrinsicWidth = readDeviceIdiom() === 'phone' && !compact && items.length < 4 && !hasAccessory;
|
|
@@ -88,6 +105,7 @@ export function TabBar({ className, adaptive = true, onShowSidebar, items: decla
|
|
|
88
105
|
tint,
|
|
89
106
|
insetHost: elementRef.current ?? undefined,
|
|
90
107
|
placement: 'top',
|
|
108
|
+
searchProgress,
|
|
91
109
|
itemFrames,
|
|
92
110
|
});
|
|
93
111
|
return () => liquid?.registerTabs(regionId, undefined);
|
|
@@ -102,6 +120,7 @@ export function TabBar({ className, adaptive = true, onShowSidebar, items: decla
|
|
|
102
120
|
tint,
|
|
103
121
|
liquidTop,
|
|
104
122
|
itemFrames,
|
|
123
|
+
searchProgress,
|
|
105
124
|
]);
|
|
106
125
|
useLayoutEffect(() => {
|
|
107
126
|
if (sidebar)
|
|
@@ -152,12 +171,15 @@ export function TabBar({ className, adaptive = true, onShowSidebar, items: decla
|
|
|
152
171
|
const tabBarStyle = {
|
|
153
172
|
...style,
|
|
154
173
|
...(sidebar ? sidebarCssTokens : {}),
|
|
174
|
+
...(sidebar && searchProgress
|
|
175
|
+
? { translate: `0 ${-64 * searchProgress}px`, opacity: 1 - searchProgress }
|
|
176
|
+
: {}),
|
|
155
177
|
'--satin-tab-tint': tint,
|
|
156
178
|
'--satin-tab-count': items.length,
|
|
157
179
|
'--satin-tab-selection-offset': `${selectedIndex * 100}%`,
|
|
158
180
|
'--satin-tab-selected-index': selectedIndex,
|
|
159
181
|
};
|
|
160
|
-
return createPortal(_jsxs("nav", { ...props, ref: elementRef, role: "tablist", onKeyDown: handleTabKeyDown, "aria-label": props['aria-label'] ?? 'App tabs', className: className, "data-satin-glass-region": sidebar ? undefined : regionId, "data-satin-tab-placement": sidebar ? 'top' : 'bottom', "data-satin-glass-variant": variant, "data-satin-role": "chrome", "data-satin-tab-bar": "true", style: tabBarStyle, children: [_jsx(GlassBackground, { glass: variant === 'clear' ? Glass.clear : Glass.regular }), _jsxs("div", { "data-satin-tab-items": "true", children: [onShowSidebar && (_jsx(Pressable, { "data-satin-tab-sidebar-show": "true", "aria-label": "Show Tab Sidebar", onPress: onShowSidebar, children: _jsx(SymbolImage, { name: "sidebar.left" }) })), items.map((item, index) => {
|
|
182
|
+
return createPortal(_jsxs("nav", { ...props, ref: elementRef, role: "tablist", inert: props.inert || (sidebar && searchProgress > 0) || undefined, "aria-hidden": props['aria-hidden'] || (sidebar && searchProgress > 0) || undefined, onKeyDown: handleTabKeyDown, "aria-label": props['aria-label'] ?? 'App tabs', className: className, "data-satin-glass-region": sidebar ? undefined : regionId, "data-satin-tab-placement": sidebar ? 'top' : 'bottom', "data-satin-glass-variant": variant, "data-satin-role": "chrome", "data-satin-tab-bar": "true", style: tabBarStyle, children: [_jsx(GlassBackground, { glass: variant === 'clear' ? Glass.clear : Glass.regular }), _jsxs("div", { "data-satin-tab-items": "true", children: [onShowSidebar && (_jsx(Pressable, { "data-satin-tab-sidebar-show": "true", "aria-label": "Show Tab Sidebar", onPress: onShowSidebar, children: _jsx(SymbolImage, { name: "sidebar.left" }) })), items.map((item, index) => {
|
|
161
183
|
const isSelected = item.id === selected;
|
|
162
184
|
return (_jsxs(Pressable, { role: "tab", id: item.tabId, "aria-controls": item.panelId, "aria-selected": isSelected, tabIndex: isSelected || (selectedIndex < 0 && index === 0) ? 0 : -1, "aria-label": item.accessibilityLabel ??
|
|
163
185
|
(sidebar && item.role === 'search'
|
package/dist/glass/errorAlert.js
CHANGED
|
@@ -33,7 +33,8 @@ export function installGlassErrorAlert() {
|
|
|
33
33
|
const errors = getGlassErrors().filter(error => !error.transient);
|
|
34
34
|
if (!errors.length)
|
|
35
35
|
return;
|
|
36
|
-
|
|
36
|
+
const prelaunch = window.__SATIN_PRELAUNCH__;
|
|
37
|
+
if (prelaunch && ['preparing', 'failed'].includes(prelaunch.snapshot().state)) {
|
|
37
38
|
document.querySelector(':root > pre[data-satin-glass-error]')?.remove();
|
|
38
39
|
return;
|
|
39
40
|
}
|
package/dist/glass/errors.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** Why the glass renderer failed, such as `gpu-unavailable` or `device-lost`. In a release build a
|
|
2
|
+
* failure can switch glass to the fallback material. */
|
|
2
3
|
export type GlassErrorCode = 'gpu-unavailable' | 'adapter-unavailable' | 'device-request' | 'context-configure' | 'device-lost' | 'validation' | 'out-of-memory' | 'shader-compilation' | 'worker-startup' | 'readiness-timeout' | 'presentation' | 'unsupported-configuration';
|
|
4
|
+
/** One glass renderer failure: its code, message, the stage it happened in and when. */
|
|
3
5
|
export type GlassError = Readonly<{
|
|
4
6
|
code: GlassErrorCode;
|
|
5
7
|
message: string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PadSearchBarMotion } from '../../search/padSearchBarMotion.js';
|
|
1
2
|
import { getGlassErrors } from '../errors.js';
|
|
2
3
|
import { createRenderSpecs, } from '../renderSpecs.js';
|
|
3
4
|
import { TabMotion, TAB_INTERACTIVE_MOTION } from '../lens/tabMotion.js';
|
|
@@ -13,6 +14,7 @@ export class FallbackGlassRuntime {
|
|
|
13
14
|
regions;
|
|
14
15
|
dock;
|
|
15
16
|
thumb;
|
|
17
|
+
padSearch;
|
|
16
18
|
minimize = new TabMinimizeIntent();
|
|
17
19
|
minimizeDom;
|
|
18
20
|
tab = new TabMotion(TAB_INTERACTIVE_MOTION);
|
|
@@ -93,7 +95,7 @@ export class FallbackGlassRuntime {
|
|
|
93
95
|
dockSpec() {
|
|
94
96
|
const spec = createRenderSpecs(this.options.geometry, this.options.appearance).find(spec => spec.id === 'dock');
|
|
95
97
|
// Chrome's DOM host already owns the viewport placement.
|
|
96
|
-
return { ...spec, frameX: 0, frameY: 0 };
|
|
98
|
+
return { ...spec, opacity: 1, frameX: 0, frameY: 0 };
|
|
97
99
|
}
|
|
98
100
|
minimizeTrack(direction, path = 'tap') {
|
|
99
101
|
const track = this.minimizeDom.start(direction, this.dockSpec(), path);
|
|
@@ -131,9 +133,13 @@ export class FallbackGlassRuntime {
|
|
|
131
133
|
this.thumb.paint(this.thumbSpec(selection), this.options.appearance);
|
|
132
134
|
}
|
|
133
135
|
updateGeometry(next) {
|
|
136
|
+
const previousOpacity = this.options.geometry.tabBarOpacity;
|
|
134
137
|
const previousPlacement = this.options.geometry.tabBarPlacement;
|
|
135
138
|
this.options = { ...this.options, geometry: { ...this.options.geometry, ...next } };
|
|
136
139
|
const geometry = this.options.geometry;
|
|
140
|
+
if (previousPlacement === 'top' && geometry.tabBarOpacity !== undefined && previousOpacity !== geometry.tabBarOpacity) {
|
|
141
|
+
(this.padSearch ??= new PadSearchBarMotion()).start(this.options.elements.chrome.dock, 1 - geometry.tabBarOpacity, this.dockSpec());
|
|
142
|
+
}
|
|
137
143
|
this.tab.configure(geometry.tabBarCentres, geometry.tabBarPillWidth, geometry.tabBarPillHeight, geometry.dockWidth, {
|
|
138
144
|
selected: geometry.tabBarSelectedIndex ?? 0,
|
|
139
145
|
centerY: geometry.tabBarPillCenterY,
|
|
@@ -274,6 +280,7 @@ export class FallbackGlassRuntime {
|
|
|
274
280
|
this.dispose(owner);
|
|
275
281
|
}
|
|
276
282
|
dispose(_owner) {
|
|
283
|
+
this.padSearch?.dispose();
|
|
277
284
|
if (this.disposed)
|
|
278
285
|
return;
|
|
279
286
|
this.disposed = true;
|
|
@@ -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",
|