@satinhq/kit 0.1.0-beta.3 → 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 (240) hide show
  1. package/NOTICE +1 -1
  2. package/README.md +1 -2
  3. package/dist/acknowledgements/inventory.json +1 -1
  4. package/dist/actions/RowActions.d.ts +2 -2
  5. package/dist/actions/RowActions.js +91 -25
  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/data/database.d.ts +9 -8
  59. package/dist/data/database.js +4 -4
  60. package/dist/docs/reference.md +982 -964
  61. package/dist/docs/tailwind.md +76 -0
  62. package/dist/docs/tokens.md +1 -1
  63. package/dist/gestures/GestureView.d.ts +3 -1
  64. package/dist/gestures/GestureView.js +2 -1
  65. package/dist/gestures/gesture.d.ts +22 -2
  66. package/dist/gestures/gesture.js +7 -2
  67. package/dist/glass/GlassEffect.d.ts +2 -1
  68. package/dist/glass/GlassEffect.js +2 -1
  69. package/dist/glass/GlassPresentation.d.ts +1 -0
  70. package/dist/glass/GlassPresentation.js +21 -4
  71. package/dist/glass/TabBar.js +23 -1
  72. package/dist/glass/errorAlert.js +2 -1
  73. package/dist/glass/errors.d.ts +3 -1
  74. package/dist/glass/fallback/runtime.d.ts +1 -0
  75. package/dist/glass/fallback/runtime.js +8 -1
  76. package/dist/glass/liquidContext.d.ts +2 -0
  77. package/dist/glass/menuPresenter.d.ts +2 -0
  78. package/dist/glass/regions.d.ts +2 -1
  79. package/dist/glass/renderSpecs.d.ts +1 -0
  80. package/dist/glass/renderSpecs.js +1 -0
  81. package/dist/glass/webgpu/controlRim.d.ts +29 -0
  82. package/dist/glass/webgpu/controlRim.js +68 -0
  83. package/dist/glass/webgpu/passes.d.ts +2 -2
  84. package/dist/glass/webgpu/production.d.ts +1 -0
  85. package/dist/glass/webgpu/production.js +27 -1
  86. package/dist/glass/webgpu/rasterUniforms.js +9 -0
  87. package/dist/glass/webgpu/shaders.d.ts +2 -2
  88. package/dist/glass/webgpu/shaders.js +5 -1
  89. package/dist/haptics/SensoryButton.d.ts +4 -4
  90. package/dist/haptics/SensoryButton.js +3 -3
  91. package/dist/image/Image.d.ts +6 -3
  92. package/dist/image/Image.js +4 -3
  93. package/dist/image/asset.d.ts +2 -1
  94. package/dist/keyboard/adapter.d.ts +3 -4
  95. package/dist/keyboard/geometry.d.ts +1 -1
  96. package/dist/list-form/ListForm.d.ts +8 -7
  97. package/dist/list-form/ListForm.js +2 -2
  98. package/dist/list-form/editing.d.ts +5 -6
  99. package/dist/list-form/editing.js +4 -5
  100. package/dist/list-form/offsets.d.ts +2 -2
  101. package/dist/list-form/offsets.js +2 -2
  102. package/dist/media/react.d.ts +2 -1
  103. package/dist/media/react.js +2 -1
  104. package/dist/media/types.d.ts +6 -3
  105. package/dist/motion/TransitionView.d.ts +4 -2
  106. package/dist/motion/TransitionView.js +3 -2
  107. package/dist/motion/animationValue.d.ts +1 -1
  108. package/dist/motion/matchedGeometry.d.ts +6 -5
  109. package/dist/motion/matchedGeometry.js +2 -1
  110. package/dist/motion/transition.d.ts +4 -1
  111. package/dist/motion/transition.js +2 -0
  112. package/dist/navigation/DetailList.d.ts +1 -1
  113. package/dist/navigation/DetailList.js +1 -1
  114. package/dist/navigation/Sidebar.d.ts +3 -1
  115. package/dist/navigation/Sidebar.js +1 -1
  116. package/dist/navigation/SidebarSymbol.d.ts +2 -1
  117. package/dist/navigation/SidebarSymbol.js +2 -1
  118. package/dist/navigation/navigationContext.d.ts +2 -1
  119. package/dist/navigation/navigationContext.js +2 -1
  120. package/dist/navigation/splitContext.d.ts +2 -1
  121. package/dist/navigation/splitContext.js +2 -1
  122. package/dist/navigation/toolbarMinimization.d.ts +4 -3
  123. package/dist/navigation/types.d.ts +7 -5
  124. package/dist/presentation/Presentation.d.ts +8 -7
  125. package/dist/presentation/Presentation.js +3 -2
  126. package/dist/presentation/model.d.ts +2 -1
  127. package/dist/press/Pressable.d.ts +5 -1
  128. package/dist/press/Pressable.js +2 -0
  129. package/dist/press/stateMachine.d.ts +1 -1
  130. package/dist/public/Label.d.ts +2 -1
  131. package/dist/public/Label.js +2 -1
  132. package/dist/public/Screen.d.ts +4 -6
  133. package/dist/public/actions.d.ts +2 -1
  134. package/dist/public/actions.js +2 -1
  135. package/dist/public/buttonBorderShape.d.ts +2 -1
  136. package/dist/public/colors.d.ts +7 -4
  137. package/dist/public/colors.js +3 -2
  138. package/dist/public/controlStyle.d.ts +17 -14
  139. package/dist/public/controls.d.ts +27 -26
  140. package/dist/public/controls.js +20 -19
  141. package/dist/public/deviceActions.d.ts +6 -4
  142. package/dist/public/deviceActions.js +4 -2
  143. package/dist/public/environment.d.ts +16 -13
  144. package/dist/public/environment.js +11 -9
  145. package/dist/public/filePickers.d.ts +6 -4
  146. package/dist/public/filePickers.js +3 -2
  147. package/dist/public/glass.d.ts +6 -3
  148. package/dist/public/glass.js +2 -1
  149. package/dist/public/glassValue.d.ts +3 -4
  150. package/dist/public/glassValue.js +3 -4
  151. package/dist/public/keyboard.d.ts +4 -1
  152. package/dist/public/keyboard.js +2 -1
  153. package/dist/public/navigationTransition.d.ts +5 -5
  154. package/dist/public/navigationTransition.js +2 -2
  155. package/dist/public/presentation.d.ts +8 -7
  156. package/dist/public/presentation.js +5 -5
  157. package/dist/public/runtime.d.ts +9 -8
  158. package/dist/public/runtime.js +7 -7
  159. package/dist/public/scroll.d.ts +6 -3
  160. package/dist/public/scroll.js +3 -1
  161. package/dist/public/structure.d.ts +2 -2
  162. package/dist/public/toolbar.d.ts +15 -11
  163. package/dist/public/toolbar.js +12 -7
  164. package/dist/reachability/model.d.ts +4 -3
  165. package/dist/reachability/react.d.ts +4 -1
  166. package/dist/reachability/react.js +2 -1
  167. package/dist/root/SatinRoot.d.ts +3 -3
  168. package/dist/root/SatinRoot.js +12 -36
  169. package/dist/root/mountSatin.d.ts +1 -1
  170. package/dist/root/mountSatin.js +26 -35
  171. package/dist/root/runtimeContext.d.ts +2 -1
  172. package/dist/root/runtimeContext.js +2 -1
  173. package/dist/safe-area/Overlay.d.ts +3 -2
  174. package/dist/safe-area/Overlay.js +2 -1
  175. package/dist/safe-area/SafeAreaInset.d.ts +11 -7
  176. package/dist/safe-area/SafeAreaInset.js +7 -6
  177. package/dist/safe-area/context.d.ts +3 -3
  178. package/dist/safe-area/context.js +1 -1
  179. package/dist/safe-area/displayCorners.d.ts +4 -2
  180. package/dist/safe-area/displayCorners.js +4 -2
  181. package/dist/safe-area/modifiers.d.ts +14 -10
  182. package/dist/safe-area/modifiers.js +8 -4
  183. package/dist/scroll/ScrollSurface.js +5 -1
  184. package/dist/scroll/ScrollTarget.d.ts +1 -1
  185. package/dist/scroll/ScrollTarget.js +1 -1
  186. package/dist/scroll/edgePolicy.d.ts +2 -2
  187. package/dist/scroll/position.d.ts +5 -3
  188. package/dist/scroll/types.d.ts +1 -1
  189. package/dist/search/PadSearchDrawer.d.ts +7 -0
  190. package/dist/search/PadSearchDrawer.js +8 -0
  191. package/dist/search/PadSearchPresentationContext.d.ts +6 -0
  192. package/dist/search/PadSearchPresentationContext.js +4 -0
  193. package/dist/search/SearchDrawer.d.ts +1 -1
  194. package/dist/search/SearchDrawer.js +6 -1
  195. package/dist/search/SearchSuggestionsPopover.d.ts +6 -0
  196. package/dist/search/SearchSuggestionsPopover.js +42 -0
  197. package/dist/search/SearchToolbarField.d.ts +2 -1
  198. package/dist/search/SearchToolbarField.js +28 -7
  199. package/dist/search/padSearchBarMotion.d.ts +69 -0
  200. package/dist/search/padSearchBarMotion.js +73 -0
  201. package/dist/search/padSearchFieldSamples.json +491 -0
  202. package/dist/search/searchPlacement.d.ts +3 -0
  203. package/dist/search/searchPlacement.js +7 -0
  204. package/dist/search/searchable.d.ts +14 -12
  205. package/dist/search/usePadSearchFieldMotion.d.ts +4 -0
  206. package/dist/search/usePadSearchFieldMotion.js +100 -0
  207. package/dist/search/useSearchPresentation.d.ts +2 -1
  208. package/dist/search/useSearchPresentation.js +23 -5
  209. package/dist/server.d.ts +4 -4
  210. package/dist/server.js +4 -4
  211. package/dist/shapes/corners.d.ts +3 -3
  212. package/dist/shapes/corners.js +1 -2
  213. package/dist/styles.css +8289 -8244
  214. package/dist/sw/vite.d.ts +4 -0
  215. package/dist/sw/vite.js +2 -0
  216. package/dist/symbols/Symbol.d.ts +7 -5
  217. package/dist/symbols/Symbol.js +5 -3
  218. package/dist/symbols/catalogue.d.ts +1 -1
  219. package/dist/symbols/types.d.ts +6 -4
  220. package/dist/tabs/TabSection.d.ts +3 -2
  221. package/dist/tabs/TabSection.js +2 -1
  222. package/dist/tabs/TabView.js +54 -51
  223. package/dist/tasks/useTask.d.ts +5 -4
  224. package/dist/tasks/useTask.js +4 -3
  225. package/dist/testing.d.ts +3 -2
  226. package/dist/testing.js +3 -2
  227. package/dist/text-input/focusChain.d.ts +4 -3
  228. package/dist/text-input/focusChain.js +3 -2
  229. package/dist/text-input/kinds.d.ts +1 -1
  230. package/dist/web/WebView.d.ts +10 -3
  231. package/dist/web/WebView.js +3 -2
  232. package/dist/web/authentication.d.ts +12 -1
  233. package/dist/web/authentication.js +2 -1
  234. package/dist/web/openURL.d.ts +10 -4
  235. package/dist/web/openURL.js +4 -2
  236. package/dist/web/useWebAuthentication.d.ts +2 -1
  237. package/dist/web/useWebAuthentication.js +2 -1
  238. package/dist/zoom/Zoomable.d.ts +3 -3
  239. package/dist/zoom/Zoomable.js +2 -2
  240. package/package.json +1 -1
