@nativescript/vite 8.0.0-alpha.67 → 8.0.0-alpha.68

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 (170) hide show
  1. package/configuration/angular.js +78 -182
  2. package/configuration/angular.js.map +1 -1
  3. package/configuration/base.js +10 -139
  4. package/configuration/base.js.map +1 -1
  5. package/helpers/app-css-state.d.ts +20 -0
  6. package/helpers/app-css-state.js.map +1 -1
  7. package/helpers/main-entry.js +44 -4
  8. package/helpers/main-entry.js.map +1 -1
  9. package/helpers/ns-core-external-urls.d.ts +32 -0
  10. package/helpers/ns-core-external-urls.js +107 -0
  11. package/helpers/ns-core-external-urls.js.map +1 -0
  12. package/helpers/typescript-check.js +2 -2
  13. package/helpers/typescript-check.js.map +1 -1
  14. package/helpers/workers.d.ts +0 -8
  15. package/helpers/workers.js +43 -381
  16. package/helpers/workers.js.map +1 -1
  17. package/hmr/client/framework-client-strategy.d.ts +17 -9
  18. package/hmr/client/framework-client-strategy.js +3 -4
  19. package/hmr/client/framework-client-strategy.js.map +1 -1
  20. package/hmr/client/hmr-pending-overlay.js +2 -21
  21. package/hmr/client/hmr-pending-overlay.js.map +1 -1
  22. package/hmr/client/index.d.ts +9 -1
  23. package/hmr/client/index.js +34 -2621
  24. package/hmr/client/index.js.map +1 -1
  25. package/hmr/client/overlay-driver.d.ts +10 -0
  26. package/hmr/client/overlay-driver.js +33 -0
  27. package/hmr/client/overlay-driver.js.map +1 -0
  28. package/hmr/client/protocol-dispatch.d.ts +8 -0
  29. package/hmr/client/protocol-dispatch.js +309 -0
  30. package/hmr/client/protocol-dispatch.js.map +1 -0
  31. package/hmr/client/root-reset.d.ts +15 -0
  32. package/hmr/client/root-reset.js +358 -0
  33. package/hmr/client/root-reset.js.map +1 -0
  34. package/hmr/client/strategy-loader.d.ts +24 -0
  35. package/hmr/client/strategy-loader.js +83 -0
  36. package/hmr/client/strategy-loader.js.map +1 -0
  37. package/hmr/client/update-pipeline.d.ts +27 -0
  38. package/hmr/client/update-pipeline.js +464 -0
  39. package/hmr/client/update-pipeline.js.map +1 -0
  40. package/hmr/client/utils.js +3 -2
  41. package/hmr/client/utils.js.map +1 -1
  42. package/hmr/client/ws-transport.d.ts +21 -0
  43. package/hmr/client/ws-transport.js +344 -0
  44. package/hmr/client/ws-transport.js.map +1 -0
  45. package/hmr/frameworks/angular/build/angular-linker.js +4 -0
  46. package/hmr/frameworks/angular/build/angular-linker.js.map +1 -1
  47. package/hmr/frameworks/angular/build/linker-stats.d.ts +18 -0
  48. package/hmr/frameworks/angular/build/linker-stats.js +29 -0
  49. package/hmr/frameworks/angular/build/linker-stats.js.map +1 -0
  50. package/hmr/frameworks/angular/build/ts-fallback-transform.d.ts +5 -0
  51. package/hmr/frameworks/angular/build/ts-fallback-transform.js +138 -0
  52. package/hmr/frameworks/angular/build/ts-fallback-transform.js.map +1 -0
  53. package/hmr/frameworks/angular/build/worker-url-preserve.d.ts +5 -0
  54. package/hmr/frameworks/angular/build/worker-url-preserve.js +140 -0
  55. package/hmr/frameworks/angular/build/worker-url-preserve.js.map +1 -0
  56. package/hmr/frameworks/angular/client/index.d.ts +3 -0
  57. package/hmr/frameworks/angular/client/index.js +28 -42
  58. package/hmr/frameworks/angular/client/index.js.map +1 -1
  59. package/hmr/frameworks/angular/server/ng-component-route.d.ts +43 -0
  60. package/hmr/frameworks/angular/server/ng-component-route.js +113 -0
  61. package/hmr/frameworks/angular/server/ng-component-route.js.map +1 -0
  62. package/hmr/frameworks/angular/server/strategy.js +15 -11
  63. package/hmr/frameworks/angular/server/strategy.js.map +1 -1
  64. package/hmr/frameworks/angular/server/websocket-angular-hot-update.d.ts +12 -0
  65. package/hmr/frameworks/angular/server/websocket-angular-hot-update.js +39 -0
  66. package/hmr/frameworks/angular/server/websocket-angular-hot-update.js.map +1 -1
  67. package/hmr/frameworks/react/client/strategy.d.ts +12 -0
  68. package/hmr/frameworks/react/client/strategy.js +27 -0
  69. package/hmr/frameworks/react/client/strategy.js.map +1 -0
  70. package/hmr/frameworks/solid/client/strategy.d.ts +7 -0
  71. package/hmr/frameworks/solid/client/strategy.js +391 -0
  72. package/hmr/frameworks/solid/client/strategy.js.map +1 -0
  73. package/hmr/frameworks/solid/server/strategy.js +2 -0
  74. package/hmr/frameworks/solid/server/strategy.js.map +1 -1
  75. package/hmr/frameworks/typescript/client/strategy.d.ts +7 -0
  76. package/hmr/frameworks/typescript/client/strategy.js +418 -0
  77. package/hmr/frameworks/typescript/client/strategy.js.map +1 -0
  78. package/hmr/frameworks/typescript/server/strategy.js +2 -0
  79. package/hmr/frameworks/typescript/server/strategy.js.map +1 -1
  80. package/hmr/frameworks/vue/client/index.d.ts +1 -1
  81. package/hmr/frameworks/vue/client/index.js +40 -319
  82. package/hmr/frameworks/vue/client/index.js.map +1 -1
  83. package/hmr/frameworks/vue/client/navigate-app.d.ts +3 -0
  84. package/hmr/frameworks/vue/client/navigate-app.js +178 -0
  85. package/hmr/frameworks/vue/client/navigate-app.js.map +1 -0
  86. package/hmr/frameworks/vue/client/strategy.d.ts +1 -1
  87. package/hmr/frameworks/vue/client/strategy.js +9 -14
  88. package/hmr/frameworks/vue/client/strategy.js.map +1 -1
  89. package/hmr/frameworks/vue/client/vue-sfc-update-overlay.js +2 -20
  90. package/hmr/frameworks/vue/client/vue-sfc-update-overlay.js.map +1 -1
  91. package/hmr/frameworks/vue/server/strategy.js +3 -0
  92. package/hmr/frameworks/vue/server/strategy.js.map +1 -1
  93. package/hmr/server/css-connect-sync.d.ts +28 -0
  94. package/hmr/server/css-connect-sync.js +78 -0
  95. package/hmr/server/css-connect-sync.js.map +1 -0
  96. package/hmr/server/framework-strategy.d.ts +30 -0
  97. package/hmr/server/websocket-hmr-pending.d.ts +0 -8
  98. package/hmr/server/websocket-hmr-pending.js +0 -22
  99. package/hmr/server/websocket-hmr-pending.js.map +1 -1
  100. package/hmr/server/websocket-hot-update.d.ts +3 -0
  101. package/hmr/server/websocket-hot-update.js +63 -36
  102. package/hmr/server/websocket-hot-update.js.map +1 -1
  103. package/hmr/server/websocket-module-bindings.js +23 -5
  104. package/hmr/server/websocket-module-bindings.js.map +1 -1
  105. package/hmr/server/websocket-module-specifiers.d.ts +0 -5
  106. package/hmr/server/websocket-module-specifiers.js +0 -28
  107. package/hmr/server/websocket-module-specifiers.js.map +1 -1
  108. package/hmr/server/websocket-ns-m-paths.d.ts +18 -0
  109. package/hmr/server/websocket-ns-m-paths.js +79 -0
  110. package/hmr/server/websocket-ns-m-paths.js.map +1 -1
  111. package/hmr/server/websocket-ns-m-request.d.ts +2 -1
  112. package/hmr/server/websocket-ns-m-request.js +3 -2
  113. package/hmr/server/websocket-ns-m-request.js.map +1 -1
  114. package/hmr/server/websocket-ns-m.js +100 -374
  115. package/hmr/server/websocket-ns-m.js.map +1 -1
  116. package/hmr/server/websocket-served-module-helpers.d.ts +7 -0
  117. package/hmr/server/websocket-served-module-helpers.js +93 -69
  118. package/hmr/server/websocket-served-module-helpers.js.map +1 -1
  119. package/hmr/server/websocket.d.ts +0 -1
  120. package/hmr/server/websocket.js +27 -2
  121. package/hmr/server/websocket.js.map +1 -1
  122. package/hmr/shared/ns-globals.d.ts +0 -1
  123. package/hmr/shared/ns-globals.js.map +1 -1
  124. package/hmr/shared/protocol.d.ts +14 -0
  125. package/hmr/shared/protocol.js.map +1 -1
  126. package/hmr/shared/runtime/dev-overlay-ios.d.ts +87 -0
  127. package/hmr/shared/runtime/dev-overlay-ios.js +521 -0
  128. package/hmr/shared/runtime/dev-overlay-ios.js.map +1 -0
  129. package/hmr/shared/runtime/dev-overlay-shared.d.ts +58 -0
  130. package/hmr/shared/runtime/dev-overlay-shared.js +116 -0
  131. package/hmr/shared/runtime/dev-overlay-shared.js.map +1 -0
  132. package/hmr/shared/runtime/dev-overlay-tree.d.ts +59 -0
  133. package/hmr/shared/runtime/dev-overlay-tree.js +712 -0
  134. package/hmr/shared/runtime/dev-overlay-tree.js.map +1 -0
  135. package/hmr/shared/runtime/dev-overlay.d.ts +15 -107
  136. package/hmr/shared/runtime/dev-overlay.js +19 -1336
  137. package/hmr/shared/runtime/dev-overlay.js.map +1 -1
  138. package/hmr/shared/runtime/overlay-flag.d.ts +9 -0
  139. package/hmr/shared/runtime/overlay-flag.js +34 -0
  140. package/hmr/shared/runtime/overlay-flag.js.map +1 -0
  141. package/hmr/shared/runtime/root-placeholder.js +3 -2
  142. package/hmr/shared/runtime/root-placeholder.js.map +1 -1
  143. package/hmr/shared/runtime/session-bootstrap.js +2 -1
  144. package/hmr/shared/runtime/session-bootstrap.js.map +1 -1
  145. package/hmr/shared/runtime/vendor-resolve.d.ts +22 -0
  146. package/hmr/shared/runtime/vendor-resolve.js +45 -0
  147. package/hmr/shared/runtime/vendor-resolve.js.map +1 -0
  148. package/hmr/shared/vendor/manifest-collect.js +0 -3
  149. package/hmr/shared/vendor/manifest-collect.js.map +1 -1
  150. package/package.json +1 -3
  151. package/runtime/core-aliases-early.js +3 -2
  152. package/runtime/core-aliases-early.js.map +1 -1
  153. package/helpers/module-runner-patch.d.ts +0 -3
  154. package/helpers/module-runner-patch.js +0 -63
  155. package/helpers/module-runner-patch.js.map +0 -1
  156. package/hmr/server/websocket-css-hot-update.d.ts +0 -33
  157. package/hmr/server/websocket-css-hot-update.js +0 -65
  158. package/hmr/server/websocket-css-hot-update.js.map +0 -1
  159. package/hmr/shared/runtime/http-only-boot.d.ts +0 -2
  160. package/hmr/shared/runtime/http-only-boot.js +0 -172
  161. package/hmr/shared/runtime/http-only-boot.js.map +0 -1
  162. package/hmr/vendor-bootstrap.d.ts +0 -1
  163. package/hmr/vendor-bootstrap.js +0 -30
  164. package/hmr/vendor-bootstrap.js.map +0 -1
  165. package/shims/react-reconciler-constants.d.ts +0 -14
  166. package/shims/react-reconciler-constants.js +0 -21
  167. package/shims/react-reconciler-constants.js.map +0 -1
  168. package/shims/react-reconciler.d.ts +0 -8
  169. package/shims/react-reconciler.js +0 -15
  170. package/shims/react-reconciler.js.map +0 -1
