@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
package/dist/sw/vite.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import type { Plugin } from 'vite';
2
+ /** Options for the service worker build plugin: extra files to precache, public routes, legacy
3
+ * cache names and diagnostic headers. */
2
4
  export interface SatinAppShellPluginOptions {
3
5
  readonly mediaProbe?: string;
4
6
  readonly shellFile?: string;
@@ -12,6 +14,8 @@ export interface SatinAppShellPluginOptions {
12
14
  /** Explicit diagnostic build only; production serves cached responses unchanged. */
13
15
  readonly diagnosticTimingHeaders?: boolean;
14
16
  }
17
+ /** The Vite build plugin that generates the app's service worker and precache list. `satinApp`
18
+ * includes it; use it directly only in Satin tooling. */
15
19
  export declare function satinAppShell(options?: SatinAppShellPluginOptions): Plugin;
16
20
  export { satinApp, satinAppFixtures, type SatinAppOptions } from '../app/vite.js';
17
21
  export { defineApp } from '../public/definition.js';
package/dist/sw/vite.js CHANGED
@@ -2,6 +2,8 @@ import { copyFile, mkdir } from 'node:fs/promises';
2
2
  import { resolve } from 'node:path';
3
3
  import { APP_SHELL_VERSION_FILE } from './protocol.js';
4
4
  import { generateAppShell } from './generator.js';
5
+ /** The Vite build plugin that generates the app's service worker and precache list. `satinApp`
6
+ * includes it; use it directly only in Satin tooling. */
5
7
  export function satinAppShell(options = {}) {
6
8
  let config;
7
9
  let outputWritten = false;
@@ -1,11 +1,12 @@
1
1
  import { type ReactNode, type SVGProps } from 'react';
2
2
  import { type CoreSymbolName } from './catalogue.js';
3
3
  import type { SymbolGlyph, SymbolSource, SymbolWeight, SymbolScale, SymbolRenderingMode } from './types.js';
4
- /** A symbol name in SF Symbols style (`'plus'`, `'trash.fill'`). The bundled names autocomplete;
5
- * any string is accepted for glyphs added through `SymbolProvider`. */
4
+ /** A symbol name, such as `'plus'` or `'trash.fill'`. The bundled names autocomplete; any string
5
+ * is accepted for glyphs added through `SymbolProvider`. */
6
6
  export type SymbolName = CoreSymbolName | (string & {});
7
7
  export declare const SymbolSelectionContext: import("react").Context<boolean>;
8
- /** Extend or override glyphs locally. Paths must be original or redistributable with provenance. */
8
+ /** Adds or replaces symbol glyphs for everything inside it. Paths must be original, or
9
+ * redistributable with their provenance recorded. */
9
10
  export declare function SymbolProvider({ glyphs, children, }: {
10
11
  glyphs: SymbolSource;
11
12
  children: ReactNode;
@@ -24,8 +25,9 @@ export type SymbolImageProps = Omit<SVGProps<SVGSVGElement>, 'name' | 'children'
24
25
  glyph?: SymbolGlyph;
25
26
  };
26
27
  /**
27
- * `Image(systemName:)`: a vector symbol that sizes with the surrounding font and takes its
28
- * colour. A `.fill` variant is chosen automatically where the context selects it (a selected tab).
28
+ * Draws a named vector symbol that sizes with the surrounding font and takes its colour. A
29
+ * `.fill` variant is chosen automatically where the context calls for it, such as a selected
30
+ * tab.
29
31
  *
30
32
  * @example
31
33
  * <SymbolImage name="star.fill" />
@@ -6,7 +6,8 @@ import { symbolGeometry, symbolNormalization, symbolNormalizationTransform, symb
6
6
  import { useInheritedSymbolWeight } from './inheritedWeight.js';
7
7
  const SourceContext = createContext(coreSymbols);
8
8
  export const SymbolSelectionContext = createContext(false);
9
- /** Extend or override glyphs locally. Paths must be original or redistributable with provenance. */
9
+ /** Adds or replaces symbol glyphs for everything inside it. Paths must be original, or
10
+ * redistributable with their provenance recorded. */
10
11
  export function SymbolProvider({ glyphs, children, }) {
11
12
  const inherited = useContext(SourceContext);
12
13
  return _jsx(SourceContext, { value: { ...inherited, ...glyphs }, children: children });
@@ -20,8 +21,9 @@ export function resolveSymbol(source, name, selected = false) {
20
21
  return Object.hasOwn(source, name) ? source[name] : undefined;
21
22
  }
22
23
  /**
23
- * `Image(systemName:)`: a vector symbol that sizes with the surrounding font and takes its
24
- * colour. A `.fill` variant is chosen automatically where the context selects it (a selected tab).
24
+ * Draws a named vector symbol that sizes with the surrounding font and takes its colour. A
25
+ * `.fill` variant is chosen automatically where the context calls for it, such as a selected
26
+ * tab.
25
27
  *
26
28
  * @example
27
29
  * <SymbolImage name="star.fill" />
@@ -1,4 +1,4 @@
1
1
  import type { SymbolGlyph } from './types.js';
2
- /** The core symbol name contract used by public app declarations. */
2
+ /** Every symbol name in the bundled catalogue. */
3
3
  export type CoreSymbolName = "pin" | "pin.fill" | "pin.slash" | "pin.slash.fill" | "star" | "star.fill" | "house" | "house.fill" | "bell" | "bell.fill" | "person" | "person.fill" | "calendar" | "clock" | "clock.fill" | "trash" | "trash.fill" | "heart" | "heart.fill" | "folder" | "folder.fill" | "pencil" | "checkmark" | "checkmark.circle" | "checkmark.circle.fill" | "xmark" | "xmark.circle" | "xmark.circle.fill" | "info.circle" | "info.circle.fill" | "ellipsis" | "ellipsis.circle" | "ellipsis.circle.fill" | "plus" | "minus" | "chevron.left" | "chevron.right" | "chevron.up" | "chevron.down" | "circle" | "circle.fill" | "chart.bar" | "chart.bar.fill" | "photo" | "photo.fill" | "envelope" | "envelope.fill" | "bookmark" | "bookmark.fill" | "timer" | "timer.fill" | "stopwatch" | "stopwatch.fill" | "book" | "book.fill" | "doc.text" | "doc.text.fill" | "rectangle.stack" | "rectangle.stack.fill" | "slider.horizontal.3" | "square.and.arrow.up" | "square.and.arrow.up.fill" | "square.and.pencil" | "square.and.pencil.fill" | "gearshape" | "gearshape.fill" | "magnifyingglass" | "sidebar.left" | "arrowshape.turn.up.left" | "arrowshape.turn.up.left.fill" | "chevron.up.chevron.down" | "questionmark.square" | "questionmark.square.fill" | "play" | "play.fill" | "pause" | "pause.fill" | "arrow.clockwise" | "bolt" | "bolt.fill" | "flag" | "flag.fill" | "tray" | "tray.fill" | "cart" | "cart.fill" | "list.bullet" | "person.crop.circle" | "person.crop.circle.fill" | "dot.radiowaves.left.right" | "square.grid.2x2" | "square.grid.2x2.fill" | "battery.100" | "camera" | "camera.fill" | "car" | "device.phone.portrait" | "globe" | "keyboard" | "lock" | "lock.fill" | "moon" | "moon.fill" | "hand.raised" | "hand.raised.fill" | "sparkles" | "textformat" | "hourglass" | "arrow.up" | "arrow.down" | "arrow.down.circle" | "arrow.down.circle.fill" | "stop" | "stop.fill" | "doc.on.doc" | "doc.on.doc.fill" | "face.smiling" | "face.smiling.fill" | "bell.slash" | "bell.slash.fill" | "envelope.open" | "envelope.open.fill" | "envelope.badge" | "envelope.badge.fill" | "hand.thumbsup" | "hand.thumbsup.fill" | "hand.thumbsdown" | "hand.thumbsdown.fill" | "line.3.horizontal" | "line.3.horizontal.decrease" | "photo.on.rectangle" | "square.stack" | "square.stack.fill" | "forward" | "forward.fill" | "backward" | "backward.fill" | "shuffle" | "repeat" | "repeat.1" | "music.note" | "music.note.list" | "music.mic" | "music.house" | "music.house.fill" | "speaker" | "speaker.fill" | "speaker.wave.3" | "speaker.wave.3.fill" | "quote.bubble" | "quote.bubble.fill" | "tv.music.note" | "tv.music.note.fill" | "waveform" | "play.circle" | "play.circle.fill" | "play.rectangle" | "play.rectangle.fill" | "plus.circle" | "plus.circle.fill" | "checkmark.square" | "checkmark.square.fill" | "heart.slash" | "heart.slash.fill" | "flag.slash" | "flag.slash.fill" | "person.2" | "person.2.fill" | "arrowshape.turn.up.right" | "arrowshape.turn.up.right.fill" | "bubble.left.and.bubble.right" | "bubble.left.and.bubble.right.fill" | "text.insert" | "text.append" | "text.badge.plus" | "viewfinder" | "sun.max" | "sun.max.fill" | "gift" | "gift.fill" | "airplane" | "pawprint" | "briefcase" | "briefcase.fill" | "list.number" | "dot.radiowaves.left.and.right" | "exclamationmark.triangle" | "exclamationmark.triangle.fill" | "arrow.right" | "arrow.left" | "square.and.arrow.down" | "square.and.arrow.down.fill" | "gear" | "link" | "arrow.counterclockwise" | "arrow.uturn.backward" | "arrow.uturn.forward" | "archivebox" | "archivebox.fill" | "line.3.horizontal.decrease.circle" | "line.3.horizontal.decrease.circle.fill" | "questionmark.circle" | "questionmark.circle.fill" | "arrow.up.arrow.down" | "tag" | "tag.fill" | "paperplane" | "paperplane.fill" | "flame" | "flame.fill" | "location" | "location.fill" | "doc" | "doc.fill" | "mappin" | "mappin.and.ellipse" | "arrow.up.right" | "at" | "drop" | "drop.fill" | "headphones" | "bed.double" | "bed.double.fill" | "pause.circle" | "pause.circle.fill" | "display" | "dollarsign.circle" | "dollarsign.circle.fill" | "checkmark.seal" | "checkmark.seal.fill" | "tray.full" | "tray.full.fill" | "person.badge.plus" | "person.badge.plus.fill" | "folder.badge.plus" | "arrow.2.squarepath" | "dumbbell" | "figure.run" | "leaf" | "clock.arrow.circlepath" | "receipt" | "airplayaudio" | "bell.badge.fill" | "bell.badge" | "bicycle" | "book.closed.fill" | "book.closed" | "brain.head.profile" | "calendar.badge.clock" | "chart.line.uptrend.xyaxis" | "checklist" | "cross.case.fill" | "cross.case" | "cup.and.saucer.fill" | "cup.and.saucer" | "dumbbell.fill" | "exclamationmark.2" | "eye.fill" | "eye.slash.fill" | "eye.slash" | "eye" | "fork.knife" | "graduationcap.fill" | "graduationcap" | "key.fill" | "key" | "leaf.fill" | "list.bullet.clipboard.fill" | "list.bullet.clipboard" | "note.text.fill" | "note.text" | "pill.fill" | "pill" | "receipt.fill" | "scalemass.fill" | "scalemass" | "server.rack.fill" | "server.rack";
4
4
  export declare const coreSymbols: Readonly<Record<CoreSymbolName, SymbolGlyph>>;
@@ -1,5 +1,6 @@
1
1
  import type { FontWeight } from '../content/content.js';
2
- /** Filled paths on Framework7's 56-unit grid. No font, HTML or external resource. */
2
+ /** A symbol's artwork: filled SVG paths on a 56-unit grid, with optional layers for hierarchical
3
+ * and palette rendering, and its provenance. No font, HTML or external resource. */
3
4
  export type SymbolGlyph = Readonly<{
4
5
  paths: readonly Readonly<{
5
6
  d: string;
@@ -22,9 +23,10 @@ export type SymbolGlyph = Readonly<{
22
23
  }>;
23
24
  /** Glyphs by symbol name, for `SymbolProvider`. */
24
25
  export type SymbolSource = Readonly<Record<string, SymbolGlyph>>;
25
- /** `Font.Weight` as applied to a symbol. */
26
+ /** The weight a symbol is drawn at. It uses the same names as font weights. */
26
27
  export type SymbolWeight = FontWeight;
27
- /** `Image.Scale`. */
28
+ /** A symbol's size relative to the text around it: `small`, `medium` or `large`. */
28
29
  export type SymbolScale = 'small' | 'medium' | 'large';
29
- /** `SymbolRenderingMode`. */
30
+ /** How a symbol's layers are coloured: `monochrome` in one colour, `hierarchical` in shades of
31
+ * one colour, or `palette` with a colour per layer. */
30
32
  export type SymbolRenderingMode = 'monochrome' | 'hierarchical' | 'palette';
@@ -1,5 +1,5 @@
1
1
  import type { ReactNode } from 'react';
2
- /** Groups destinations in the adaptable sidebar without creating another navigator. */
2
+ /** A sidebar group's title, optional id and placement, and its `Tab` children. */
3
3
  export type TabSectionProps = Readonly<{
4
4
  title: string;
5
5
  /** Stable section identity when titles change or two sections have the same title. */
@@ -8,5 +8,6 @@ export type TabSectionProps = Readonly<{
8
8
  tabPlacement?: 'sidebarOnly';
9
9
  children: ReactNode;
10
10
  }>;
11
- /** A collapsible sidebar group. TabView consumes this declaration. */
11
+ /** Groups `Tab`s under a collapsible header in the `TabView` sidebar. It adds no navigator of its
12
+ * own. */
12
13
  export declare function TabSection({ children }: TabSectionProps): ReactNode;
@@ -1,4 +1,5 @@
1
- /** A collapsible sidebar group. TabView consumes this declaration. */
1
+ /** Groups `Tab`s under a collapsible header in the `TabView` sidebar. It adds no navigator of its
2
+ * own. */
2
3
  export function TabSection({ children }) {
3
4
  return children;
4
5
  }
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { PadSearchPresentationContext, PadSearchTabContext, } from '../search/PadSearchPresentationContext.js';
2
3
  import { TabSidebarSurface } from './TabSidebarSurface.js';
3
4
  import { ScrollSurface } from '../scroll/ScrollSurface.js';
4
5
  import { TabSection } from './TabSection.js';
@@ -53,6 +54,8 @@ function RetainedTab({ active, presented, id, children, register, focusOnActivat
53
54
  }
54
55
  /** Lazy, retained tabs. Hidden app effects remain app-owned; Satin work pauses. */
55
56
  export function TabView({ children, selection, defaultSelection, onSelectionChange, bottomAccessory, bottomAccessoryMaterial = 'glass', tabViewStyle = 'automatic', }) {
57
+ const [searchOwner, setSearchOwner] = useState(null);
58
+ const searchPresentation = useMemo(() => ({ owner: searchOwner, setOwner: setSearchOwner }), [searchOwner]);
56
59
  const layout = useLayoutClass();
57
60
  const { metrics } = useSatinLayoutRuntime();
58
61
  const canShowSidebar = tabViewStyle === 'sidebarAdaptable' &&
@@ -204,55 +207,55 @@ export function TabView({ children, selection, defaultSelection, onSelectionChan
204
207
  if (overlaySidebar)
205
208
  changeSidebar(false);
206
209
  }, children: [item.role === 'search' ? (_jsx(SymbolImage, { name: "magnifyingglass" })) : item.symbol ? (_jsx(SymbolImage, { name: item.symbol })) : (item.icon), _jsx("span", { children: item.label })] }, item.id));
207
- return (_jsxs("div", { ref: container, "data-satin-tab-view": "true", "data-satin-tab-sidebar-expanded": showSidebar || undefined, "data-satin-tab-sidebar-overlay": overlaySidebar || undefined, "data-satin-tab-sidebar-collapsed-overlay": didCollapseOverlay || undefined, "data-satin-tab-sidebar-expanded-from-tabs": (showSidebar && !overlaySidebar && expandedFromTabs) || undefined, style: didCollapseOverlay
208
- ? {
209
- '--satin-tab-sidebar-return-inset': `${metrics.physicalInsets.top}px`,
210
- }
211
- : undefined, children: [showSidebar && (_jsxs(TabSidebarSurface, { overlay: overlaySidebar, onDismiss: () => changeSidebar(false), children: [_jsx("div", { "data-satin-tab-sidebar-toggle-host": "true", children: _jsx(GlassButton, { "data-satin-tab-sidebar-hide": "true", iconOnly: true, "aria-label": "Hide Tab Sidebar", onPress: () => changeSidebar(false), children: _jsx(SymbolImage, { name: "tab-sidebar-collapse", glyph: tabSidebarCollapseGlyph, width: 25.3, height: 25.3 }) }) }), _jsxs(ScrollSurface, { id: `${scopeId}-sidebar-scroll`, primary: false, role: "tablist", "aria-orientation": "vertical", onKeyDown: handleTabKeyDown, children: [sidebarItems
212
- .filter(item => item.role === 'search' || !grouped.has(item.id))
213
- .map(sidebarRow), sections.map(section => {
214
- const expanded = !collapsedSections.has(section.id);
215
- const groupId = `${scopeId}-section-${encodeURIComponent(section.id)}`;
216
- return (_jsxs("div", { "data-satin-tab-section": "true", children: [_jsxs(Pressable, { "aria-label": section.title, "aria-expanded": expanded, "aria-controls": groupId, "data-satin-tab-section-header": "true", onPress: () => setCollapsedSections(previous => {
217
- const next = new Set(previous);
218
- if (expanded)
219
- next.add(section.id);
220
- else
221
- next.delete(section.id);
222
- return next;
223
- }), children: [_jsx("span", { children: section.title }), _jsx(SymbolImage, { name: expanded ? 'chevron.down' : 'chevron.right' })] }), _jsx("div", { id: groupId, role: "group", "aria-label": section.title, hidden: !expanded, children: sidebarItems
224
- .filter(item => item.role !== 'search' && section.tabs.includes(item.id))
225
- .map(sidebarRow) })] }, section.id));
226
- })] })] })), _jsx(SafeAreaInset, { inert: showSidebar && overlaySidebar, edge: "bottom", spacing: 0, content: _jsxs("div", { ref: bottomChrome, "data-satin-tab-bottom-chrome": readDeviceIdiom() === 'phone' ? 'phone' : 'pad',
227
- // Search's compact hit target uses the full dock, even when its resting bar hugs
228
- // two or three items. Only an actual accessory makes that resting bar full-width.
229
- style: (bottomAccessory != null || searchTab) && readDeviceIdiom() === 'phone'
230
- ? {
231
- width: glassDockWidth(metrics.layoutViewport.width, metrics.screen, metrics.insets, metrics.layoutViewport.height),
232
- marginInline: 'auto',
233
- transform: `translateX(${(metrics.insets.left - metrics.insets.right) / 2}px)`,
234
- }
235
- : undefined, children: [bottomAccessory != null && (_jsx("div", { ref: accessoryContact, "data-satin-tab-bottom-accessory": "true",
236
- // Native iPad: 24 pt edge margins, 48 pt minimum row. In portrait
237
- // only this floating chrome clears the 280 pt modal sidebar;
238
- // landscape's parent already owns the native sidebar column.
239
- style: readDeviceIdiom() === 'pad'
240
- ? {
241
- marginLeft: showSidebar && overlaySidebar ? 304 : 24,
242
- marginRight: 24,
243
- // The installed iPad can report a 25 pt system inset.
244
- // Compensate it fully to retain the native 24 pt edge gap.
245
- marginBottom: 24 - metrics.physicalInsets.bottom,
246
- minHeight: 48,
247
- }
248
- : undefined, children: _jsx("div", { "data-satin-accessory-clip": "true", children: _jsx("div", { "data-satin-accessory-content": "true", children: bottomAccessoryMaterial === 'custom' ? (bottomAccessory) : (_jsx(GlassEffect, { style: { width: '100%', height: '100%' }, children: bottomAccessory })) }) }) })), selected !== undefined && (!showSidebar || overlaySidebar) && (_jsx(TabBarAccessoryContext, { value: bottomAccessory != null, children: _jsx(TabBar, { inert: showSidebar && overlaySidebar, "aria-hidden": (showSidebar && overlaySidebar) || undefined, style: showSidebar && overlaySidebar ? { visibility: 'hidden' } : undefined, items: barItems, selected: selected, onSelect: select, onShowSidebar: canShowSidebar ? () => changeSidebar(true) : undefined }) })), searchTab && (_jsx("div", { "data-satin-tab-compact-search": "true", children: _jsx("button", { type: "button", "aria-label": searchTab.accessibilityLabel ?? searchTab.label, disabled: searchTab.disabled, onClick: () => {
249
- restoreCompactTabBar(bottomChrome.current);
250
- // A selected Search shortcut reforms the bar without reselecting its stack.
251
- if (selected !== searchTab.id)
252
- select(searchTab.id);
253
- } }) }))] }), children: tabs.map((tab, index) => (_jsx(RetainedTab, { panelId: items[index].panelId, tabId: showSidebar
254
- ? `${items[index].tabId}-sidebar`
255
- : barItems.some(item => item.id === tab.id)
256
- ? items[index].tabId
257
- : undefined, label: tab.label, focusOnActivate: tab.id !== initialSelection, id: tab.id, active: tab.id === requested, presented: tab.id === selected, register: register, children: mounted.includes(tab.id) ? tab.children : null }, tab.id))) })] }));
210
+ return (_jsx(PadSearchPresentationContext, { value: searchPresentation, children: _jsxs("div", { ref: container, "data-satin-tab-view": "true", "data-satin-tab-sidebar-expanded": showSidebar || undefined, "data-satin-tab-sidebar-overlay": overlaySidebar || undefined, "data-satin-tab-sidebar-collapsed-overlay": didCollapseOverlay || undefined, "data-satin-tab-sidebar-expanded-from-tabs": (showSidebar && !overlaySidebar && expandedFromTabs) || undefined, style: didCollapseOverlay
211
+ ? {
212
+ '--satin-tab-sidebar-return-inset': `${metrics.physicalInsets.top}px`,
213
+ }
214
+ : undefined, children: [showSidebar && (_jsxs(TabSidebarSurface, { overlay: overlaySidebar, onDismiss: () => changeSidebar(false), children: [_jsx("div", { "data-satin-tab-sidebar-toggle-host": "true", children: _jsx(GlassButton, { "data-satin-tab-sidebar-hide": "true", iconOnly: true, "aria-label": "Hide Tab Sidebar", onPress: () => changeSidebar(false), children: _jsx(SymbolImage, { name: "tab-sidebar-collapse", glyph: tabSidebarCollapseGlyph, width: 25.3, height: 25.3 }) }) }), _jsxs(ScrollSurface, { id: `${scopeId}-sidebar-scroll`, primary: false, role: "tablist", "aria-orientation": "vertical", onKeyDown: handleTabKeyDown, children: [sidebarItems
215
+ .filter(item => item.role === 'search' || !grouped.has(item.id))
216
+ .map(sidebarRow), sections.map(section => {
217
+ const expanded = !collapsedSections.has(section.id);
218
+ const groupId = `${scopeId}-section-${encodeURIComponent(section.id)}`;
219
+ return (_jsxs("div", { "data-satin-tab-section": "true", children: [_jsxs(Pressable, { "aria-label": section.title, "aria-expanded": expanded, "aria-controls": groupId, "data-satin-tab-section-header": "true", onPress: () => setCollapsedSections(previous => {
220
+ const next = new Set(previous);
221
+ if (expanded)
222
+ next.add(section.id);
223
+ else
224
+ next.delete(section.id);
225
+ return next;
226
+ }), children: [_jsx("span", { children: section.title }), _jsx(SymbolImage, { name: expanded ? 'chevron.down' : 'chevron.right' })] }), _jsx("div", { id: groupId, role: "group", "aria-label": section.title, hidden: !expanded, children: sidebarItems
227
+ .filter(item => item.role !== 'search' && section.tabs.includes(item.id))
228
+ .map(sidebarRow) })] }, section.id));
229
+ })] })] })), _jsx(SafeAreaInset, { inert: showSidebar && overlaySidebar, edge: "bottom", spacing: 0, content: _jsxs("div", { ref: bottomChrome, "data-satin-tab-bottom-chrome": readDeviceIdiom() === 'phone' ? 'phone' : 'pad',
230
+ // Search's compact hit target uses the full dock, even when its resting bar hugs
231
+ // two or three items. Only an actual accessory makes that resting bar full-width.
232
+ style: (bottomAccessory != null || searchTab) && readDeviceIdiom() === 'phone'
233
+ ? {
234
+ width: glassDockWidth(metrics.layoutViewport.width, metrics.screen, metrics.insets, metrics.layoutViewport.height),
235
+ marginInline: 'auto',
236
+ transform: `translateX(${(metrics.insets.left - metrics.insets.right) / 2}px)`,
237
+ }
238
+ : undefined, children: [bottomAccessory != null && (_jsx("div", { ref: accessoryContact, "data-satin-tab-bottom-accessory": "true",
239
+ // Native iPad: 24 pt edge margins, 48 pt minimum row. In portrait
240
+ // only this floating chrome clears the 280 pt modal sidebar;
241
+ // landscape's parent already owns the native sidebar column.
242
+ style: readDeviceIdiom() === 'pad'
243
+ ? {
244
+ marginLeft: showSidebar && overlaySidebar ? 304 : 24,
245
+ marginRight: 24,
246
+ // The installed iPad can report a 25 pt system inset.
247
+ // Compensate it fully to retain the native 24 pt edge gap.
248
+ marginBottom: 24 - metrics.physicalInsets.bottom,
249
+ minHeight: 48,
250
+ }
251
+ : undefined, children: _jsx("div", { "data-satin-accessory-clip": "true", children: _jsx("div", { "data-satin-accessory-content": "true", children: bottomAccessoryMaterial === 'custom' ? (bottomAccessory) : (_jsx(GlassEffect, { style: { width: '100%', height: '100%' }, children: bottomAccessory })) }) }) })), selected !== undefined && (!showSidebar || overlaySidebar) && (_jsx(TabBarAccessoryContext, { value: bottomAccessory != null, children: _jsx(TabBar, { inert: showSidebar && overlaySidebar, "aria-hidden": (showSidebar && overlaySidebar) || undefined, style: showSidebar && overlaySidebar ? { visibility: 'hidden' } : undefined, items: barItems, selected: selected, onSelect: select, onShowSidebar: canShowSidebar ? () => changeSidebar(true) : undefined }) })), searchTab && (_jsx("div", { "data-satin-tab-compact-search": "true", children: _jsx("button", { type: "button", "aria-label": searchTab.accessibilityLabel ?? searchTab.label, disabled: searchTab.disabled, onClick: () => {
252
+ restoreCompactTabBar(bottomChrome.current);
253
+ // A selected Search shortcut reforms the bar without reselecting its stack.
254
+ if (selected !== searchTab.id)
255
+ select(searchTab.id);
256
+ } }) }))] }), children: tabs.map((tab, index) => (_jsx(RetainedTab, { panelId: items[index].panelId, tabId: showSidebar
257
+ ? `${items[index].tabId}-sidebar`
258
+ : barItems.some(item => item.id === tab.id)
259
+ ? items[index].tabId
260
+ : undefined, label: tab.label, focusOnActivate: tab.id !== initialSelection, id: tab.id, active: tab.id === requested, presented: tab.id === selected, register: register, children: _jsx(PadSearchTabContext, { value: tab.role === 'search' && tab.id === selected ? tab.id : null, children: mounted.includes(tab.id) ? tab.children : null }) }, tab.id))) })] }) }));
258
261
  }
@@ -2,15 +2,16 @@
2
2
  export type TaskCommit = (update: () => void) => boolean;
3
3
  /** The work `useTask` runs: pass `signal` to fetches and publish state through `commit`. */
4
4
  export type TaskAction = (signal: AbortSignal, commit: TaskCommit) => void | Promise<void>;
5
- /** `task(id:)`: a change of `id` cancels the current run and starts a new one. */
5
+ /** Options for `useTask`. A change of `id` cancels the current run and starts a new one. */
6
6
  export type TaskOptions = Readonly<{
7
7
  id?: unknown;
8
8
  }>;
9
9
  /** Cancels the current run and starts the task again; resolves when that run ends. */
10
10
  export type TaskRunner = () => Promise<void>;
11
11
  /**
12
- * A task tied to this view’s lifetime. Pass signal to fetch and publish results through
13
- * commit. Cancellation is cooperative; arbitrary setters outside commit cannot
14
- * be intercepted. The returned runner cancels and replaces the current run.
12
+ * Runs async work while its screen is showing. It cancels the run when the screen is covered,
13
+ * its tab is hidden or the component unmounts, and starts again when it shows. Pass `signal` to
14
+ * `fetch` and publish results through `commit`. An error the task throws reaches the nearest
15
+ * error boundary. The returned runner cancels and replaces the current run.
15
16
  */
16
17
  export declare function useTask(action: TaskAction, { id }?: TaskOptions): TaskRunner;
@@ -2,9 +2,10 @@ import { useCallback, useContext, useLayoutEffect, useRef, useState } from 'reac
2
2
  import { ScreenContext } from '../navigation/navigationContext.js';
3
3
  import { TabActivityContext } from '../tabs/context.js';
4
4
  /**
5
- * A task tied to this view’s lifetime. Pass signal to fetch and publish results through
6
- * commit. Cancellation is cooperative; arbitrary setters outside commit cannot
7
- * be intercepted. The returned runner cancels and replaces the current run.
5
+ * Runs async work while its screen is showing. It cancels the run when the screen is covered,
6
+ * its tab is hidden or the component unmounts, and starts again when it shows. Pass `signal` to
7
+ * `fetch` and publish results through `commit`. An error the task throws reaches the nearest
8
+ * error boundary. The returned runner cancels and replaces the current run.
8
9
  */
9
10
  export function useTask(action, { id } = {}) {
10
11
  const screen = useContext(ScreenContext);
package/dist/testing.d.ts CHANGED
@@ -1,6 +1,7 @@
1
- /** App-test assertion without exposing navigation engine objects. */
1
+ /** Throws unless the visible screen in `container` is the route `screen`. For app tests. */
2
2
  export declare function assertNavigation(container: ParentNode, screen: string): void;
3
- /** Explicit diagnostic action. The token is requested at call time and never persisted. */
3
+ /** Sends a test notification through a push relay and returns how many devices accepted it. The
4
+ * token is requested when called and never stored. */
4
5
  export declare function sendTestNotification(relay: {
5
6
  url: string;
6
7
  token: () => string | Promise<string>;
package/dist/testing.js CHANGED
@@ -1,11 +1,12 @@
1
1
  import { createPushRelayAdapter } from './push/notifications.js';
2
- /** App-test assertion without exposing navigation engine objects. */
2
+ /** Throws unless the visible screen in `container` is the route `screen`. For app tests. */
3
3
  export function assertNavigation(container, screen) {
4
4
  const active = container.querySelector('[data-satin-screen-container][data-satin-covered="false"]');
5
5
  if (active?.getAttribute('data-satin-screen') !== screen)
6
6
  throw new Error(`Expected active route ${screen}`);
7
7
  }
8
- /** Explicit diagnostic action. The token is requested at call time and never persisted. */
8
+ /** Sends a test notification through a push relay and returns how many devices accepted it. The
9
+ * token is requested when called and never stored. */
9
10
  export function sendTestNotification(relay) {
10
11
  return createPushRelayAdapter(relay).sendTest();
11
12
  }
@@ -24,7 +24,11 @@ export const TextEditor = forwardRef(function TextEditor({ value, onChange, labe
24
24
  // Only a scroll surface bounds growth. A content-sized parent is the editor's own height,
25
25
  // so reading it as a limit would stop the editor from ever growing.
26
26
  const containerHeight = surface?.clientHeight ?? Infinity;
27
- const visibleHeight = keyboard.viewportHeight > 0
27
+ // A sheet enters from outside the viewport. Its transformed screen position is
28
+ // not an editing limit: transforms do not deliver a ResizeObserver notification
29
+ // when the sheet settles. Only intersect screen geometry for an open keyboard.
30
+ const keyboardVisible = keyboard.height > 0 && keyboard.viewportHeight > 0;
31
+ const visibleHeight = keyboardVisible
28
32
  ? keyboard.viewportHeight -
29
33
  Math.max(0, (surface?.getBoundingClientRect().top ?? 0) - keyboard.offsetTop)
30
34
  : containerHeight;
@@ -32,7 +36,7 @@ export const TextEditor = forwardRef(function TextEditor({ value, onChange, labe
32
36
  // to measure it: use an isolated, non-focusable copy with the same text layout instead.
33
37
  const available = maxLines === undefined
34
38
  ? Math.min(containerHeight, visibleHeight) - 24
35
- : Math.min(Math.max(minLines, maxLines) * lineHeight + padding, keyboard.viewportHeight > 0 ? visibleHeight - 24 : Infinity);
39
+ : Math.min(Math.max(minLines, maxLines) * lineHeight + padding, keyboardVisible ? visibleHeight - 24 : Infinity);
36
40
  const height = editorHeight(measureEditorContentHeight(element), minLines, lineHeight, padding, available);
37
41
  if (element.style.height === `${height}px`)
38
42
  return;
@@ -65,7 +69,7 @@ export const TextEditor = forwardRef(function TextEditor({ value, onChange, labe
65
69
  observer.disconnect();
66
70
  element.removeEventListener('input', resize);
67
71
  };
68
- }, [value, minLines, maxLines, keyboard.viewportHeight, keyboard.offsetTop]);
72
+ }, [value, minLines, maxLines, keyboard.height, keyboard.viewportHeight, keyboard.offsetTop]);
69
73
  useLayoutEffect(() => {
70
74
  if (autoFocus && ref.current)
71
75
  focusFromFramework(ref.current, { preventScroll: true });
@@ -1,4 +1,4 @@
1
- /** The focusable field capabilities accepted by a declared editing order. */
1
+ /** The parts of a field element that `useFocusChain` uses to move focus. */
2
2
  export type FocusChainField = Pick<HTMLInputElement, 'focus' | 'blur' | 'disabled' | 'readOnly' | 'isConnected' | 'closest'>;
3
3
  /** Explicit order survives conditional rendering; disabled/unmounted fields are skipped. */
4
4
  export declare function createFocusChain<Key extends string>(order: readonly Key[]): {
@@ -10,8 +10,9 @@ export declare function createFocusChain<Key extends string>(order: readonly Key
10
10
  dismiss(): void;
11
11
  };
12
12
  /**
13
- * `@FocusState` for a form: the return key moves to the next field in `order` and the
14
- * last one dismisses the keyboard. Spread `field(key)` onto each field.
13
+ * Moves focus through a form's fields in `order`. The return key goes to the next field and the
14
+ * last one dismisses the keyboard. Disabled and unmounted fields are skipped. Spread
15
+ * `field(key)` onto each field.
15
16
  *
16
17
  * @example
17
18
  * const focus = useFocusChain(['name', 'email'])
@@ -49,8 +49,9 @@ export function createFocusChain(order) {
49
49
  };
50
50
  }
51
51
  /**
52
- * `@FocusState` for a form: the return key moves to the next field in `order` and the
53
- * last one dismisses the keyboard. Spread `field(key)` onto each field.
52
+ * Moves focus through a form's fields in `order`. The return key goes to the next field and the
53
+ * last one dismisses the keyboard. Disabled and unmounted fields are skipped. Spread
54
+ * `field(key)` onto each field.
54
55
  *
55
56
  * @example
56
57
  * const focus = useFocusChain(['name', 'email'])
@@ -1,7 +1,7 @@
1
1
  import type { InputHTMLAttributes } from 'react';
2
2
  /** What a field holds, setting its keyboard, autofill and autocorrection together. */
3
3
  export type TextFieldKind = 'text' | 'name' | 'email' | 'url' | 'tel' | 'number' | 'decimal' | 'search' | 'username' | 'new-password' | 'current-password' | 'one-time-code';
4
- /** `SubmitLabel`: the return key's title. */
4
+ /** The return key's title: `done`, `go`, `next`, `search` or `send`. */
5
5
  export type SubmitLabel = 'done' | 'go' | 'next' | 'search' | 'send';
6
6
  type Attributes = Pick<InputHTMLAttributes<HTMLInputElement>, 'type' | 'inputMode' | 'autoComplete' | 'autoCapitalize' | 'autoCorrect' | 'spellCheck' | 'enterKeyHint'>;
7
7
  export declare function textFieldAttributes(kind?: TextFieldKind, submitLabel?: SubmitLabel): Attributes;
@@ -1,4 +1,6 @@
1
1
  import { type CSSProperties } from 'react';
2
+ /** The embedded page's load state. A cross-origin page reports `unverified`, because its contents
3
+ * cannot be read. */
2
4
  export type WebFrameState = {
3
5
  status: 'idle' | 'loading';
4
6
  } | {
@@ -12,20 +14,24 @@ export type WebFrameState = {
12
14
  status: 'unavailable';
13
15
  reason: 'invalid-url' | 'load-error';
14
16
  };
17
+ /** Whether a `reload` or `stop` request was sent to the frame, or why not. */
15
18
  export type WebFrameActionResult = {
16
19
  status: 'dispatched';
17
20
  } | {
18
21
  status: 'unavailable';
19
22
  reason: 'not-mounted' | 'cross-origin-or-denied';
20
23
  };
21
- /** Only same-origin frames expose their current page or accept reload/stop commands. */
24
+ /** Reads a `WebView`'s state and sends it `reload` and `stop`. Only same-origin frames expose
25
+ * their current page or accept these commands. */
22
26
  export interface WebFrameController {
23
27
  readonly state: WebFrameState;
24
28
  reload(): WebFrameActionResult;
25
29
  stop(): WebFrameActionResult;
26
30
  }
27
- /** Observe a WebView and request the operations its origin permits. */
31
+ /** Makes a controller to pass to a `WebView`, for reading its state and reloading or stopping it. */
28
32
  export declare function useWebFrame(): WebFrameController;
33
+ /** The page URL, its accessible title, an optional controller, and what the page may run. Scripts
34
+ * and forms are off by default. */
29
35
  export interface WebViewProps {
30
36
  url: string;
31
37
  /** Accessible name for the embedded page. */
@@ -38,5 +44,6 @@ export interface WebViewProps {
38
44
  style?: CSSProperties;
39
45
  onStateChange?: (state: WebFrameState) => void;
40
46
  }
41
- /** A sandboxed embedded page. Framing policy and partitioned storage remain browser-owned. */
47
+ /** Embeds a web page in a sandboxed frame. The browser decides whether the page may be framed, and
48
+ * the frame's storage is partitioned from the app's. */
42
49
  export declare function WebView({ url: input, title, controller, className, style, onStateChange, allowsJavaScript, allowsForms, }: WebViewProps): import("react").JSX.Element | null;
@@ -50,7 +50,7 @@ function createFrameStore() {
50
50
  },
51
51
  };
52
52
  }
53
- /** Observe a WebView and request the operations its origin permits. */
53
+ /** Makes a controller to pass to a `WebView`, for reading its state and reloading or stopping it. */
54
54
  export function useWebFrame() {
55
55
  const store = useMemo(() => {
56
56
  const value = createFrameStore();
@@ -60,7 +60,8 @@ export function useWebFrame() {
60
60
  useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
61
61
  return store.controller;
62
62
  }
63
- /** A sandboxed embedded page. Framing policy and partitioned storage remain browser-owned. */
63
+ /** Embeds a web page in a sandboxed frame. The browser decides whether the page may be framed, and
64
+ * the frame's storage is partitioned from the app's. */
64
65
  export function WebView({ url: input, title, controller, className, style, onStateChange, allowsJavaScript = false, allowsForms = false, }) {
65
66
  const local = useWebFrame();
66
67
  const store = stores.get(controller ?? local);
@@ -1,3 +1,5 @@
1
+ /** The OAuth provider, client, callback and app URLs for `defineWebAuthentication`, and the
2
+ * `exchange` function that finishes sign-in on your server. */
1
3
  export interface WebAuthenticationConfiguration {
2
4
  /** Stable name, unique within this app origin. */
3
5
  id: string;
@@ -16,6 +18,8 @@ export interface WebAuthenticationConfiguration {
16
18
  /** Establish your session on the server. Never return or message tokens to another window. */
17
19
  exchange: (request: WebAuthenticationExchange) => Promise<void>;
18
20
  }
21
+ /** What `exchange` receives: the authorization code, the PKCE verifier and the values your
22
+ * server needs to redeem and check it. */
19
23
  export interface WebAuthenticationExchange {
20
24
  code: string;
21
25
  codeVerifier: string;
@@ -26,6 +30,7 @@ export interface WebAuthenticationExchange {
26
30
  expectedIssuer?: string;
27
31
  signal: AbortSignal;
28
32
  }
33
+ /** Where a sign-in stands, from `idle` through `pending` and `completing` to a final status. */
29
34
  export type WebAuthenticationState = {
30
35
  status: 'idle';
31
36
  } | {
@@ -38,6 +43,7 @@ export type WebAuthenticationState = {
38
43
  status: 'failed' | 'unavailable';
39
44
  reason: string;
40
45
  };
46
+ /** Whether `start` opened the provider in a popup or a redirect, or why it could not. */
41
47
  export type WebAuthenticationStartResult = {
42
48
  status: 'dispatched';
43
49
  transport: 'popup' | 'redirect';
@@ -45,6 +51,7 @@ export type WebAuthenticationStartResult = {
45
51
  status: 'unavailable';
46
52
  reason: string;
47
53
  };
54
+ /** The outcome of `complete` on the callback page: the URL to return to, or why it was rejected. */
48
55
  export type WebAuthenticationCallbackResult = {
49
56
  status: 'completed';
50
57
  returnURL: string;
@@ -52,12 +59,15 @@ export type WebAuthenticationCallbackResult = {
52
59
  status: 'rejected' | 'unavailable';
53
60
  reason: string;
54
61
  };
62
+ /** A prepared sign-in. Call `start` from the user's press. */
55
63
  export interface WebAuthenticationRequest {
56
64
  /** Call synchronously in the user's press handler. A blocked popup may retry as redirect. */
57
65
  start(options?: {
58
66
  transport?: 'popup' | 'redirect';
59
67
  }): WebAuthenticationStartResult;
60
68
  }
69
+ /** A sign-in flow made by `defineWebAuthentication`: prepare, start, complete and resume it, or
70
+ * subscribe to its state. */
61
71
  export interface WebAuthentication {
62
72
  /** Generate PKCE and durably register the transaction before enabling the sign-in button. */
63
73
  prepare(): Promise<WebAuthenticationRequest>;
@@ -71,5 +81,6 @@ export interface WebAuthentication {
71
81
  /** Attach bounded popup monitoring and lifecycle listeners. Returns their cleanup. */
72
82
  connect(): () => void;
73
83
  }
74
- /** Durable authorization-code + PKCE handoff. This does not implement a native authentication session. */
84
+ /** Sets up an OAuth authorization-code sign-in with PKCE. The transaction is stored durably, so
85
+ * it survives a redirect or a suspended app. Your server redeems the code. */
75
86
  export declare function defineWebAuthentication(configuration: WebAuthenticationConfiguration): WebAuthentication;
@@ -29,7 +29,8 @@ function callbackValue(value, state) {
29
29
  return { state, error: response.error };
30
30
  return null;
31
31
  }
32
- /** Durable authorization-code + PKCE handoff. This does not implement a native authentication session. */
32
+ /** Sets up an OAuth authorization-code sign-in with PKCE. The transaction is stored durably, so
33
+ * it survives a redirect or a suspended app. Your server redeems the code. */
33
34
  export function defineWebAuthentication(configuration) {
34
35
  const app = secureWebURL(configuration.appURL, configuration.appURL);
35
36
  const callback = secureWebURL(configuration.callbackURL, app.href);
@@ -1,10 +1,12 @@
1
1
  import { type CSSProperties, type ReactNode } from 'react';
2
- /** A preference for the browser-owned presentation, not a Safari launch guarantee. */
2
+ /** How to open a web page. `prefersInApp` is a preference; the browser decides how the page is
3
+ * presented. */
3
4
  export interface OpenURLOptions {
4
5
  /** Defaults to true. A strict external-browser request is unavailable on this runtime. */
5
6
  prefersInApp?: boolean;
6
7
  }
7
- /** Dispatched means WebKit accepted the action; it does not mean the page loaded. */
8
+ /** What happened to an open request: `handled` in the app, `dispatched` to the browser, or
9
+ * `unavailable` with a reason. `dispatched` does not mean the page loaded. */
8
10
  export type OpenURLResult = {
9
11
  status: 'handled';
10
12
  destination: 'app';
@@ -15,6 +17,7 @@ export type OpenURLResult = {
15
17
  status: 'unavailable';
16
18
  reason: 'invalid-url' | 'external-browser' | 'unregistered-app-url' | 'navigation-failed' | 'popup-blocked';
17
19
  };
20
+ /** The function `useOpenURL` returns. It opens a URL and reports the result at once. */
18
21
  export type OpenURLAction = (url: string, options?: OpenURLOptions) => OpenURLResult;
19
22
  /** Internal factory keeps the same policy in component and imperative actions. */
20
23
  export declare function createOpenURLAction(options: {
@@ -22,8 +25,10 @@ export declare function createOpenURLAction(options: {
22
25
  openInternal: (url: string) => boolean;
23
26
  dispatch: (url: string) => boolean;
24
27
  }): OpenURLAction;
25
- /** Open a web URL from an action. Registered app URLs stay in the current document. */
28
+ /** Returns a function that opens a URL from an action. App URLs that match a registered route
29
+ * navigate inside the app; other URLs go to the browser. */
26
30
  export declare function useOpenURL(): OpenURLAction;
31
+ /** The link's destination URL, its content and an optional result callback. */
27
32
  export interface LinkProps extends OpenURLOptions {
28
33
  destination: string;
29
34
  children: ReactNode;
@@ -33,5 +38,6 @@ export interface LinkProps extends OpenURLOptions {
33
38
  /** Receives dispatch or rejection immediately, never inferred page completion. */
34
39
  onResult?: (result: OpenURLResult) => void;
35
40
  }
36
- /** An accessible URL action with the framework's press and scroll-cancellation policy. */
41
+ /** Opens a URL when pressed, like `useOpenURL`. A press that turns into a scroll does not open
42
+ * it. */
37
43
  export declare function Link({ destination, prefersInApp, onResult, children, ...props }: LinkProps): import("react").JSX.Element;