@@ -3,6 +3,7 @@ import { checkDeployment, } from './deployment.js';
3
3
  export async function installPageCheck(expected, check, requestTimeoutMs = 4000,
4
4
  // Called once the check passes; a custom install page loads the author's code here.
5
5
  onPass) {
6
+ const environment = window.__SATIN_PRELAUNCH__;
6
7
  const guide = document.getElementById('satin-install-guide');
7
8
  const status = document.getElementById('satin-install-check');
8
9
  const standalone = matchMedia('(display-mode: standalone)').matches ||
@@ -50,100 +51,45 @@ onPass) {
50
51
  status.hidden = true;
51
52
  status.style.display = 'none';
52
53
  guide.hidden = false;
54
+ environment.recover();
53
55
  onPass?.();
54
56
  return;
55
57
  }
56
58
  guide.hidden = true;
57
- status.hidden = false;
58
- status.style.removeProperty('display');
59
- status.replaceChildren();
60
- status.setAttribute('role', 'alert');
61
- status.dataset.error = 'true';
62
- const style = document.createElement('style');
63
- style.textContent = `
64
- #satin-install-check[data-error] {
65
- position: fixed;
66
- inset: 0;
67
- z-index: 20002;
68
- display: grid;
69
- place-items: center;
70
- overflow: auto;
71
- padding: 32px;
72
- box-sizing: border-box;
73
- background: #f2f2f7;
74
- color: #1c1c1e;
75
- color-scheme: light dark;
76
- font: 17px/1.45 -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
77
- text-align: center;
78
- }
79
- #satin-install-check button { font: inherit; border: 0; background: transparent; color: #007aff; min-height: 44px; cursor: pointer; }
80
- #satin-install-check .install-error-content { width: min(100%, 560px); }
81
- #satin-install-check h2 { font-size: 24px; margin: 0 0 12px; }
82
- #satin-install-check p { opacity: 1; margin: 12px 0; }
83
- #satin-install-check .install-error-detail {
84
- font-size: 13px;
85
- color: #6c6c70;
86
- overflow-wrap: anywhere;
87
- }
88
- #satin-install-check ul { list-style: none; padding: 0; margin: 24px 0 0; }
89
- #satin-install-check li { margin: 8px 0; font-size: 13px; overflow-wrap: anywhere; }
90
- @media (prefers-color-scheme: dark) {
91
- #satin-install-check[data-error] { background: #000; color: #fff; }
92
- #satin-install-check .install-error-detail { color: #aeaeb2; }
93
- }
94
- `;
95
- document.head.append(style);
96
- const content = document.createElement('div');
97
- content.className = 'install-error-content';
98
- const title = document.createElement('h2');
59
+ status.hidden = true;
99
60
  const unverified = !wrongBase && failures.length > 0 && failures.every(failure => failure.transient);