@@ -1,28 +1,24 @@
1
- import { BOOT_PLACEHOLDER_MOTION, computeBootProgressFillScale, formatBootDetailLine, formatBootPrimaryLine } from './boot-placeholder-ui.js';
2
- import { BOOT_TITLE, DEFAULT_SNAPSHOT, createBootOverlaySnapshot, createConnectionOverlaySnapshot, createUpdateOverlaySnapshot } from './dev-overlay-snapshots.js';
3
- import { getGlobalScope } from './global-scope.js';
4
- // Re-export the snapshot model so existing `./dev-overlay.js` importers keep working.
5
- export { createBootOverlaySnapshot, createConnectionOverlaySnapshot, createUpdateOverlaySnapshot } from './dev-overlay-snapshots.js';
6
- const DEFAULT_OVERLAY_POSITION = 'top';
7
- function getOverlayGlobal() {
8
- return getGlobalScope();
9
- }
10
1
  /**
11
- * Resolve the configured live-overlay position.
2
+ * Dev overlay public API: boot / connection / update stage setters plus the
3
+ * auto-hide timing policy. Rendering is delegated to two backends behind the
4
+ * shared snapshot/state model (`dev-overlay-shared.ts`):
12
5
  *
13
- * Reads `globalThis.__NS_HMR_OVERLAY_POSITION__` so a project can
14
- * override the default at boot time (e.g. inside `app.ts` before the
15
- * Vite session bootstraps). Falls back to 'top' which gives the
16
- * toast-style chip with a slide-in animation and safe-area padding.
6
+ * - `dev-overlay-ios.ts` — dedicated UIWindow above `UIWindowLevelAlert`, so
7
+ * the overlay stacks above modals/sheets/system alerts (preferred on iOS)
8
+ * - `dev-overlay-tree.ts` — boot placeholder + in-tree toast chip (Android,
9
+ * and the fallback when the iOS window can't be built)
17
10
  */
