@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.
Files changed (309) hide show
  1. package/NOTICE +2 -2
  2. package/README.md +2 -3
  3. package/dist/acknowledgements/inventory.json +5 -5
  4. package/dist/actions/RowActions.d.ts +4 -4
  5. package/dist/actions/RowActions.js +93 -27
  6. package/dist/actions/fullSwipeContact.d.ts +73 -0
  7. package/dist/actions/fullSwipeContact.js +186 -0
  8. package/dist/actions/rowScrub.d.ts +7 -4
  9. package/dist/actions/rowScrub.js +39 -37
  10. package/dist/actions/types.d.ts +13 -7
  11. package/dist/app/InstallComponents.d.ts +4 -7
  12. package/dist/app/InstallComponents.js +7 -7
  13. package/dist/app/InstallationDiagnostics.d.ts +2 -1
  14. package/dist/app/InstallationDiagnostics.js +2 -1
  15. package/dist/app/appearance.d.ts +5 -3
  16. package/dist/app/appearance.js +5 -3
  17. package/dist/app/appearanceStore.d.ts +3 -2
  18. package/dist/app/buildInfo.d.ts +4 -4
  19. package/dist/app/buildInfo.js +2 -2
  20. package/dist/app/definition.d.ts +11 -3
  21. package/dist/app/definition.js +2 -0
  22. package/dist/app/head.d.ts +2 -1
  23. package/dist/app/head.js +13 -16
  24. package/dist/app/inlineCSP.d.ts +6 -0
  25. package/dist/app/inlineCSP.js +25 -0
  26. package/dist/app/install.d.ts +1 -0
  27. package/dist/app/install.js +5 -2
  28. package/dist/app/installCheck.js +32 -86
  29. package/dist/app/installContext.d.ts +10 -5
  30. package/dist/app/installContext.js +2 -0
  31. package/dist/app/installCustom.js +1 -0
  32. package/dist/app/installExperience.js +0 -60
  33. package/dist/app/installationFailure.d.ts +3 -2
  34. package/dist/app/installationFailure.js +1 -1
  35. package/dist/app/launchScreen.d.ts +6 -2
  36. package/dist/app/launchScreen.js +104 -1
  37. package/dist/app/launchScreenRuntime.d.ts +31 -4
  38. package/dist/app/launchScreenRuntime.js +150 -22
  39. package/dist/app/launchWatchdog.js +25 -65
  40. package/dist/app/platformScripts.js +11 -0
  41. package/dist/app/styles.d.ts +3 -0
  42. package/dist/app/styles.js +42 -0
  43. package/dist/app/viewportValue.d.ts +2 -1
  44. package/dist/app/vite.d.ts +4 -0
  45. package/dist/app/vite.js +22 -24
  46. package/dist/content/content.d.ts +35 -30
  47. package/dist/content/content.js +19 -16
  48. package/dist/content/layout.d.ts +10 -2
  49. package/dist/content/layout.js +4 -2
  50. package/dist/content/scaledMetric.d.ts +2 -1
  51. package/dist/content/scaledMetric.js +2 -1
  52. package/dist/controls/ColorPicker.d.ts +2 -4
  53. package/dist/controls/ColorPicker.js +2 -4
  54. package/dist/controls/DatePicker.d.ts +4 -4
  55. package/dist/controls/DatePicker.js +2 -2
  56. package/dist/controls/ProgressView.d.ts +4 -4
  57. package/dist/controls/ProgressView.js +1 -2
  58. package/dist/controls/civilDate.d.ts +1 -1
  59. package/dist/controls/civilDate.js +1 -1
  60. package/dist/data/database.d.ts +9 -8
  61. package/dist/data/database.js +4 -4
  62. package/dist/docs/components/file-importer.md +1 -1
  63. package/dist/docs/components/paste-button.md +1 -1
  64. package/dist/docs/components/photos-picker.md +1 -1
  65. package/dist/docs/components/share-link.md +1 -1
  66. package/dist/docs/limitations.md +1 -1
  67. package/dist/docs/push.md +3 -3
  68. package/dist/docs/reference.md +1888 -0
  69. package/dist/docs/tailwind.md +76 -0
  70. package/dist/docs/tokens.md +1 -1
  71. package/dist/gestures/GestureView.d.ts +3 -1
  72. package/dist/gestures/GestureView.js +2 -1
  73. package/dist/gestures/gesture.d.ts +22 -2
  74. package/dist/gestures/gesture.js +7 -2
  75. package/dist/glass/GlassEffect.d.ts +2 -1
  76. package/dist/glass/GlassEffect.js +2 -1
  77. package/dist/glass/GlassPresentation.d.ts +1 -0
  78. package/dist/glass/GlassPresentation.js +21 -4
  79. package/dist/glass/TabBar.js +23 -1
  80. package/dist/glass/errorAlert.js +2 -1
  81. package/dist/glass/errors.d.ts +3 -1
  82. package/dist/glass/fallback/runtime.d.ts +1 -0
  83. package/dist/glass/fallback/runtime.js +8 -1
  84. package/dist/glass/lens/controlLift.d.ts +1 -1
  85. package/dist/glass/lens/controlLift.js +1 -1
  86. package/dist/glass/lens/interactivePress.d.ts +2 -2
  87. package/dist/glass/lens/interactivePress.js +2 -2
  88. package/dist/glass/lens/menu-opening-native-fits.json +1 -1
  89. package/dist/glass/lens/menuContextFit.json +1 -1
  90. package/dist/glass/lens/menuDropFit.json +1 -1
  91. package/dist/glass/lens/menuField.d.ts +2 -2
  92. package/dist/glass/lens/menuField.js +3 -3
  93. package/dist/glass/lens/menuGlyphDropFit.json +1 -1
  94. package/dist/glass/lens/menuMorphFit.json +1 -1
  95. package/dist/glass/lens/menuMorphPhoneFit.json +1 -1
  96. package/dist/glass/lens/tabAccessoryMotion.d.ts +1 -1
  97. package/dist/glass/lens/tabSearchMember.d.ts +1 -1
  98. package/dist/glass/lens/tabSearchMember.js +1 -1
  99. package/dist/glass/liquidContext.d.ts +2 -0
  100. package/dist/glass/menuPresenter.d.ts +2 -0
  101. package/dist/glass/menuPresenter.js +1 -1
  102. package/dist/glass/regions.d.ts +3 -2
  103. package/dist/glass/regions.js +1 -1
  104. package/dist/glass/renderSpecs.d.ts +1 -0
  105. package/dist/glass/renderSpecs.js +1 -0
  106. package/dist/glass/shapeTrack.d.ts +1 -1
  107. package/dist/glass/submenuMotion.d.ts +1 -1
  108. package/dist/glass/submenuMotion.js +1 -1
  109. package/dist/glass/webgpu/controlRim.d.ts +29 -0
  110. package/dist/glass/webgpu/controlRim.js +68 -0
  111. package/dist/glass/webgpu/entry.worker.js +3 -6
  112. package/dist/glass/webgpu/frameClock.d.ts +6 -2
  113. package/dist/glass/webgpu/frameClock.js +9 -2
  114. package/dist/glass/webgpu/frameProbe.d.ts +1 -1
  115. package/dist/glass/webgpu/frameProbe.js +3 -3
  116. package/dist/glass/webgpu/passes.d.ts +2 -2
  117. package/dist/glass/webgpu/production.d.ts +1 -0
  118. package/dist/glass/webgpu/production.js +31 -1
  119. package/dist/glass/webgpu/raster.d.ts +18 -0
  120. package/dist/glass/webgpu/raster.js +105 -15
  121. package/dist/glass/webgpu/rasterMaterial.d.ts +4 -0
  122. package/dist/glass/webgpu/rasterMaterial.js +17 -1
  123. package/dist/glass/webgpu/rasterUniforms.js +9 -0
  124. package/dist/glass/webgpu/regionScene.d.ts +4 -1
  125. package/dist/glass/webgpu/regionScene.js +30 -1
  126. package/dist/glass/webgpu/renderer.js +23 -7
  127. package/dist/glass/webgpu/shaders.d.ts +2 -2
  128. package/dist/glass/webgpu/shaders.js +5 -1
  129. package/dist/haptics/SensoryButton.d.ts +4 -4
  130. package/dist/haptics/SensoryButton.js +3 -3
  131. package/dist/image/Image.d.ts +6 -3
  132. package/dist/image/Image.js +4 -3
  133. package/dist/image/asset.d.ts +2 -1
  134. package/dist/keyboard/adapter.d.ts +3 -4
  135. package/dist/keyboard/adapter.js +2 -2
  136. package/dist/keyboard/avoidance.d.ts +1 -1
  137. package/dist/keyboard/avoidance.js +1 -1
  138. package/dist/keyboard/geometry.d.ts +1 -1
  139. package/dist/keyboard/motion.d.ts +1 -1
  140. package/dist/keyboard/motion.js +1 -1
  141. package/dist/list-form/ListForm.d.ts +10 -9
  142. package/dist/list-form/ListForm.js +3 -3
  143. package/dist/list-form/ListItem.js +1 -1
  144. package/dist/list-form/RowLabel.d.ts +1 -1
  145. package/dist/list-form/RowLabel.js +1 -1
  146. package/dist/list-form/Slider.js +1 -1
  147. package/dist/list-form/editMotion.js +1 -1
  148. package/dist/list-form/editing.d.ts +10 -11
  149. package/dist/list-form/editing.js +5 -6
  150. package/dist/list-form/offsets.d.ts +2 -2
  151. package/dist/list-form/offsets.js +2 -2
  152. package/dist/media/react.d.ts +2 -1
  153. package/dist/media/react.js +2 -1
  154. package/dist/media/types.d.ts +6 -3
  155. package/dist/motion/TransitionView.d.ts +4 -2
  156. package/dist/motion/TransitionView.js +3 -2
  157. package/dist/motion/animationValue.d.ts +2 -2
  158. package/dist/motion/animationValue.js +9 -10
  159. package/dist/motion/matchedGeometry.d.ts +10 -9
  160. package/dist/motion/matchedGeometry.js +9 -9
  161. package/dist/motion/springTrack.d.ts +1 -2
  162. package/dist/motion/springTrack.js +10 -5
  163. package/dist/motion/transition.d.ts +4 -1
  164. package/dist/motion/transition.js +2 -0
  165. package/dist/navigation/DetailList.d.ts +1 -1
  166. package/dist/navigation/DetailList.js +1 -1
  167. package/dist/navigation/ScreenContainer.js +1 -1
  168. package/dist/navigation/Sidebar.d.ts +3 -1
  169. package/dist/navigation/Sidebar.js +1 -1
  170. package/dist/navigation/SidebarSymbol.d.ts +2 -1
  171. package/dist/navigation/SidebarSymbol.js +2 -1
  172. package/dist/navigation/chromeCurves.js +1 -1
  173. package/dist/navigation/navigationContext.d.ts +2 -1
  174. package/dist/navigation/navigationContext.js +2 -1
  175. package/dist/navigation/splitContext.d.ts +2 -1
  176. package/dist/navigation/splitContext.js +2 -1
  177. package/dist/navigation/toolbarMinimization.d.ts +4 -3
  178. package/dist/navigation/types.d.ts +8 -6
  179. package/dist/navigation/zoomGeometry.js +1 -1
  180. package/dist/navigation/zoomMotion.d.ts +1 -1
  181. package/dist/navigation/zoomMotion.js +2 -2
  182. package/dist/navigation/zoomTransition.js +3 -4
  183. package/dist/presentation/Presentation.d.ts +10 -9
  184. package/dist/presentation/Presentation.js +4 -3
  185. package/dist/presentation/model.d.ts +2 -1
  186. package/dist/presentation/presentationSource.js +1 -1
  187. package/dist/press/Pressable.d.ts +5 -1
  188. package/dist/press/Pressable.js +2 -0
  189. package/dist/press/stateMachine.d.ts +1 -1
  190. package/dist/public/Label.d.ts +2 -1
  191. package/dist/public/Label.js +2 -1
  192. package/dist/public/Screen.d.ts +4 -6
  193. package/dist/public/actions.d.ts +2 -1
  194. package/dist/public/actions.js +2 -1
  195. package/dist/public/buttonBorderShape.d.ts +2 -1
  196. package/dist/public/colors.d.ts +7 -4
  197. package/dist/public/colors.js +3 -2
  198. package/dist/public/controlStyle.d.ts +17 -14
  199. package/dist/public/controls.d.ts +29 -28
  200. package/dist/public/controls.js +25 -24
  201. package/dist/public/deviceActions.d.ts +6 -4
  202. package/dist/public/deviceActions.js +4 -2
  203. package/dist/public/environment.d.ts +16 -13
  204. package/dist/public/environment.js +11 -9
  205. package/dist/public/filePickers.d.ts +6 -4
  206. package/dist/public/filePickers.js +3 -2
  207. package/dist/public/glass.d.ts +8 -6
  208. package/dist/public/glass.js +2 -2
  209. package/dist/public/glassValue.d.ts +3 -4
  210. package/dist/public/glassValue.js +3 -4
  211. package/dist/public/keyboard.d.ts +4 -1
  212. package/dist/public/keyboard.js +2 -1
  213. package/dist/public/navigationTransition.d.ts +6 -6
  214. package/dist/public/navigationTransition.js +3 -3
  215. package/dist/public/presentation.d.ts +8 -7
  216. package/dist/public/presentation.js +5 -5
  217. package/dist/public/runtime.d.ts +9 -8
  218. package/dist/public/runtime.js +7 -7
  219. package/dist/public/scroll.d.ts +6 -3
  220. package/dist/public/scroll.js +3 -1
  221. package/dist/public/structure.d.ts +2 -2
  222. package/dist/public/toolbar.d.ts +15 -11
  223. package/dist/public/toolbar.js +13 -8
  224. package/dist/reachability/model.d.ts +4 -3
  225. package/dist/reachability/react.d.ts +4 -1
  226. package/dist/reachability/react.js +2 -1
  227. package/dist/root/SatinRoot.d.ts +3 -3
  228. package/dist/root/SatinRoot.js +12 -36
  229. package/dist/root/mountSatin.d.ts +1 -1
  230. package/dist/root/mountSatin.js +26 -35
  231. package/dist/root/runtimeContext.d.ts +2 -1
  232. package/dist/root/runtimeContext.js +2 -1
  233. package/dist/safe-area/Overlay.d.ts +3 -2
  234. package/dist/safe-area/Overlay.js +2 -1
  235. package/dist/safe-area/SafeAreaInset.d.ts +11 -7
  236. package/dist/safe-area/SafeAreaInset.js +10 -9
  237. package/dist/safe-area/context.d.ts +3 -3
  238. package/dist/safe-area/context.js +1 -1
  239. package/dist/safe-area/displayCorners.d.ts +4 -2
  240. package/dist/safe-area/displayCorners.js +4 -2
  241. package/dist/safe-area/modifiers.d.ts +14 -10
  242. package/dist/safe-area/modifiers.js +10 -6
  243. package/dist/safe-area/useSafeAreaMetrics.js +2 -2
  244. package/dist/scroll/ScrollSurface.d.ts +5 -5
  245. package/dist/scroll/ScrollSurface.js +6 -2
  246. package/dist/scroll/ScrollTarget.d.ts +1 -1
  247. package/dist/scroll/ScrollTarget.js +1 -1
  248. package/dist/scroll/edgePolicy.d.ts +2 -2
  249. package/dist/scroll/position.d.ts +5 -3
  250. package/dist/scroll/position.js +1 -1
  251. package/dist/scroll/screenGeometry.d.ts +1 -1
  252. package/dist/scroll/screenGeometry.js +1 -1
  253. package/dist/scroll/types.d.ts +1 -1
  254. package/dist/search/PadSearchDrawer.d.ts +7 -0
  255. package/dist/search/PadSearchDrawer.js +8 -0
  256. package/dist/search/PadSearchPresentationContext.d.ts +6 -0
  257. package/dist/search/PadSearchPresentationContext.js +4 -0
  258. package/dist/search/SearchDrawer.d.ts +1 -1
  259. package/dist/search/SearchDrawer.js +7 -2
  260. package/dist/search/SearchSuggestionsPopover.d.ts +6 -0
  261. package/dist/search/SearchSuggestionsPopover.js +42 -0
  262. package/dist/search/SearchToolbarField.d.ts +2 -1
  263. package/dist/search/SearchToolbarField.js +28 -7
  264. package/dist/search/padSearchBarMotion.d.ts +69 -0
  265. package/dist/search/padSearchBarMotion.js +73 -0
  266. package/dist/search/padSearchFieldSamples.json +491 -0
  267. package/dist/search/searchMotion.d.ts +1 -1
  268. package/dist/search/searchMotion.js +1 -1
  269. package/dist/search/searchPlacement.d.ts +3 -0
  270. package/dist/search/searchPlacement.js +7 -0
  271. package/dist/search/searchable.d.ts +14 -12
  272. package/dist/search/usePadSearchFieldMotion.d.ts +4 -0
  273. package/dist/search/usePadSearchFieldMotion.js +100 -0
  274. package/dist/search/useSearchPresentation.d.ts +2 -1
  275. package/dist/search/useSearchPresentation.js +24 -6
  276. package/dist/server.d.ts +4 -4
  277. package/dist/server.js +4 -4
  278. package/dist/shapes/corners.d.ts +3 -3
  279. package/dist/shapes/corners.js +2 -3
  280. package/dist/styles.css +8302 -8236
  281. package/dist/sw/vite.d.ts +4 -0
  282. package/dist/sw/vite.js +2 -0
  283. package/dist/symbols/Symbol.d.ts +7 -5
  284. package/dist/symbols/Symbol.js +5 -3
  285. package/dist/symbols/catalogue.d.ts +1 -1
  286. package/dist/symbols/types.d.ts +6 -4
  287. package/dist/tabs/TabSection.d.ts +3 -2
  288. package/dist/tabs/TabSection.js +2 -1
  289. package/dist/tabs/TabView.js +54 -51
  290. package/dist/tasks/useTask.d.ts +5 -4
  291. package/dist/tasks/useTask.js +4 -3
  292. package/dist/testing.d.ts +3 -2
  293. package/dist/testing.js +3 -2
  294. package/dist/text-input/TextEditor.js +7 -3
  295. package/dist/text-input/focusChain.d.ts +4 -3
  296. package/dist/text-input/focusChain.js +3 -2
  297. package/dist/text-input/kinds.d.ts +1 -1
  298. package/dist/web/WebView.d.ts +10 -3
  299. package/dist/web/WebView.js +3 -2
  300. package/dist/web/authentication.d.ts +12 -1
  301. package/dist/web/authentication.js +2 -1
  302. package/dist/web/openURL.d.ts +10 -4
  303. package/dist/web/openURL.js +4 -2
  304. package/dist/web/useWebAuthentication.d.ts +2 -1
  305. package/dist/web/useWebAuthentication.js +2 -1
  306. package/dist/zoom/Zoomable.d.ts +3 -3
  307. package/dist/zoom/Zoomable.js +2 -2
  308. package/package.json +4 -4
  309. 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.