100
- title.textContent = standalone
61
+ for (const failure of failures)
62
+ console.error('[Satin deployment]', failure.reason, failure.url, failure.fix);
63
+ const title = standalone
101
64
  ? 'Wrong Home Screen icon'
102
65
  : unverified
103
66
  ? "Can't reach the app right now"
104
67
  : 'This app is misconfigured';
105
- const explanation = document.createElement('p');
106
- const action = document.createElement('p');
107
- content.append(title, explanation, action);
108
- status.append(content);
109
- for (const failure of failures) {
110
- console.error('[Satin deployment]', failure.reason, failure.url, failure.fix);
111
- }
112
- if (standalone) {
113
- explanation.textContent = 'This icon opens the install page instead of the app.';
114
- action.textContent = 'Delete this icon, then reinstall from Safari.';
115
- const detail = document.createElement('p');
116
- detail.className = 'install-error-detail';
117
- detail.textContent = location.href;
118
- content.append(detail);
119
- return;
120
- }
121
- if (unverified) {
122
- explanation.textContent = 'Check your connection and try again.';
123
- action.remove();
124
- const retry = document.createElement('button');
125
- retry.textContent = 'Retry';
126
- retry.addEventListener('click', () => {
127
- retry.disabled = true;
128
- void installPageCheck(expected, check, 30_000, onPass);
68
+ const reason = standalone
69
+ ? 'This icon opens the install page instead of the app. Reinstall from Safari.'
70
+ : unverified
71
+ ? 'Check your connection and try again.'
72
+ : wrongBase
73
+ ? `Built for ${expected.base} but served at ${servedBase}. Serve this build at ${expected.base}, or rebuild with Vite base ${servedBase}.`
74
+ : 'Required app files are missing or do not match this build. ' + failures[0].fix;
75
+ const detail = standalone
76
+ ? location.href
77
+ : unverified
78
+ ? undefined
79
+ : failures
80
+ .map(failure => {
81
+ const url = new URL(failure.url, location.href);
82
+ const address = url.origin === location.origin ? url.pathname + url.search : url.href;
83
+ return `${failure.check ?? 'Configuration'} → ${address} → ${failure.status ?? failure.reason}`;
84
+ })
85
+ .join('\n');
86
+ let retrying = false;
87
+ environment.error(reason, title, () => {
88
+ if (retrying)
89
+ return;
90
+ retrying = true;
91
+ void installPageCheck(expected, check, 30_000, onPass).finally(() => {
92
+ retrying = false;
129
93
  });
130
- content.append(retry);
131
- return;
132
- }
133
- explanation.textContent = wrongBase
134
- ? `Built for ${expected.base} but served at ${servedBase}.`
135
- : 'Required app files are missing or do not match this build.';
136
- action.textContent = wrongBase
137
- ? `Serve this build at ${expected.base}, or rebuild with Vite base ${servedBase}.`
138
- : failures[0].fix;
139
- const checks = document.createElement('ul');
140
- checks.setAttribute('aria-label', 'Failed checks');
141
- for (const failure of failures) {
142
- const row = document.createElement('li');
143
- const url = new URL(failure.url, location.href);
144
- const address = url.origin === location.origin ? url.pathname + url.search : url.href;
145
- row.textContent = `${failure.check ?? 'Configuration'} → ${address} → ${failure.status ?? failure.reason}`;
146
- checks.append(row);
147
- }
148
- content.append(checks);
94
+ }, detail);
149
95
  }
@@ -1,10 +1,9 @@
1
- /**
2
- * What a custom install page needs to know (public through `@satinhq/kit/install`). Every value is
3
- * read from the browser as it is; nothing is inferred. In particular "installed" is only the
4
- * standalone display mode: a Safari tab cannot tell whether the app is on the Home Screen.
5
- */
1
+ /** The device the install page is open on: `iphone`, `ipad` or `other`. */
6
2
  export type InstallDevice = 'iphone' | 'ipad' | 'other';
3
+ /** The browser the install page is open in. `in-app` is a browser inside another app, which
4
+ * cannot add to the Home Screen. */
7
5
  export type InstallBrowser = 'safari' | 'in-app' | 'other';
6
+ /** The app being installed: its name, icon, tint and install page address. */
8
7
  export interface InstallApp {
9
8
  readonly name: string;
10
9
  readonly icon: string;
@@ -13,6 +12,9 @@ export interface InstallApp {
13
12
  /** The absolute address of the install page, for a QR code or Copy Link. */
14
13
  readonly installUrl: string;
15
14
  }
15
+ /** What a custom install page knows about where it is open: device, browser, Safari version,
16
+ * standalone mode and the app. A Safari tab cannot tell whether the app is already on the Home
17
+ * Screen. */
16
18
  export interface InstallContext {
17
19
  readonly device: InstallDevice;
18
20
  readonly browser: InstallBrowser;
@@ -22,6 +24,7 @@ export interface InstallContext {
22
24
  readonly standalone: boolean;
23
25
  readonly app: InstallApp;
24
26
  }
27
+ /** Whether `copyInstallLink` copied the address, and the address itself. */
25
28
  export interface CopyLinkResult {
26
29
  readonly copied: boolean;
27
30
  /** The address, so a failure can show it for the person to select. */
@@ -30,6 +33,8 @@ export interface CopyLinkResult {
30
33
  export declare function classifyInstallDevice(userAgent: string, platform: string, maxTouchPoints: number): InstallDevice;
31
34
  export declare function classifyInstallBrowser(userAgent: string): InstallBrowser;
32
35
  export declare function safariVersion(userAgent: string): number | null;
36
+ /** Reads the install context from the browser. It throws outside the install page that
37
+ * `satinApp` builds. */
33
38
  export declare function readInstallContext(): InstallContext;
34
39
  /** Copies the install page's address. Never throws: a refusal is reported with the address. */
35
40
  export declare function copyInstallLink(): Promise<CopyLinkResult>;
@@ -20,6 +20,8 @@ function readApp() {
20
20
  installUrl: new URL(data.installRoute, location.href).href,
21
21
  };
22
22
  }
23
+ /** Reads the install context from the browser. It throws outside the install page that
24
+ * `satinApp` builds. */
23
25
  export function readInstallContext() {
24
26
  const { userAgent, platform, maxTouchPoints } = navigator;
25
27
  const signals = classifyInstallSignals(userAgent, platform, maxTouchPoints);
@@ -18,6 +18,7 @@ export function installCustomMount(entry) {
18
18
  script.type = 'module';
19
19
  script.src = entry;
20
20
  script.dataset.satinInstallAuthor = '';
21
+ script.onerror = () => window.__SATIN_PRELAUNCH__?.error('The install page failed to load. Please retry.');
21
22
  document.body.append(script);
22
23
  }
23
24
  export function customInstallPage(input) {
@@ -217,66 +217,6 @@ html:not([data-ready]) [data-js], html[data-ready] [data-nojs], [data-copy-faile
217
217
  }
218
218
  html[data-env="gate"] .ix { max-width: 440px; margin: 0 auto; }
219
219
 
220
- /*
221
- * The deployment check's failure report keeps its words and gets the page's voice. The
222
- * check injects its own styles later, so each rule here starts at the body element to outrank them.
223
- */
224
- body:has(#satin-install-check[data-error]) :is(.ix-hero, .ix-pointer) { visibility: hidden; }
225
- /* Nested, so the long selector is written once. */
226
- body #satin-install-check {
227
- &[data-error] {
228
- padding: 32px 24px;
229
- background: var(--bg);
230
- color: var(--label);
231
- font: inherit;
232
- }
233
- & .install-error-content::before {
234
- content: "!";
235
- display: grid;
236
- place-items: center;
237
- width: 44px;
238
- height: 44px;
239
- margin: 0 auto 16px;
240
- border-radius: 50%;
241
- background: #ff3b30;
242
- color: #fff;
243
- font-size: 26px;
244
- font-weight: 700;
245
- line-height: 1;
246
- }
247
- & .install-error-content:has(button)::before {
248
- content: "?";
249
- background: var(--third);
250
- }
251
- & h2 { font-size: 1.294rem; margin: 0 0 8px; }
252
- & p { margin: 6px 0; color: var(--second); }
253
- & p:nth-of-type(2) { color: var(--label); }
254
- & button {
255
- min-height: 50px;
256
- margin-top: 20px;
257
- padding: 0 36px;
258
- border-radius: 25px;
259
- background: var(--tint);
260
- color: var(--on-tint);
261
- font-weight: 600;
262
- }
263
- & ul {
264
- margin-top: 24px;
265
- padding: 4px 16px;
266
- border-radius: 20px;
267
- background: var(--fill);
268
- text-align: left;
269
- }
270
- & li {
271
- margin: 0;
272
- padding: 10px 0;
273
- color: var(--label);
274
- font: 0.706rem/1.45 ui-monospace, SFMono-Regular, Menlo, monospace;
275
- }
276
- & li + li { border-top: 1px solid var(--hair); }
277
- & .install-error-detail { color: var(--second); }
278
- }
279
-
280
220
  @media (prefers-reduced-motion: reduce) {
281
221
  *, *::before, *::after { animation: none !important; transition: none !important; }
282
222
  }
@@ -1,4 +1,5 @@
1
- /** A diagnostic installation failure recorded before app launch. */
1
+ /** An installation failure recorded before the app launched: the build, the URL, the reason and
2
+ * whether the network failed. */
2
3
  export interface InstallationFailure {
3
4
  buildId: string;
4
5
  url: string;
@@ -6,7 +7,7 @@ export interface InstallationFailure {
6
7
  networkFailure: boolean;
7
8
  recordedAt?: number;
8
9
  }
9
- /** Reads the recorded installation failure for acceptance fixtures. */
10
+ /** Reads the last recorded installation failure for an app scope, or null when there is none. */
10
11
  export declare function readInstallationFailure(scope: string): InstallationFailure | null;
11
12
  export declare function readInstallationSuccess(scope: string): {
12
13
  buildId: string;
@@ -1,4 +1,4 @@
1
- /** Reads the recorded installation failure for acceptance fixtures. */
1
+ /** Reads the last recorded installation failure for an app scope, or null when there is none. */
2
2
  export function readInstallationFailure(scope) {
3
3
  try {
4
4
  const value = JSON.parse(localStorage.getItem('satin-install-failure:' + scope) ?? 'null');
@@ -1,5 +1,6 @@
1
1
  import type { AppDefinition } from './definition.js';
2
- /** Serializable so the same slot paints before JavaScript modules load. */
2
+ /** The launch screen's icon and optional label. The inline environment paints it before application JavaScript loads, so
3
+ * both must be plain strings. */
3
4
  export interface LaunchScreenSlot {
4
5
  readonly icon?: string;
5
6
  readonly label?: string;
@@ -7,10 +8,13 @@ export interface LaunchScreenSlot {
7
8
  export declare function launchScreenMarkup(app: AppDefinition): string;
8
9
  export declare const launchScreenLayer = 20000;
9
10
  export declare const launchErrorLayer: number;
10
- export declare const launchScreenCSS = "#satin-launch-screen{position:fixed;inset:calc(-1 * var(--satin-launch-excluded-top, 0px)) 0 0;z-index:20000;display:flex;flex-direction:column;align-items:center;justify-content:center;background:var(--satin-launch-background);color:var(--satin-launch-ink);font:17px system-ui;pointer-events:none}#satin-launch-screen[hidden],#satin-launch-progress[hidden]{display:none}.satin-launch-icon{position:relative;width:108px;height:108px;display:grid;place-items:center}#satin-launch-screen>p{position:absolute;top:calc(50% + 54px);margin:16px 0 0}.satin-launch-icon img{border-radius:15px}#satin-launch-progress{position:absolute;inset:0;width:108px;height:108px;transform:rotate(-90deg)}";
11
+ export declare const launchScreenCSS = "\n#satin-launch-screen {\n position:fixed;\n inset:calc(-1 * var(--satin-launch-excluded-top, 0px)) 0 0;\n z-index:20000;\n display:flex;\n flex-direction:column;\n align-items:center;\n justify-content:center;\n background:var(--satin-launch-background);\n color:var(--satin-launch-ink);\n font:17px system-ui;\n pointer-events:none\n}\n#satin-launch-screen[hidden],#satin-launch-progress[hidden] {\n display:none\n}\n.satin-launch-icon {\n position:relative;\n width:108px;\n height:108px;\n display:grid;\n place-items:center\n}\n#satin-launch-screen>p {\n position:absolute;\n top:calc(50% + 54px);\n margin:16px 0 0\n}\n.satin-launch-icon img {\n border-radius:15px\n}\n#satin-launch-progress {\n position:absolute;\n inset:0;\n width:108px;\n height:108px;\n transform:rotate(-90deg)\n}\n";
11
12
  export interface DownloadProgress {
12
13
  /** Monotonic response-header progress, independent of downloaded bytes. */
13
14
  activity?: number;
14
15
  readonly loaded: number;
15
16
  readonly total: number;
16
17
  }
18
+ /** Closed-shadow styles. Inner !important declarations protect the host itself
19
+ * from author resets; descendants never participate in the app cascade. */
20
+ export declare const prelaunchCSS = "\n:host {\n all:initial !important;\n display:block !important;\n position:fixed !important;\n inset:0 !important;\n z-index:20004 !important;\n opacity:1 !important;\n visibility:visible !important;\n transform:none !important;\n filter:none !important;\n clip-path:none !important;\n overflow:visible !important;\n pointer-events:none !important;\n contain:none !important;\n isolation:isolate !important;\n color-scheme:light dark !important\n}\n* {\n box-sizing:border-box\n}\n [hidden] {\n display:none !important\n}\n\n#satin-launch-screen {\n position:fixed;\n inset:calc(-1 * var(--satin-launch-excluded-top, 0px)) 0 0;\n z-index:20000;\n display:flex;\n flex-direction:column;\n align-items:center;\n justify-content:center;\n background:var(--satin-launch-background);\n color:var(--satin-launch-ink);\n font:17px system-ui;\n pointer-events:none\n}\n#satin-launch-screen[hidden],#satin-launch-progress[hidden] {\n display:none\n}\n.satin-launch-icon {\n position:relative;\n width:108px;\n height:108px;\n display:grid;\n place-items:center\n}\n#satin-launch-screen>p {\n position:absolute;\n top:calc(50% + 54px);\n margin:16px 0 0\n}\n.satin-launch-icon img {\n border-radius:15px\n}\n#satin-launch-progress {\n position:absolute;\n inset:0;\n width:108px;\n height:108px;\n transform:rotate(-90deg)\n}\n#satin-launch-error {\n position:fixed;\n inset:0;\n display:flex;\n flex-direction:column;\n align-items:center;\n justify-content:center;\n padding:32px;\n background:var(--satin-launch-background);\n color:var(--satin-launch-ink);\n font:17px/1.45 system-ui;\n text-align:center;\n pointer-events:auto;\n overflow:auto\n}\n#satin-launch-error h1 {\n font-size:24px;\n margin:0 0 12px\n}\n#satin-launch-error p {\n max-width:420px;\n margin:17px 0;\n white-space:pre-wrap;\n overflow-wrap:anywhere\n}\n#satin-launch-error .detail {\n font-size:13px;\n opacity:.65\n}\n#satin-launch-error button {\n font:600 17px system-ui;\n color:#007aff;\n background:transparent;\n border:0;\n padding:16px 32px;\n min-height:44px\n}\n";
@@ -11,4 +11,107 @@ export function launchScreenMarkup(app) {
11
11
  // ordering away from the maximum CSS integer boundary.
12
12
  export const launchScreenLayer = 20_000;
13
13
  export const launchErrorLayer = launchScreenLayer + 1;
14
- export const launchScreenCSS = `#satin-launch-screen{position:fixed;inset:calc(-1 * var(--satin-launch-excluded-top, 0px)) 0 0;z-index:${launchScreenLayer};display:flex;flex-direction:column;align-items:center;justify-content:center;background:var(--satin-launch-background);color:var(--satin-launch-ink);font:17px system-ui;pointer-events:none}#satin-launch-screen[hidden],#satin-launch-progress[hidden]{display:none}.satin-launch-icon{position:relative;width:108px;height:108px;display:grid;place-items:center}#satin-launch-screen>p{position:absolute;top:calc(50% + 54px);margin:16px 0 0}.satin-launch-icon img{border-radius:15px}#satin-launch-progress{position:absolute;inset:0;width:108px;height:108px;transform:rotate(-90deg)}`;
14
+ export const launchScreenCSS = `
15
+ #satin-launch-screen {
16
+ position:fixed;
17
+ inset:calc(-1 * var(--satin-launch-excluded-top, 0px)) 0 0;
18
+ z-index:${launchScreenLayer};
19
+ display:flex;
20
+ flex-direction:column;
21
+ align-items:center;
22
+ justify-content:center;
23
+ background:var(--satin-launch-background);
24
+ color:var(--satin-launch-ink);
25
+ font:17px system-ui;
26
+ pointer-events:none
27
+ }
28
+ #satin-launch-screen[hidden],#satin-launch-progress[hidden] {
29
+ display:none
30
+ }
31
+ .satin-launch-icon {
32
+ position:relative;
33
+ width:108px;
34
+ height:108px;
35
+ display:grid;
36
+ place-items:center
37
+ }
38
+ #satin-launch-screen>p {
39
+ position:absolute;
40
+ top:calc(50% + 54px);
41
+ margin:16px 0 0
42
+ }
43
+ .satin-launch-icon img {
44
+ border-radius:15px
45
+ }
46
+ #satin-launch-progress {
47
+ position:absolute;
48
+ inset:0;
49
+ width:108px;
50
+ height:108px;
51
+ transform:rotate(-90deg)
52
+ }
53
+ `;
54
+ /** Closed-shadow styles. Inner !important declarations protect the host itself
55
+ * from author resets; descendants never participate in the app cascade. */
56
+ export const prelaunchCSS = `
57
+ :host {
58
+ all:initial !important;
59
+ display:block !important;
60
+ position:fixed !important;
61
+ inset:0 !important;
62
+ z-index:20004 !important;
63
+ opacity:1 !important;
64
+ visibility:visible !important;
65
+ transform:none !important;
66
+ filter:none !important;
67
+ clip-path:none !important;
68
+ overflow:visible !important;
69
+ pointer-events:none !important;
70
+ contain:none !important;
71
+ isolation:isolate !important;
72
+ color-scheme:light dark !important
73
+ }
74
+ * {
75
+ box-sizing:border-box
76
+ }
77
+ [hidden] {
78
+ display:none !important
79
+ }
80
+ ${launchScreenCSS}#satin-launch-error {
81
+ position:fixed;
82
+ inset:0;
83
+ display:flex;
84
+ flex-direction:column;
85
+ align-items:center;
86
+ justify-content:center;
87
+ padding:32px;
88
+ background:var(--satin-launch-background);
89
+ color:var(--satin-launch-ink);
90
+ font:17px/1.45 system-ui;
91
+ text-align:center;
92
+ pointer-events:auto;
93
+ overflow:auto
94
+ }
95
+ #satin-launch-error h1 {
96
+ font-size:24px;
97
+ margin:0 0 12px
98
+ }
99
+ #satin-launch-error p {
100
+ max-width:420px;
101
+ margin:17px 0;
102
+ white-space:pre-wrap;
103
+ overflow-wrap:anywhere
104
+ }
105
+ #satin-launch-error .detail {
106
+ font-size:13px;
107
+ opacity:.65
108
+ }
109
+ #satin-launch-error button {
110
+ font:600 17px system-ui;
111
+ color:#007aff;
112
+ background:transparent;
113
+ border:0;
114
+ padding:16px 32px;
115
+ min-height:44px
116
+ }
117
+ `;
@@ -1,5 +1,21 @@
1
1
  import type { DownloadProgress } from './launchScreen.js';
2
- interface LaunchScreenController {
2
+ export interface LaunchScreenController {
3
+ readonly version: 1;
4
+ stylesReady(): Promise<void>;
5
+ error(reason: string, title?: string, retry?: () => void, detail?: string): void;
6
+ recover(): void;
7
+ appearance(background: string, ink: string): void;
8
+ snapshot(): {
9
+ version: 1;
10
+ state: string;
11
+ error: string;
12
+ coverVisible: boolean;
13
+ ringVisible: boolean;
14
+ label: string | null;
15
+ offset: string;
16
+ box: number[];
17
+ background: string;
18
+ };
3
19
  progress(progress: DownloadProgress | null): void;
4
20
  updated(start?: 0 | 100): Promise<boolean>;
5
21
  complete(): Promise<boolean>;
@@ -7,13 +23,24 @@ interface LaunchScreenController {
7
23
  fail(onRetired: () => void): void;
8
24
  restore(): void;
9
25
  }
10
- export declare function installLaunchScreenRuntime(): LaunchScreenController;
26
+ export interface PrelaunchOptions {
27
+ readonly markup: string;
28
+ readonly css: string;
29
+ readonly scope: string;
30
+ readonly background: string;
31
+ readonly darkBackground: string;
32
+ readonly darkInk: string;
33
+ readonly nonce?: string;
34
+ readonly cover?: boolean;
35
+ }
36
+ export declare function installLaunchScreenRuntime(options: PrelaunchOptions): LaunchScreenController;
37
+ export declare function prelaunchController(): LaunchScreenController;
38
+ export declare function showLaunchFailure(reason: string, title?: string): void;
11
39
  export declare function setLaunchScreen(visible: boolean): void;
12
40
  export declare function setLaunchProgress(progress: DownloadProgress | null): void;
13
41
  /** Complete a visible arc before a cold-update document replacement. */
14
42
  export declare function completeLaunchProgress(): Promise<boolean>;
15
43
  /** Confirm an applied update on its successor document before revealing the app. */
16
44
  export declare function showAppliedUpdate(): Promise<boolean>;
17
- /** Serialized after the declarative cover, before modules and their first paint. */
45
+ /** Runs immediately after the inline environment, before application modules. */
18
46
  export declare function resumeAppliedUpdate(scope: string, buildId?: string): void;
19
- export {};