18
- export function getHmrDevOverlayPosition() {
19
- const g = getOverlayGlobal();
20
- const stored = g.__NS_HMR_OVERLAY_POSITION__;
21
- if (stored === 'top' || stored === 'bottom' || stored === 'center') {
22
- return stored;
23
- }
24
- return DEFAULT_OVERLAY_POSITION;
25
- }
11
+ import { DEFAULT_SNAPSHOT, createBootOverlaySnapshot, createConnectionOverlaySnapshot, createUpdateOverlaySnapshot } from './dev-overlay-snapshots.js';
12
+ import { getHmrDevOverlayPosition, getOverlayGlobal, getRuntimeState } from './dev-overlay-shared.js';
13
+ import { applySnapshotToBootRefs, applySnapshotToLiveRefs, buildBootOverlayRefs, ensureLiveOverlayRefs, updateBootStatusLabel } from './dev-overlay-tree.js';
14
+ import { applySnapshotToIosRefs, ensureIosOverlayRefs, getIosOverlayHost } from './dev-overlay-ios.js';
15
+ import { resolveOverlayEnabled } from './overlay-flag.js';
16
+ // Re-export the snapshot model and backend pure helpers so existing
17
+ // `./dev-overlay.js` importers keep working.
18
+ export { createBootOverlaySnapshot, createConnectionOverlaySnapshot, createUpdateOverlaySnapshot } from './dev-overlay-snapshots.js';
19
+ export { getHmrDevOverlayPosition } from './dev-overlay-shared.js';
20
+ export { computeAndroidToastMargin } from './dev-overlay-tree.js';
21
+ export { computeIosOverlayLayout, computeIosOverlayWindowLevel } from './dev-overlay-ios.js';
26
22
  /**
27
23
  * Imperative setter for the live-overlay position. Re-applies the
28
24
  * current snapshot so the change is visible without waiting for the
@@ -38,1302 +34,6 @@ export function setHmrDevOverlayPosition(position) {
38
34
  const state = getRuntimeState();
39
35
  applyRuntimeSnapshot(state.snapshot);
40
36
  }
41
- function getRuntimeState() {
42
- const g = getOverlayGlobal();
43
- if (!g.__NS_HMR_DEV_OVERLAY_STATE__) {
44
- g.__NS_HMR_DEV_OVERLAY_STATE__ = {
45
- snapshot: { ...DEFAULT_SNAPSHOT },
46
- bootRefs: null,
47
- liveRefs: null,
48
- iosRefs: null,
49
- iosBuildFailed: false,
50
- verbose: false,
51
- updateAutoHideTimer: null,
52
- updateCycleStartedAt: 0,
53
- };
54
- }
55
- const state = g.__NS_HMR_DEV_OVERLAY_STATE__;
56
- // Backfill newer fields for legacy state objects (e.g. after hot reload)
57
- // so we never observe an undefined iosRefs/iosBuildFailed at runtime.
58
- if (typeof state.iosRefs === 'undefined')
59
- state.iosRefs = null;
60
- if (typeof state.iosBuildFailed === 'undefined')
61
- state.iosBuildFailed = false;
62
- if (typeof state.updateAutoHideTimer === 'undefined')
63
- state.updateAutoHideTimer = null;
64
- if (typeof state.updateCycleStartedAt !== 'number')
65
- state.updateCycleStartedAt = 0;
66
- return state;
67
- }
68
- function resolveCoreExport(name) {
69
- const g = getOverlayGlobal();
70
- try {
71
- const reg = g.__nsVendorRegistry;
72
- if (reg && typeof reg.get === 'function') {
73
- const mod = reg.get('@nativescript/core');
74
- const ns = (mod && (mod.default ?? mod)) || mod;
75
- if (ns && ns[name]) {
76
- return ns[name];
77
- }
78
- }
79
- }
80
- catch { }
81
- try {
82
- if (g && g[name]) {
83
- return g[name];
84
- }
85
- }
86
- catch { }
87
- try {
88
- const req = g.__nsVendorRequire || g.__nsRequire || g.require;
89
- if (typeof req === 'function') {
90
- const mod = req('@nativescript/core');
91
- const ns = (mod && (mod.default ?? mod)) || mod;
92
- if (ns && ns[name]) {
93
- return ns[name];
94
- }
95
- }
96
- }
97
- catch { }
98
- try {
99
- const nativeReq = g.__nativeRequire;
100
- if (typeof nativeReq === 'function') {
101
- const mod = nativeReq('@nativescript/core', '/');
102
- const ns = (mod && (mod.default ?? mod)) || mod;
103
- if (ns && ns[name]) {
104
- return ns[name];
105
- }
106
- }
107
- }
108
- catch { }
109
- return undefined;
110
- }
111
- function asColor(value) {
112
- try {
113
- const Color = resolveCoreExport('Color');
114
- if (Color) {
115
- return new Color(value);
116
- }
117
- }
118
- catch { }
119
- return value;
120
- }
121
- function formatStatusText(snapshot) {
122
- const progressText = typeof snapshot.progress === 'number' ? ` (${Math.round(Number(snapshot.progress))}%)` : '';
123
- const primary = `${snapshot.phase || ''}${progressText}`.trim();
124
- return [primary, snapshot.detail].filter(Boolean).join('\n');
125
- }
126
- function buildBootOverlayRefs(snapshot) {
127
- const Page = resolveCoreExport('Page');
128
- const StackLayout = resolveCoreExport('StackLayout');
129
- const Label = resolveCoreExport('Label');
130
- const ActivityIndicator = resolveCoreExport('ActivityIndicator');
131
- if (!Page || !StackLayout || !Label) {
132
- return null;
133
- }
134
- const page = new Page();
135
- page.actionBarHidden = true;
136
- const root = new StackLayout();
137
- root.padding = 24;
138
- root.verticalAlignment = 'middle';
139
- root.horizontalAlignment = 'stretch';
140
- const titleLabel = new Label();
141
- titleLabel.text = BOOT_TITLE;
142
- titleLabel.textAlignment = 'center';
143
- titleLabel.textWrap = true;
144
- titleLabel.fontSize = 22;
145
- titleLabel.fontWeight = '600';
146
- const statusLabel = new Label();
147
- statusLabel.textAlignment = 'center';
148
- statusLabel.textWrap = true;
149
- statusLabel.fontSize = 14;
150
- statusLabel.marginTop = 12;
151
- const activityIndicator = ActivityIndicator
152
- ? (() => {
153
- const indicator = new ActivityIndicator();
154
- indicator.marginTop = 16;
155
- indicator.width = 28;
156
- indicator.height = 28;
157
- indicator.horizontalAlignment = 'center';
158
- return indicator;
159
- })()
160
- : null;
161
- root.addChild(titleLabel);
162
- root.addChild(statusLabel);
163
- if (activityIndicator) {
164
- root.addChild(activityIndicator);
165
- }
166
- page.content = root;
167
- const refs = {
168
- page,
169
- root,
170
- titleLabel,
171
- statusLabel,
172
- activityIndicator,
173
- };
174
- applySnapshotToBootRefs(refs, snapshot);
175
- return refs;
176
- }
177
- function applySnapshotToBootRefs(refs, snapshot) {
178
- if (!refs) {
179
- return;
180
- }
181
- refs.page.actionBarHidden = true;
182
- refs.page.backgroundColor = asColor(snapshot.tone === 'error' ? '#b4181068' : '#a1771683');
183
- refs.root.visibility = snapshot.visible && snapshot.mode === 'boot' ? 'visible' : 'collapse';
184
- refs.titleLabel.text = BOOT_TITLE;
185
- refs.titleLabel.color = asColor(snapshot.tone === 'error' ? '#b41810e6' : '#563e3fb1');
186
- refs.statusLabel.text = formatStatusText(snapshot) || 'Preparing the HTTP HMR bootstrap (4%)';
187
- refs.statusLabel.color = asColor(snapshot.tone === 'error' ? '#b41810e6' : '#563e3fb1');
188
- if (refs.activityIndicator) {
189
- refs.activityIndicator.busy = !!snapshot.busy;
190
- refs.activityIndicator.visibility = snapshot.visible && snapshot.mode === 'boot' && snapshot.busy ? 'visible' : 'collapse';
191
- }
192
- }
193
- function findBootActivityIndicator() {
194
- const g = getOverlayGlobal();
195
- if (g.__NS_DEV_BOOT_ACTIVITY_INDICATOR__) {
196
- return g.__NS_DEV_BOOT_ACTIVITY_INDICATOR__;
197
- }
198
- try {
199
- const frame = g.__NS_DEV_PLACEHOLDER_ROOT_VIEW__;
200
- if (!frame)
201
- return null;
202
- const page = frame.currentPage || frame._currentEntry?.resolvedPage;
203
- const content = page?.content;
204
- const children = content?.getChildrenCount?.() || 0;
205
- if (children >= 3) {
206
- const indicator = content.getChildAt?.(2);
207
- if (indicator && typeof indicator.busy !== 'undefined') {
208
- g.__NS_DEV_BOOT_ACTIVITY_INDICATOR__ = indicator;
209
- return indicator;
210
- }
211
- }
212
- }
213
- catch { }
214
- return null;
215
- }
216
- function findBootStatusLabel() {
217
- const g = getOverlayGlobal();
218
- // Try direct reference first
219
- if (g.__NS_DEV_BOOT_STATUS_LABEL__) {
220
- return g.__NS_DEV_BOOT_STATUS_LABEL__;
221
- }
222
- // Fallback: walk from placeholder root view
223
- try {
224
- const frame = g.__NS_DEV_PLACEHOLDER_ROOT_VIEW__;
225
- if (!frame)
226
- return null;
227
- const page = frame.currentPage || frame._currentEntry?.resolvedPage;
228
- if (!page)
229
- return null;
230
- const content = page.content;
231
- if (!content)
232
- return null;
233
- // StackLayout with children: titleLabel, statusLabel
234
- const children = content.getChildrenCount?.() || 0;
235
- if (children >= 2) {
236
- const label = content.getChildAt?.(1);
237
- if (label && typeof label.text !== 'undefined') {
238
- // Cache for next time
239
- g.__NS_DEV_BOOT_STATUS_LABEL__ = label;
240
- return label;
241
- }
242
- }
243
- }
244
- catch { }
245
- return null;
246
- }
247
- function findBootDetailLabel() {
248
- const g = getOverlayGlobal();
249
- return g.__NS_DEV_BOOT_DETAIL_LABEL__ || null;
250
- }
251
- function findBootProgressFill() {
252
- const g = getOverlayGlobal();
253
- return g.__NS_DEV_BOOT_PROGRESS_FILL__ || null;
254
- }
255
- function updateBootStatusLabel(snapshot) {
256
- const statusLabel = findBootStatusLabel();
257
- const detailLabel = findBootDetailLabel();
258
- const progressFill = findBootProgressFill();
259
- const activityIndicator = findBootActivityIndicator();
260
- // New (card) layout: phase line + detail line live in separate
261
- // labels so the typography can differ. Legacy (single-label)
262
- // layout: keep the original combined "phase (X%)\ndetail" text so
263
- // nothing visually regresses for runtimes still attached to the
264
- // older placeholder shape.
265
- const hasSplitLabels = !!detailLabel;
266
- const phaseLine = formatBootPrimaryLine(snapshot);
267
- const detailLine = formatBootDetailLine(snapshot);
268
- const combinedText = formatStatusText(snapshot) || 'Preparing the HTTP HMR bootstrap (4%)';
269
- if (!statusLabel) {
270
- if (activityIndicator) {
271
- try {
272
- activityIndicator.busy = !!snapshot.busy;
273
- activityIndicator.visibility = snapshot.visible && snapshot.mode === 'boot' && snapshot.busy ? 'visible' : 'collapse';
274
- }
275
- catch { }
276
- }
277
- applyBootProgressFill(progressFill, snapshot);
278
- return;
279
- }
280
- try {
281
- statusLabel.text = hasSplitLabels ? phaseLine || 'Preparing the HTTP HMR bootstrap' : combinedText;
282
- // Card layout uses the calibrated phase-text colour from the
283
- // palette; legacy single-label layout keeps the original muted
284
- // brown so we don't visually regress mid-session.
285
- const phaseColorHex = snapshot.tone === 'error' ? '#B91C1C' : hasSplitLabels ? '#475569' : '#563e3fb1';
286
- statusLabel.color = asColor(phaseColorHex);
287
- if (typeof statusLabel.requestLayout === 'function') {
288
- statusLabel.requestLayout();
289
- }
290
- const parent = statusLabel.parent;
291
- if (parent && typeof parent.requestLayout === 'function') {
292
- parent.requestLayout();
293
- }
294
- }
295
- catch { }
296
- if (detailLabel) {
297
- try {
298
- detailLabel.text = detailLine;
299
- detailLabel.color = asColor(snapshot.tone === 'error' ? '#DC2626' : '#94A3B8');
300
- detailLabel.visibility = detailLine ? 'visible' : 'collapse';
301
- }
302
- catch { }
303
- }
304
- applyBootProgressFill(progressFill, snapshot);
305
- if (activityIndicator) {
306
- try {
307
- activityIndicator.busy = !!snapshot.busy;
308
- activityIndicator.visibility = snapshot.visible && snapshot.mode === 'boot' && snapshot.busy ? 'visible' : 'collapse';
309
- }
310
- catch { }
311
- }
312
- }
313
- // Drive the progress fill scaleX from the snapshot. Uses NS's view
314
- // animate API for a smooth 220 ms easeOut between heartbeat ticks; a
315
- // monotonic ratchet on `globalThis.__NS_DEV_BOOT_PROGRESS_LAST_SCALE__`
316
- // guards against the fill snapping backwards if a less-progressed
317
- // snapshot ever lands between ticks (mirrors the JS-side
318
- // `applyMonotonicBootProgress` contract).
319
- function applyBootProgressFill(progressFill, snapshot) {
320
- if (!progressFill)
321
- return;
322
- const g = getOverlayGlobal();
323
- const isError = snapshot.tone === 'error';
324
- progressFill.backgroundColor = asColor(isError ? '#B41810' : '#3B6FE5');
325
- const targetScale = computeBootProgressFillScale(snapshot.progress ?? null);
326
- const previousRaw = Number(g.__NS_DEV_BOOT_PROGRESS_LAST_SCALE__);
327
- const previous = Number.isFinite(previousRaw) ? previousRaw : 0;
328
- const next = Math.max(previous, targetScale);
329
- g.__NS_DEV_BOOT_PROGRESS_LAST_SCALE__ = next;
330
- try {
331
- // NS view.animate scales around `originX`/`originY`; the
332
- // placeholder builder pins `originX = 0` so the fill grows
333
- // rightward. animate() may be unavailable in some headless
334
- // test environments — fall through to a direct property set.
335
- if (typeof progressFill.animate === 'function') {
336
- progressFill
337
- .animate({
338
- scale: { x: next, y: 1 },
339
- duration: BOOT_PLACEHOLDER_MOTION.progressDurationMs,
340
- curve: 'easeOut',
341
- })
342
- .catch(() => {
343
- try {
344
- progressFill.scaleX = next;
345
- }
346
- catch { }
347
- });
348
- }
349
- else {
350
- progressFill.scaleX = next;
351
- }
352
- }
353
- catch {
354
- progressFill.scaleX = next;
355
- }
356
- }
357
- function resolveActivePage() {
358
- try {
359
- const Frame = resolveCoreExport('Frame');
360
- const frame = Frame?.topmost?.() || null;
361
- const currentPage = frame?.currentPage || frame?._currentEntry?.resolvedPage || null;
362
- if (currentPage) {
363
- return currentPage;
364
- }
365
- }
366
- catch { }
367
- try {
368
- const Application = resolveCoreExport('Application');
369
- const rootView = Application?.getRootView?.() || null;
370
- if (rootView?.currentPage) {
371
- return rootView.currentPage;
372
- }
373
- if (typeof rootView?.content !== 'undefined') {
374
- return rootView;
375
- }
376
- }
377
- catch { }
378
- return null;
379
- }
380
- function readCachedAndroidSafeAreaInsets() {
381
- const g = getOverlayGlobal();
382
- const cached = g.__NS_HMR_ANDROID_SAFE_INSETS__;
383
- if (cached && typeof cached.top === 'number') {
384
- return cached;
385
- }
386
- return null;
387
- }
388
- /**
389
- * Read the Android system-bar safe-area insets (status bar / navigation
390
- * bar / display cutout) in device-independent pixels.
391
- *
392
- * NativeScript runs every Android activity edge-to-edge, so the overlay
393
- * wrapper fills the whole window — we need these insets to keep the toast
394
- * chip out from under the system bars (see `computeAndroidToastMargin`).
395
- *
396
- * Returns null on non-Android runtimes (iOS / web / vitest) so callers
397
- * fall back to base margins. The last good reading is cached on the
398
- * global so a transient null (e.g. insets queried before the decor view
399
- * is attached) reuses the previous value instead of snapping the chip
400
- * back under the status bar.
401
- */
402
- function readAndroidSafeAreaInsets() {
403
- const g = getOverlayGlobal();
404
- // `android.*` is exposed as a global only on the NativeScript Android
405
- // runtime; its absence means iOS, web, or a test environment.
406
- const androidNs = g.android || (typeof globalThis.android !== 'undefined' ? globalThis.android : undefined);
407
- if (!androidNs) {
408
- return null;
409
- }
410
- try {
411
- const Application = resolveCoreExport('Application');
412
- const activity = Application?.android?.foregroundActivity || Application?.android?.startActivity;
413
- const decorView = activity?.getWindow?.()?.getDecorView?.();
414
- const rootInsets = decorView?.getRootWindowInsets?.();
415
- if (!rootInsets) {
416
- return readCachedAndroidSafeAreaInsets();
417
- }
418
- let density = 1;
419
- try {
420
- const metricsDensity = Number(activity?.getResources?.()?.getDisplayMetrics?.()?.density);
421
- if (Number.isFinite(metricsDensity) && metricsDensity > 0) {
422
- density = metricsDensity;
423
- }
424
- }
425
- catch { }
426
- let px = { top: 0, bottom: 0, left: 0, right: 0 };
427
- const WindowInsets = androidNs.view?.WindowInsets;
428
- const Type = WindowInsets?.Type;
429
- if (typeof rootInsets.getInsets === 'function' && Type && typeof Type.systemBars === 'function') {
430
- // API 30+: query the system bars + display cutout so notches /
431
- // camera holes are respected in landscape.
432
- let mask = Type.systemBars();
433
- if (typeof Type.displayCutout === 'function') {
434
- mask = mask | Type.displayCutout();
435
- }
436
- const i = rootInsets.getInsets(mask);
437
- px = { top: Number(i?.top) || 0, bottom: Number(i?.bottom) || 0, left: Number(i?.left) || 0, right: Number(i?.right) || 0 };
438
- }
439
- else {
440
- // API < 30 fallback.
441
- px = {
442
- top: Number(rootInsets.getSystemWindowInsetTop?.()) || 0,
443
- bottom: Number(rootInsets.getSystemWindowInsetBottom?.()) || 0,
444
- left: Number(rootInsets.getSystemWindowInsetLeft?.()) || 0,
445
- right: Number(rootInsets.getSystemWindowInsetRight?.()) || 0,
446
- };
447
- }
448
- const insets = {
449
- top: px.top / density,
450
- bottom: px.bottom / density,
451
- left: px.left / density,
452
- right: px.right / density,
453
- };
454
- g.__NS_HMR_ANDROID_SAFE_INSETS__ = insets;
455
- return insets;
456
- }
457
- catch {
458
- return readCachedAndroidSafeAreaInsets();
459
- }
460
- }
461
- function buildLiveOverlayView(snapshot) {
462
- const GridLayout = resolveCoreExport('GridLayout');
463
- const StackLayout = resolveCoreExport('StackLayout');
464
- const Label = resolveCoreExport('Label');
465
- if (!GridLayout || !StackLayout || !Label) {
466
- return null;
467
- }
468
- const overlay = new GridLayout();
469
- overlay.visibility = 'collapse';
470
- overlay.width = '100%';
471
- overlay.height = '100%';
472
- overlay.horizontalAlignment = 'stretch';
473
- overlay.verticalAlignment = 'stretch';
474
- // Toast mode lets touches reach the underlying app. We flip
475
- // isUserInteractionEnabled in applySnapshotToLiveRefs based on
476
- // the resolved position, but keep it false here as a safe default
477
- // (the panel itself is purely informational).
478
- try {
479
- overlay.isUserInteractionEnabled = false;
480
- }
481
- catch { }
482
- const panel = new StackLayout();
483
- panel.horizontalAlignment = 'center';
484
- // Vertical alignment is overridden in applySnapshotToLiveRefs
485
- // based on getHmrDevOverlayPosition(); 'middle' is the default
486
- panel.verticalAlignment = 'middle';
487
- panel.width = 320;
488
- panel.margin = 24;
489
- panel.padding = 16;
490
- const titleLabel = new Label();
491
- titleLabel.textAlignment = 'center';
492
- titleLabel.textWrap = true;
493
- titleLabel.fontSize = 18;
494
- titleLabel.fontWeight = '600';
495
- const statusLabel = new Label();
496
- statusLabel.textAlignment = 'center';
497
- statusLabel.textWrap = true;
498
- statusLabel.fontSize = 13;
499
- statusLabel.marginTop = 10;
500
- panel.addChild(titleLabel);
501
- panel.addChild(statusLabel);
502
- overlay.addChild(panel);
503
- const refs = {
504
- overlay,
505
- titleLabel,
506
- statusLabel,
507
- wasVisible: false,
508
- currentPosition: getHmrDevOverlayPosition(),
509
- };
510
- applySnapshotToLiveRefs(refs, snapshot);
511
- return refs;
512
- }
513
- function ensureLiveOverlayRefs(snapshot) {
514
- const state = getRuntimeState();
515
- const page = resolveActivePage();
516
- if (!page) {
517
- return null;
518
- }
519
- if (state.liveRefs) {
520
- if (state.liveRefs.page === page && state.liveRefs.overlay?.parent) {
521
- return state.liveRefs;
522
- }
523
- try {
524
- state.liveRefs.overlay.visibility = 'collapse';
525
- }
526
- catch { }
527
- state.liveRefs = null;
528
- }
529
- const GridLayout = resolveCoreExport('GridLayout');
530
- if (!GridLayout || typeof page.content === 'undefined') {
531
- return null;
532
- }
533
- let wrapper = page.content;
534
- if (!wrapper || !wrapper.__ns_hmr_overlay_wrapper__) {
535
- const currentContent = page.content || null;
536
- wrapper = new GridLayout();
537
- wrapper.width = '100%';
538
- wrapper.height = '100%';
539
- wrapper.__ns_hmr_overlay_wrapper__ = true;
540
- try {
541
- page.content = null;
542
- }
543
- catch { }
544
- if (currentContent) {
545
- wrapper.addChild(currentContent);
546
- }
547
- page.content = wrapper;
548
- }
549
- const liveView = buildLiveOverlayView(snapshot);
550
- if (!liveView) {
551
- return null;
552
- }
553
- wrapper.addChild(liveView.overlay);
554
- state.liveRefs = {
555
- page,
556
- wrapper,
557
- ...liveView,
558
- };
559
- return state.liveRefs;
560
- }
561
- function applySnapshotToLiveRefs(refs, snapshot) {
562
- if (!refs) {
563
- return;
564
- }
565
- // 'update' mode shares the live (in-tree) overlay chrome with
566
- // 'connection'. Both render a small panel inside the page; only
567
- // the colours, text, and (now) panel position change with the
568
- // snapshot's tone and the configured overlay position.
569
- const visible = snapshot.visible && (snapshot.mode === 'connection' || snapshot.mode === 'update');
570
- const wasVisible = !!refs.wasVisible;
571
- const position = getHmrDevOverlayPosition();
572
- const previousPosition = refs.currentPosition || position;
573
- const isToast = position !== 'center';
574
- refs.titleLabel.text = snapshot.title;
575
- refs.statusLabel.text = formatStatusText(snapshot);
576
- const textColor = snapshot.tone === 'error' ? '#b41810e6' : snapshot.tone === 'success' ? '#0e6e2fff' : '#563e3fb1';
577
- refs.titleLabel.color = asColor(textColor);
578
- refs.statusLabel.color = asColor(textColor);
579
- // Backdrop tints (centered modal only). Toast modes use a fully
580
- // transparent backdrop so the rest of the app stays visible AND
581
- // reachable; the panel itself carries enough colour to stand out.
582
- if (isToast) {
583
- refs.overlay.backgroundColor = asColor('transparent');
584
- }
585
- else {
586
- // Original wash-by-tone for centered:
587
- // error → red wash (matches existing UX)
588
- // success → richer green wash so the apply event is visible
589
- // on bright app backgrounds
590
- // default → warm orange (existing connection-overlay look)
591
- const overlayBg = snapshot.tone === 'error' ? '#b4181068' : snapshot.tone === 'success' ? '#1f883d80' : '#a1771683';
592
- refs.overlay.backgroundColor = asColor(overlayBg);
593
- }
594
- // Panel chrome — toast and centered share the same chip look,
595
- // just position differs. We keep the slightly richer green tint
596
- // for the HMR success state so it pops without needing the
597
- // backdrop wash.
598
- let panel = null;
599
- try {
600
- panel = refs.titleLabel.parent;
601
- if (panel) {
602
- const panelBg = snapshot.tone === 'success' ? '#E6F8E9FF' : '#FFFFFFFF';
603
- panel.backgroundColor = asColor(panelBg);
604
- panel.opacity = 1;
605
- panel.padding = 16;
606
- try {
607
- panel.borderRadius = 12;
608
- }
609
- catch { }
610
- // Position-aware alignment. The wrapper GridLayout fills the
611
- // page content area. NativeScript runs every Android activity
612
- // edge-to-edge, so that area extends under the status bar and
613
- // navigation bar — we fold the system-bar safe-area insets into
614
- // the chip's margin (via computeAndroidToastMargin) so a top
615
- // chip clears the status bar and a bottom chip clears the nav
616
- // bar instead of being clipped behind them. readAndroidSafeAreaInsets
617
- // returns null off-Android, so the margins collapse to their base
618
- // values (no behavioural change on iOS's in-tree fallback / tests).
619
- try {
620
- if (position === 'top' || position === 'bottom') {
621
- const m = computeAndroidToastMargin({ position, safeInsets: readAndroidSafeAreaInsets() });
622
- panel.verticalAlignment = position;
623
- panel.margin = `${m.top} ${m.right} ${m.bottom} ${m.left}`;
624
- }
625
- else {
626
- panel.verticalAlignment = 'middle';
627
- panel.margin = 24;
628
- }
629
- }
630
- catch { }
631
- // Force a tight re-measure so the StackLayout panel hugs the
632
- // CURRENT title+status text. Connection stages can swap between
633
- // frames whose text wraps to a different number of lines (e.g. a
634
- // long 'reconnecting' "Retrying ws://…" detail vs. the short
635
- // 'offline' message). Without an explicit relayout the panel can
636
- // retain the tallest height it ever measured, leaving an oversized
637
- // gap between title and message. Mirrors updateBootStatusLabel.
638
- try {
639
- refs.titleLabel.requestLayout?.();
640
- refs.statusLabel.requestLayout?.();
641
- panel.requestLayout?.();
642
- }
643
- catch { }
644
- }
645
- }
646
- catch { }
647
- // Touch passthrough for toast; centered mode keeps the
648
- // blocking modal so the dim backdrop is meaningful.
649
- try {
650
- refs.overlay.isUserInteractionEnabled = !isToast;
651
- }
652
- catch { }
653
- const positionChanged = previousPosition !== position;
654
- const justAppeared = visible && (!wasVisible || positionChanged);
655
- const justDismissed = !visible && wasVisible;
656
- if (justAppeared) {
657
- refs.overlay.visibility = 'visible';
658
- if (isToast && panel && typeof panel.animate === 'function') {
659
- animateLivePanelIn(panel, position);
660
- }
661
- else if (panel) {
662
- try {
663
- panel.translateY = 0;
664
- panel.opacity = 1;
665
- }
666
- catch { }
667
- }
668
- }
669
- else if (justDismissed) {
670
- if (isToast && panel && typeof panel.animate === 'function') {
671
- animateLivePanelOut(panel, previousPosition, () => {
672
- try {
673
- refs.overlay.visibility = 'collapse';
674
- }
675
- catch { }
676
- });
677
- }
678
- else {
679
- refs.overlay.visibility = 'collapse';
680
- }
681
- }
682
- else {
683
- // Steady-state refresh (e.g. offline → reconnecting → offline while
684
- // already visible): no slide animation fires here, so clear any
685
- // transform residue a previously-interrupted slide-in/out may have
686
- // left on the panel. Otherwise the chip can appear shifted or
687
- // faded across rapid connection-stage swaps.
688
- if (visible && panel) {
689
- try {
690
- panel.translateY = 0;
691
- panel.opacity = 1;
692
- }
693
- catch { }
694
- }
695
- refs.overlay.visibility = visible ? 'visible' : 'collapse';
696
- }
697
- if (typeof refs.wasVisible !== 'undefined')
698
- refs.wasVisible = visible;
699
- if (typeof refs.currentPosition !== 'undefined')
700
- refs.currentPosition = position;
701
- }
702
- /**
703
- * Slide-in animation for the in-tree toast panel.
704
- *
705
- * NativeScript's `View.animate({ translate, opacity, duration, curve })`
706
- * is widely available across Core versions, so we don't depend on any
707
- * specific curve enum being importable here. We use a moderate-to-snappy
708
- * 320ms ease-out which feels close to a UIView spring without needing
709
- * platform-specific APIs.
710
- */
711
- function animateLivePanelIn(panel, position) {
712
- if (!panel || typeof panel.animate !== 'function')
713
- return;
714
- try {
715
- const startY = position === 'bottom' ? 80 : -80;
716
- panel.translateY = startY;
717
- panel.opacity = 0;
718
- const result = panel.animate({
719
- translate: { x: 0, y: 0 },
720
- opacity: 1,
721
- duration: 320,
722
- curve: 'easeOut',
723
- });
724
- if (result && typeof result.catch === 'function') {
725
- result.catch(() => {
726
- try {
727
- panel.translateY = 0;
728
- panel.opacity = 1;
729
- }
730
- catch { }
731
- });
732
- }
733
- }
734
- catch {
735
- try {
736
- panel.translateY = 0;
737
- panel.opacity = 1;
738
- }
739
- catch { }
740
- }
741
- }
742
- function animateLivePanelOut(panel, position, onComplete) {
743
- if (!panel || typeof panel.animate !== 'function') {
744
- onComplete();
745
- return;
746
- }
747
- try {
748
- const targetY = position === 'bottom' ? 80 : -80;
749
- const result = panel.animate({
750
- translate: { x: 0, y: targetY },
751
- opacity: 0,
752
- duration: 220,
753
- curve: 'easeIn',
754
- });
755
- const finish = () => {
756
- try {
757
- panel.translateY = 0;
758
- panel.opacity = 1;
759
- }
760
- catch { }
761
- onComplete();
762
- };
763
- if (result && typeof result.then === 'function') {
764
- result.then(finish, finish);
765
- }
766
- else {
767
- finish();
768
- }
769
- }
770
- catch {
771
- onComplete();
772
- }
773
- }
774
- // pure helpers for iOS window promotion. Factored out so the layout
775
- // math and window-level selection stay unit-testable without booting a
776
- // simulator. See `dev-overlay.spec.ts`.
777
- /**
778
- * Returns the UIWindow level we use for the live/connection overlay. We lift
779
- * above `UIWindowLevelAlert` so system alerts (and any app-presented modal)
780
- * stack underneath. When the platform does not expose `UIWindowLevelAlert`
781
- * we fall back to the documented constant value (2000).
782
- */
783
- export function computeIosOverlayWindowLevel(baseAlert) {
784
- if (typeof baseAlert === 'number' && Number.isFinite(baseAlert)) {
785
- return baseAlert + 1;
786
- }
787
- return 2000 + 1;
788
- }
789
- /**
790
- * Layout math for the live overlay when it runs inside its own UIWindow.
791
- * Pure, deterministic and independent of UIKit so we can verify the rules
792
- * (max panel width, position-aware placement, safe-area clamping, sane
793
- * defaults) from tests.
794
- *
795
- * `position` controls where the panel sits vertically:
796
- * - 'top': hugs `safeInsets.top + toastVerticalInset` so the chip
797
- * sits just below the notch / Dynamic Island.
798
- * - 'bottom': hugs `viewHeight - safeInsets.bottom - panelHeight -
799
- * toastVerticalInset` so the chip sits just above the
800
- * home indicator / nav bar.
801
- * - 'center': original modal placement (vertically centered, clamped
802
- * so it never crosses the top safe-area inset).
803
- */
804
- export function computeIosOverlayLayout(input) {
805
- const viewWidth = Math.max(0, Number(input.viewWidth) || 0);
806
- const viewHeight = Math.max(0, Number(input.viewHeight) || 0);
807
- const safeInsets = {
808
- top: Math.max(0, Number(input.safeInsets?.top ?? 0) || 0),
809
- bottom: Math.max(0, Number(input.safeInsets?.bottom ?? 0) || 0),
810
- left: Math.max(0, Number(input.safeInsets?.left ?? 0) || 0),
811
- right: Math.max(0, Number(input.safeInsets?.right ?? 0) || 0),
812
- };
813
- const titleHeight = Math.max(0, Number(input.titleHeight) || 0);
814
- const statusHeight = Math.max(0, Number(input.statusHeight) || 0);
815
- const horizontalMargin = Math.max(0, Number(input.horizontalMargin ?? 24));
816
- const maxPanelWidth = Math.max(0, Number(input.maxPanelWidth ?? 340));
817
- const panelPadding = Math.max(0, Number(input.panelPadding ?? 16));
818
- const interLabelSpacing = Math.max(0, Number(input.interLabelSpacing ?? 10));
819
- const minTopInset = Math.max(0, Number(input.minTopInset ?? 20));
820
- // Default to 'center' on the pure function so the existing
821
- // snapshot/layout tests remain stable; the runtime call site
822
- // (layoutIosOverlayRefs) reads the configured position from
823
- // `getHmrDevOverlayPosition()` and forwards it explicitly.
824
- const position = input.position ?? 'center';
825
- // Distance between the panel and the safe-area edge in toast
826
- // modes. 8pt mirrors the typical iOS notification chip inset and
827
- // keeps the chip from hugging the notch / home indicator.
828
- const toastVerticalInset = Math.max(0, Number(input.toastVerticalInset ?? 8));
829
- const available = Math.max(0, viewWidth - 2 * horizontalMargin - safeInsets.left - safeInsets.right);
830
- const panelWidth = Math.min(maxPanelWidth, available);
831
- const innerWidth = Math.max(0, panelWidth - 2 * panelPadding);
832
- const spacing = titleHeight > 0 && statusHeight > 0 ? interLabelSpacing : 0;
833
- const panelHeight = panelPadding * 2 + titleHeight + spacing + statusHeight;
834
- const panelX = Math.max(0, (viewWidth - panelWidth) / 2);
835
- let panelY;
836
- if (position === 'top') {
837
- // Pin to the top safe-area inset (just below notch / Dynamic
838
- // Island). Clamp non-negative for fully-NaN input.
839
- panelY = Math.max(0, safeInsets.top + toastVerticalInset);
840
- }
841
- else if (position === 'bottom') {
842
- // Pin to the bottom safe-area inset (just above home indicator
843
- // / nav bar). If the panel can't fit between the safe-area
844
- // insets we fall back to the top safe-area edge so the chip is
845
- // always visible (rather than getting clipped off-screen).
846
- const desired = viewHeight - safeInsets.bottom - panelHeight - toastVerticalInset;
847
- panelY = Math.max(safeInsets.top + minTopInset, desired);
848
- }
849
- else {
850
- // Center vertically, but never cross the top safe-area inset
851
- // (notch/Dynamic Island). Original modal placement.
852
- const centered = (viewHeight - panelHeight) / 2;
853
- panelY = Math.max(safeInsets.top + minTopInset, centered);
854
- }
855
- return {
856
- backdrop: { x: 0, y: 0, width: viewWidth, height: viewHeight },
857
- panel: { x: panelX, y: panelY, width: panelWidth, height: panelHeight },
858
- title: { x: panelPadding, y: panelPadding, width: innerWidth, height: titleHeight },
859
- status: {
860
- x: panelPadding,
861
- y: panelPadding + titleHeight + spacing,
862
- width: innerWidth,
863
- height: statusHeight,
864
- },
865
- };
866
- }
867
- /**
868
- * Margin math for the in-tree (Android) live/update toast chip.
869
- *
870
- * NativeScript enables edge-to-edge on every Android activity
871
- * (`@nativescript/core` → `application.android.ts` → `enableEdgeToEdge`),
872
- * so the page content — and therefore the overlay wrapper we inject into
873
- * it — extends underneath the status bar and navigation bar. A fixed
874
- * top/bottom margin leaves the chip tucked behind the system bars, where
875
- * it gets clipped (Android 15 / API 35 enforces edge-to-edge, so this is
876
- * now the norm rather than the exception).
877
- *
878
- * This pure helper folds the system-bar safe-area insets into the chip's
879
- * margin so the toast always clears the status bar (top position) /
880
- * navigation bar (bottom position). It is deterministic and free of
881
- * native deps so the rules stay unit-testable — the runtime reads the
882
- * live insets via `readAndroidSafeAreaInsets()` and forwards them here.
883
- *
884
- * - 'top': margin.top = baseVerticalInset + safeInsets.top
885
- * - 'bottom': margin.bottom = baseVerticalInset + safeInsets.bottom
886
- * - 'center': fixed `centerMargin` all around (vertically centered, so
887
- * it never approaches the system bars on normal viewports).
888
- *
889
- * Horizontal margins always include the relevant safe-area inset so a
890
- * landscape display cutout / gesture pill doesn't clip the chip edge.
891
- */
892
- export function computeAndroidToastMargin(input) {
893
- // Clamp to a finite, non-negative number; fall back to `fallback`
894
- // for missing/NaN input so a glitched measurement can't produce a
895
- // negative or NaN margin (which NativeScript would silently drop).
896
- const clamp = (value, fallback) => {
897
- const n = Number(value);
898
- return Number.isFinite(n) && n >= 0 ? n : fallback;
899
- };
900
- const safe = {
901
- top: clamp(input.safeInsets?.top, 0),
902
- bottom: clamp(input.safeInsets?.bottom, 0),
903
- left: clamp(input.safeInsets?.left, 0),
904
- right: clamp(input.safeInsets?.right, 0),
905
- };
906
- const baseVerticalInset = clamp(input.baseVerticalInset, 8);
907
- const baseHorizontalInset = clamp(input.baseHorizontalInset, 16);
908
- const centerMargin = clamp(input.centerMargin, 24);
909
- if (input.position === 'center') {
910
- return { top: centerMargin, right: centerMargin, bottom: centerMargin, left: centerMargin };
911
- }
912
- const left = Math.round(baseHorizontalInset + safe.left);
913
- const right = Math.round(baseHorizontalInset + safe.right);
914
- if (input.position === 'bottom') {
915
- return { top: 0, right, bottom: Math.round(baseVerticalInset + safe.bottom), left };
916
- }
917
- // 'top' (and any unexpected value) hugs the status-bar safe area.
918
- return { top: Math.round(baseVerticalInset + safe.top), right, bottom: 0, left };
919
- }
920
- /**
921
- * Returns the iOS UIKit symbols we rely on if we're running on an iOS runtime
922
- * with the metadata bridge available. Returns null on Android, web, or in
923
- * tests so callers can gracefully fall back to the in-tree overlay.
924
- */
925
- function getIosOverlayHost() {
926
- const g = getOverlayGlobal();
927
- if (typeof g.UIWindow === 'undefined' || typeof g.UIApplication === 'undefined' || typeof g.UIViewController === 'undefined' || typeof g.UIView === 'undefined' || typeof g.UILabel === 'undefined' || typeof g.UIColor === 'undefined' || typeof g.UIFont === 'undefined' || typeof g.UIScreen === 'undefined') {
928
- return null;
929
- }
930
- return {
931
- UIWindow: g.UIWindow,
932
- UIViewController: g.UIViewController,
933
- UIView: g.UIView,
934
- UILabel: g.UILabel,
935
- UIColor: g.UIColor,
936
- UIFont: g.UIFont,
937
- UIApplication: g.UIApplication,
938
- UIScreen: g.UIScreen,
939
- UIWindowLevelAlert: typeof g.UIWindowLevelAlert === 'number' ? g.UIWindowLevelAlert : undefined,
940
- };
941
- }
942
- /**
943
- * Walks UIApplication.sharedApplication windows and returns the first active
944
- * UIWindowScene we can locate. On iOS 13+ every UIWindow is attached to a
945
- * scene, and we must initialise our overlay window the same way or the OS
946
- * will silently refuse to render it. Returns null when no scene is found
947
- * (older iOS versions or non-UI environments).
948
- */
949
- function findActiveWindowScene(host) {
950
- try {
951
- const app = host.UIApplication.sharedApplication;
952
- const windows = app?.windows;
953
- if (!windows || typeof windows.count !== 'number')
954
- return null;
955
- for (let i = 0; i < windows.count; i++) {
956
- const w = windows.objectAtIndex(i);
957
- const scene = w && w.windowScene;
958
- if (scene)
959
- return scene;
960
- }
961
- }
962
- catch { }
963
- return null;
964
- }
965
- function buildIosOverlayRefs(state) {
966
- const host = getIosOverlayHost();
967
- if (!host)
968
- return null;
969
- // Without a scene we can't build a modern UIWindow that actually renders.
970
- // Fall back to the in-tree overlay rather than show nothing.
971
- const scene = findActiveWindowScene(host);
972
- if (!scene) {
973
- if (state.verbose) {
974
- console.info('[ns-hmr-overlay] no active UIWindowScene; skipping iOS overlay promotion');
975
- }
976
- return null;
977
- }
978
- try {
979
- const { UIWindow, UIViewController, UIView, UILabel, UIColor, UIFont } = host;
980
- const window = UIWindow.alloc().initWithWindowScene(scene);
981
- window.windowLevel = computeIosOverlayWindowLevel(host.UIWindowLevelAlert ?? null);
982
- window.backgroundColor = UIColor.clearColor;
983
- window.hidden = true;
984
- const controller = UIViewController.new();
985
- controller.view.backgroundColor = UIColor.clearColor;
986
- window.rootViewController = controller;
987
- // UIViewAutoresizing bit masks. We mirror the UIKit constants here to
988
- // avoid depending on symbols the metadata bridge does not always
989
- // expose as top-level globals.
990
- const FLEXIBLE_LEFT_MARGIN = 1 << 0;
991
- const FLEXIBLE_WIDTH = 1 << 1;
992
- const FLEXIBLE_RIGHT_MARGIN = 1 << 2;
993
- const FLEXIBLE_TOP_MARGIN = 1 << 3;
994
- const FLEXIBLE_HEIGHT = 1 << 4;
995
- const FLEXIBLE_BOTTOM_MARGIN = 1 << 5;
996
- const backdrop = UIView.new();
997
- backdrop.backgroundColor = UIColor.colorWithRedGreenBlueAlpha(0, 0, 0, 0.35);
998
- backdrop.autoresizingMask = FLEXIBLE_WIDTH | FLEXIBLE_HEIGHT;
999
- controller.view.addSubview(backdrop);
1000
- const panel = UIView.new();
1001
- panel.backgroundColor = UIColor.whiteColor;
1002
- panel.autoresizingMask = FLEXIBLE_LEFT_MARGIN | FLEXIBLE_RIGHT_MARGIN | FLEXIBLE_TOP_MARGIN | FLEXIBLE_BOTTOM_MARGIN;
1003
- try {
1004
- panel.layer.cornerRadius = 14;
1005
- panel.layer.masksToBounds = true;
1006
- }
1007
- catch { }
1008
- controller.view.addSubview(panel);
1009
- const titleLabel = UILabel.new();
1010
- titleLabel.numberOfLines = 0;
1011
- titleLabel.textAlignment = 1; // NSTextAlignmentCenter
1012
- titleLabel.font = UIFont.boldSystemFontOfSize(16);
1013
- titleLabel.textColor = UIColor.blackColor;
1014
- panel.addSubview(titleLabel);
1015
- const statusLabel = UILabel.new();
1016
- statusLabel.numberOfLines = 0;
1017
- statusLabel.textAlignment = 1;
1018
- statusLabel.font = UIFont.systemFontOfSize(13);
1019
- statusLabel.textColor = UIColor.darkGrayColor;
1020
- panel.addSubview(statusLabel);
1021
- // Subtle drop-shadow so the toast chip reads against light app
1022
- // content (white-on-white is invisible). The error / centered
1023
- // branches still get the dim backdrop, so the shadow is mostly
1024
- // a no-op for them — but it's a one-time setup.
1025
- try {
1026
- panel.layer.shadowColor = UIColor.blackColor.CGColor;
1027
- panel.layer.shadowOpacity = 0.18;
1028
- panel.layer.shadowRadius = 8;
1029
- panel.layer.shadowOffset = { width: 0, height: 2 };
1030
- panel.layer.masksToBounds = false;
1031
- }
1032
- catch { }
1033
- // `wasVisible` / `currentPosition` are mutated by
1034
- // applySnapshotToIosRefs when the snapshot triggers a slide-in
1035
- // or slide-out. They start in the "hidden" state so the very
1036
- // first visible snapshot animates in cleanly.
1037
- return {
1038
- window,
1039
- controller,
1040
- backdrop,
1041
- panel,
1042
- titleLabel,
1043
- statusLabel,
1044
- wasVisible: false,
1045
- currentPosition: getHmrDevOverlayPosition(),
1046
- };
1047
- }
1048
- catch (err) {
1049
- console.warn('[ns-hmr-overlay] iOS overlay construction failed:', err?.message || err);
1050
- return null;
1051
- }
1052
- }
1053
- function ensureIosOverlayRefs(state) {
1054
- if (state.iosRefs)
1055
- return state.iosRefs;
1056
- if (state.iosBuildFailed)
1057
- return null;
1058
- const built = buildIosOverlayRefs(state);
1059
- if (built) {
1060
- state.iosRefs = built;
1061
- }
1062
- else {
1063
- // Remember failure so we don't hammer construction on every snapshot
1064
- // update — the in-tree path will take over for this session.
1065
- state.iosBuildFailed = true;
1066
- }
1067
- return state.iosRefs;
1068
- }
1069
- function layoutIosOverlayRefs(refs, position) {
1070
- try {
1071
- const bounds = refs.controller.view.bounds;
1072
- const viewWidth = Number(bounds?.size?.width) || 0;
1073
- const viewHeight = Number(bounds?.size?.height) || 0;
1074
- const raw = refs.controller.view.safeAreaInsets;
1075
- const safeInsets = raw
1076
- ? {
1077
- top: Number(raw.top) || 0,
1078
- bottom: Number(raw.bottom) || 0,
1079
- left: Number(raw.left) || 0,
1080
- right: Number(raw.right) || 0,
1081
- }
1082
- : { top: 0, bottom: 0, left: 0, right: 0 };
1083
- // Ask UIKit what the labels want given the panel inner width. We use a
1084
- // generous height bound so nothing clips on long reconnect strings.
1085
- const panelPadding = 16;
1086
- const horizontalMargin = 24;
1087
- const maxPanelWidth = 340;
1088
- const innerWidth = Math.max(0, Math.min(maxPanelWidth, viewWidth - 2 * horizontalMargin - safeInsets.left - safeInsets.right) - 2 * panelPadding);
1089
- const titleFit = refs.titleLabel.sizeThatFits({ width: innerWidth, height: 10000 }) || { height: 0 };
1090
- const statusFit = refs.statusLabel.sizeThatFits({ width: innerWidth, height: 10000 }) || { height: 0 };
1091
- const layout = computeIosOverlayLayout({
1092
- viewWidth,
1093
- viewHeight,
1094
- safeInsets,
1095
- titleHeight: Number(titleFit.height) || 0,
1096
- statusHeight: Number(statusFit.height) || 0,
1097
- maxPanelWidth,
1098
- horizontalMargin,
1099
- panelPadding,
1100
- position,
1101
- });
1102
- const toCgRect = (rect) => ({
1103
- origin: { x: rect.x, y: rect.y },
1104
- size: { width: rect.width, height: rect.height },
1105
- });
1106
- refs.backdrop.frame = toCgRect(layout.backdrop);
1107
- refs.panel.frame = toCgRect(layout.panel);
1108
- refs.titleLabel.frame = toCgRect(layout.title);
1109
- refs.statusLabel.frame = toCgRect(layout.status);
1110
- return layout;
1111
- }
1112
- catch (err) {
1113
- console.warn('[ns-hmr-overlay] iOS overlay layout failed:', err?.message || err);
1114
- return null;
1115
- }
1116
- }
1117
- /**
1118
- * Slide-in animation for the iOS toast panel. Off-screen start frame
1119
- * lives just above (top) or below (bottom) the visible area; the panel
1120
- * snaps to its target frame with a spring so the motion feels physical
1121
- * without the heavy "settle" overshoot of a hard spring (damping 0.85
1122
- * lands quickly with a small overshoot).
1123
- */
1124
- function animateIosPanelIn(refs, position, layout) {
1125
- const g = getOverlayGlobal();
1126
- const UIView = g?.UIView;
1127
- if (!UIView)
1128
- return;
1129
- try {
1130
- const targetFrame = {
1131
- origin: { x: layout.panel.x, y: layout.panel.y },
1132
- size: { width: layout.panel.width, height: layout.panel.height },
1133
- };
1134
- // Off-screen start: distance includes a small fudge so the
1135
- // shadow blur tail isn't visible at t=0.
1136
- const startY = position === 'bottom' ? layout.backdrop.height + 24 : -(layout.panel.height + 24);
1137
- refs.panel.frame = {
1138
- origin: { x: layout.panel.x, y: startY },
1139
- size: { width: layout.panel.width, height: layout.panel.height },
1140
- };
1141
- refs.panel.alpha = 0;
1142
- try {
1143
- if (typeof UIView.animateWithDurationDelayUsingSpringWithDampingInitialSpringVelocityOptionsAnimationsCompletion === 'function') {
1144
- UIView.animateWithDurationDelayUsingSpringWithDampingInitialSpringVelocityOptionsAnimationsCompletion(0.42, 0, 0.85, 0.7, 0, () => {
1145
- refs.panel.frame = targetFrame;
1146
- refs.panel.alpha = 1;
1147
- }, null);
1148
- }
1149
- else if (typeof UIView.animateWithDurationAnimations === 'function') {
1150
- UIView.animateWithDurationAnimations(0.32, () => {
1151
- refs.panel.frame = targetFrame;
1152
- refs.panel.alpha = 1;
1153
- });
1154
- }
1155
- else {
1156
- refs.panel.frame = targetFrame;
1157
- refs.panel.alpha = 1;
1158
- }
1159
- }
1160
- catch {
1161
- refs.panel.frame = targetFrame;
1162
- refs.panel.alpha = 1;
1163
- }
1164
- }
1165
- catch { }
1166
- }
1167
- /**
1168
- * Slide-out animation for the iOS toast panel. Mirrors animateIosPanelIn:
1169
- * the panel travels to the nearest off-screen edge while fading out so
1170
- * the dismissal still feels intentional even on fast HMR cycles.
1171
- */
1172
- function animateIosPanelOut(refs, position, onComplete) {
1173
- const g = getOverlayGlobal();
1174
- const UIView = g?.UIView;
1175
- const currentFrame = refs.panel?.frame;
1176
- if (!UIView || !currentFrame) {
1177
- onComplete();
1178
- return;
1179
- }
1180
- try {
1181
- const bounds = refs.controller?.view?.bounds;
1182
- const viewHeight = Number(bounds?.size?.height) || 0;
1183
- const targetY = position === 'bottom' ? viewHeight + 24 : -(Number(currentFrame.size?.height) + 24);
1184
- const startFrame = currentFrame;
1185
- const targetFrame = {
1186
- origin: { x: Number(startFrame.origin?.x) || 0, y: targetY },
1187
- size: startFrame.size,
1188
- };
1189
- try {
1190
- if (typeof UIView.animateWithDurationDelayOptionsAnimationsCompletion === 'function') {
1191
- // UIViewAnimationOptionCurveEaseIn = 1 << 16 — accelerate
1192
- // out so the dismissal doesn't drag on screen.
1193
- UIView.animateWithDurationDelayOptionsAnimationsCompletion(0.22, 0, 1 << 16, () => {
1194
- refs.panel.frame = targetFrame;
1195
- refs.panel.alpha = 0;
1196
- }, () => onComplete());
1197
- }
1198
- else if (typeof UIView.animateWithDurationAnimationsCompletion === 'function') {
1199
- UIView.animateWithDurationAnimationsCompletion(0.22, () => {
1200
- refs.panel.frame = targetFrame;
1201
- refs.panel.alpha = 0;
1202
- }, () => onComplete());
1203
- }
1204
- else {
1205
- refs.panel.alpha = 0;
1206
- onComplete();
1207
- }
1208
- }
1209
- catch {
1210
- refs.panel.alpha = 0;
1211
- onComplete();
1212
- }
1213
- }
1214
- catch {
1215
- onComplete();
1216
- }
1217
- }
1218
- function applySnapshotToIosRefs(refs, snapshot) {
1219
- if (!refs)
1220
- return false;
1221
- try {
1222
- // 'update' mode rides the same dedicated UIWindow as
1223
- // 'connection' so the HMR apply overlay always stacks above
1224
- // modals/sheets/system alerts. The window is constructed
1225
- // lazily (ensureIosOverlayRefs) and reused for the lifetime of
1226
- // the dev session.
1227
- const visible = snapshot.visible && (snapshot.mode === 'connection' || snapshot.mode === 'update');
1228
- const wasVisible = !!refs.wasVisible;
1229
- const position = getHmrDevOverlayPosition();
1230
- const previousPosition = refs.currentPosition;
1231
- const isToast = position !== 'center';
1232
- // Touches pass through the overlay window in toast mode so
1233
- // the user can keep tapping the app while the HMR chip is
1234
- // shown. In centered mode we keep the blocking
1235
- // behaviour (the dim backdrop is itself a hint to wait).
1236
- try {
1237
- refs.window.userInteractionEnabled = !isToast;
1238
- }
1239
- catch { }
1240
- if (!visible) {
1241
- // Animate out before hiding the window so the dismissal
1242
- // has a discoverable motion. Only animate when previously
1243
- // visible and in toast mode — centered modal hides instantly.
1244
- if (wasVisible && isToast) {
1245
- animateIosPanelOut(refs, previousPosition, () => {
1246
- try {
1247
- refs.window.hidden = true;
1248
- }
1249
- catch { }
1250
- });
1251
- }
1252
- else {
1253
- refs.window.hidden = true;
1254
- }
1255
- refs.wasVisible = false;
1256
- refs.currentPosition = position;
1257
- return true;
1258
- }
1259
- refs.window.hidden = false;
1260
- refs.titleLabel.text = snapshot.title || '';
1261
- refs.statusLabel.text = formatStatusText(snapshot);
1262
- const host = getIosOverlayHost();
1263
- if (host) {
1264
- const { UIColor } = host;
1265
- const isError = snapshot.tone === 'error';
1266
- const isSuccess = snapshot.tone === 'success';
1267
- try {
1268
- if (isError) {
1269
- // Red panel + dark red text (existing UX).
1270
- refs.panel.backgroundColor = UIColor.colorWithRedGreenBlueAlpha(1, 0.96, 0.96, 1);
1271
- refs.titleLabel.textColor = UIColor.colorWithRedGreenBlueAlpha(0.7, 0.1, 0.06, 1);
1272
- refs.statusLabel.textColor = UIColor.colorWithRedGreenBlueAlpha(0.7, 0.1, 0.06, 0.9);
1273
- }
1274
- else if (isSuccess) {
1275
- // Slightly more saturated green panel + dark-green
1276
- // text. The previous 0.94/0.99/0.95 background was
1277
- // nearly indistinguishable from white on most
1278
- // devices; this bump keeps long detail strings
1279
- // readable while making the apply event obviously
1280
- // "happening right now".
1281
- refs.panel.backgroundColor = UIColor.colorWithRedGreenBlueAlpha(0.9, 0.97, 0.91, 1);
1282
- refs.titleLabel.textColor = UIColor.colorWithRedGreenBlueAlpha(0.05, 0.43, 0.18, 1);
1283
- refs.statusLabel.textColor = UIColor.colorWithRedGreenBlueAlpha(0.05, 0.43, 0.18, 1);
1284
- }
1285
- else {
1286
- // Default (info / warn) — existing connection look.
1287
- refs.panel.backgroundColor = UIColor.whiteColor;
1288
- refs.titleLabel.textColor = UIColor.blackColor;
1289
- refs.statusLabel.textColor = UIColor.darkGrayColor;
1290
- }
1291
- // Backdrop dims only in centered mode; toast mode keeps
1292
- // the rest of the app fully visible/usable. Errors get
1293
- // a slightly stronger dim in centered mode because the
1294
- // user MUST notice them.
1295
- if (isToast) {
1296
- refs.backdrop.backgroundColor = UIColor.clearColor;
1297
- }
1298
- else if (isError) {
1299
- refs.backdrop.backgroundColor = UIColor.colorWithRedGreenBlueAlpha(0, 0, 0, 0.35);
1300
- }
1301
- else if (isSuccess) {
1302
- refs.backdrop.backgroundColor = UIColor.colorWithRedGreenBlueAlpha(0, 0.15, 0.05, 0.28);
1303
- }
1304
- else {
1305
- refs.backdrop.backgroundColor = UIColor.colorWithRedGreenBlueAlpha(0, 0, 0, 0.35);
1306
- }
1307
- }
1308
- catch { }
1309
- }
1310
- const layout = layoutIosOverlayRefs(refs, position);
1311
- // Slide-in animation only fires on the actual hidden→visible
1312
- // transition (or on a position swap — e.g. dev toggling top
1313
- // to bottom mid-cycle). Subsequent updates within the same
1314
- // visible cycle just refresh text/colours without re-animating.
1315
- const positionChanged = previousPosition !== position;
1316
- const justAppeared = !wasVisible || positionChanged;
1317
- if (justAppeared && isToast && layout) {
1318
- animateIosPanelIn(refs, position, layout);
1319
- }
1320
- else if (justAppeared && !isToast) {
1321
- // Centered modal: ensure alpha is reset to 1 in case a
1322
- // previous toast-mode dismissal left it at 0.
1323
- try {
1324
- refs.panel.alpha = 1;
1325
- }
1326
- catch { }
1327
- }
1328
- refs.wasVisible = true;
1329
- refs.currentPosition = position;
1330
- return true;
1331
- }
1332
- catch (err) {
1333
- console.warn('[ns-hmr-overlay] iOS overlay apply failed:', err?.message || err);
1334
- return false;
1335
- }
1336
- }
1337
37
  function applyRuntimeSnapshot(snapshot) {
1338
38
  const state = getRuntimeState();
1339
39
  state.snapshot = snapshot;
@@ -1534,23 +234,6 @@ function createOverlayApi() {
1534
234
  },
1535
235
  };