@@ -1,6 +1,6 @@
1
1
  # Geometry and appearance tokens
2
2
 
3
- Load `@satinhq/kit/styles.css` once. 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.
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 scoped gesture region. Scroll and navigation retain their own ownership.
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 scoped gesture region. Scroll and navigation retain their own ownership.
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
- /** A React value plus a gesture-owned update/reset binding. Cancellation and release
41
- * restore the initial value; no mutable proxy is exposed to application code. */
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 {};
@@ -1,7 +1,7 @@
1
1
  import { useMemo, useRef, useState } from 'react';
2
2
  import { deferGesture, failGesture, recognizeGesture, } from './arena.js';
3
- /** A React value plus a gesture-owned update/reset binding. Cancellation and release
4
- * restore the initial value; no mutable proxy is exposed to application code. */
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
- /** `GlassEffectContainer`: groups related glass outlines into one field. */
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
- /** `GlassEffectContainer`: groups related glass outlines into one field. */
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 ? dragFrames.capsule.y : height - GLASS_DOCK_BOTTOM_GAP_PX - fallbackDockHeight);
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
- useEffect(() => {
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
@@ -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'
@@ -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
- if (document.getElementById('satin-launch-error')) {
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
  }
@@ -1,5 +1,7 @@
1
- /** Serializable across the renderer worker boundary. Release failures may select the fallback material. */
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;
@@ -7,6 +7,7 @@ export declare class FallbackGlassRuntime {
7
7
  private regions;
8
8
  private dock;
9
9
  private thumb;
10
+ private padSearch?;
10
11
  private minimize;
11
12
  private minimizeDom;
12
13
  private tab;
@@ -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 stock SwiftUI controls
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 stock SwiftUI controls
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 Liquid Glass (buttons, bar and toolbar buttons, the
4
- * back button, Glass.interactive() surfaces). Fitted 2026-09-25 to stock SwiftUI on the iOS
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 Liquid Glass (buttons, bar and toolbar buttons, the
5
- * back button, Glass.interactive() surfaces). Fitted 2026-09-25 to stock SwiftUI on the iOS
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 SwiftUI label frame is 44 pt."
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 SwiftUI .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)."
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": "Stock SwiftUI Menu(\"Menu selection\") in a List Section (native reference app), iPadOS 27.0, light",
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, stock SwiftUI toolbar Menu
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, stock SwiftUI Menu from a labelled capsule
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, stock SwiftUI toolbar Menu
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, stock SwiftUI Menu from a labelled capsule
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): stock SwiftUI
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: stock SwiftUI Menu { … } label: { Image(systemName: \"ellipsis\") } (no glass), iOS 27.0 Simulator, iPhone 17 Pro Max, light; raw/pmm-light-1.mp4",
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, stock SwiftUI toolbar Menu, black fixture",
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",