1536
236
  }
1537
- // The NS_VITE_PROGRESS_OVERLAY opt-out. Read the build-time define (const-substituted
1538
- // into served modules) first, then fall back to the globalThis seed, defaulting to
1539
- // enabled — mirrors the gate used by the per-framework update overlays.
1540
- function isHmrProgressOverlayEnabled() {
1541
- try {
1542
- if (typeof __NS_HMR_PROGRESS_OVERLAY_ENABLED__ === 'boolean')
1543
- return __NS_HMR_PROGRESS_OVERLAY_ENABLED__;
1544
- }
1545
- catch { }
1546
- try {
1547
- const seeded = getGlobalScope().__NS_HMR_PROGRESS_OVERLAY_ENABLED__;
1548
- if (typeof seeded === 'boolean')
1549
- return seeded;
1550
- }
1551
- catch { }
1552
- return true;
1553
- }
1554
237
  // When the overlay is opted out, every surface (boot / connection / update / pending)
1555
238
  // becomes a no-op so nothing is ever drawn. The boot placeholder root and the
1556
239
  // `__NS_HMR_BOOT_COMPLETE__` signal are owned by `root-placeholder.ts` and are
@@ -1581,7 +264,7 @@ export function ensureHmrDevOverlayRuntimeInstalled(verbose) {
1581
264
  const state = getRuntimeState();
1582
265
  state.verbose = state.verbose || !!verbose;
1583
266
  if (!g.__NS_HMR_DEV_OVERLAY__) {
1584
- g.__NS_HMR_DEV_OVERLAY__ = isHmrProgressOverlayEnabled() ? createOverlayApi() : createNoopOverlayApi();
267
+ g.__NS_HMR_DEV_OVERLAY__ = resolveOverlayEnabled() ? createOverlayApi() : createNoopOverlayApi();
1585
268
  }
1586
269
  return g.__NS_HMR_DEV_OVERLAY__;
1587
270
  }