@nativescript/vite 8.0.0-alpha.1 → 8.0.0-alpha.11

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 (220) hide show
  1. package/configuration/angular.d.ts +1 -1
  2. package/configuration/angular.js +486 -140
  3. package/configuration/angular.js.map +1 -1
  4. package/configuration/base.js +159 -29
  5. package/configuration/base.js.map +1 -1
  6. package/configuration/javascript.js +3 -3
  7. package/configuration/javascript.js.map +1 -1
  8. package/configuration/solid.js +27 -0
  9. package/configuration/solid.js.map +1 -1
  10. package/configuration/typescript.js +4 -4
  11. package/configuration/typescript.js.map +1 -1
  12. package/helpers/angular/angular-linker.js +38 -42
  13. package/helpers/angular/angular-linker.js.map +1 -1
  14. package/helpers/angular/inject-component-hmr-registration.d.ts +112 -0
  15. package/helpers/angular/inject-component-hmr-registration.js +359 -0
  16. package/helpers/angular/inject-component-hmr-registration.js.map +1 -0
  17. package/helpers/angular/inline-decorator-component-templates.d.ts +3 -0
  18. package/helpers/angular/inline-decorator-component-templates.js +400 -0
  19. package/helpers/angular/inline-decorator-component-templates.js.map +1 -0
  20. package/helpers/angular/shared-linker.d.ts +7 -0
  21. package/helpers/angular/shared-linker.js +37 -1
  22. package/helpers/angular/shared-linker.js.map +1 -1
  23. package/helpers/angular/synthesize-decorator-ctor-parameters.d.ts +1 -0
  24. package/helpers/angular/synthesize-decorator-ctor-parameters.js +256 -0
  25. package/helpers/angular/synthesize-decorator-ctor-parameters.js.map +1 -0
  26. package/helpers/angular/synthesize-injectable-factories.d.ts +3 -0
  27. package/helpers/angular/synthesize-injectable-factories.js +414 -0
  28. package/helpers/angular/synthesize-injectable-factories.js.map +1 -0
  29. package/helpers/angular/util.d.ts +1 -0
  30. package/helpers/angular/util.js +88 -0
  31. package/helpers/angular/util.js.map +1 -1
  32. package/helpers/commonjs-plugins.d.ts +5 -2
  33. package/helpers/commonjs-plugins.js +126 -0
  34. package/helpers/commonjs-plugins.js.map +1 -1
  35. package/helpers/config-as-json.js +10 -0
  36. package/helpers/config-as-json.js.map +1 -1
  37. package/helpers/esbuild-platform-resolver.js +5 -5
  38. package/helpers/esbuild-platform-resolver.js.map +1 -1
  39. package/helpers/external-configs.d.ts +9 -1
  40. package/helpers/external-configs.js +31 -6
  41. package/helpers/external-configs.js.map +1 -1
  42. package/helpers/global-defines.d.ts +51 -0
  43. package/helpers/global-defines.js +77 -0
  44. package/helpers/global-defines.js.map +1 -1
  45. package/helpers/import-meta-path.d.ts +4 -0
  46. package/helpers/import-meta-path.js +5 -0
  47. package/helpers/import-meta-path.js.map +1 -0
  48. package/helpers/import-specifier.d.ts +1 -0
  49. package/helpers/import-specifier.js +18 -0
  50. package/helpers/import-specifier.js.map +1 -0
  51. package/helpers/logging.d.ts +1 -0
  52. package/helpers/logging.js +63 -3
  53. package/helpers/logging.js.map +1 -1
  54. package/helpers/main-entry.d.ts +5 -2
  55. package/helpers/main-entry.js +375 -116
  56. package/helpers/main-entry.js.map +1 -1
  57. package/helpers/nativeclass-transform.js +8 -127
  58. package/helpers/nativeclass-transform.js.map +1 -1
  59. package/helpers/nativeclass-transformer-plugin.d.ts +19 -1
  60. package/helpers/nativeclass-transformer-plugin.js +318 -36
  61. package/helpers/nativeclass-transformer-plugin.js.map +1 -1
  62. package/helpers/ns-core-url.d.ts +83 -0
  63. package/helpers/ns-core-url.js +167 -0
  64. package/helpers/ns-core-url.js.map +1 -0
  65. package/helpers/prelink-angular.js +1 -4
  66. package/helpers/prelink-angular.js.map +1 -1
  67. package/helpers/project.d.ts +35 -0
  68. package/helpers/project.js +120 -2
  69. package/helpers/project.js.map +1 -1
  70. package/helpers/ts-config-paths.js +50 -2
  71. package/helpers/ts-config-paths.js.map +1 -1
  72. package/helpers/workers.d.ts +20 -19
  73. package/helpers/workers.js +620 -3
  74. package/helpers/workers.js.map +1 -1
  75. package/hmr/client/css-handler.js +60 -19
  76. package/hmr/client/css-handler.js.map +1 -1
  77. package/hmr/client/hmr-pending-overlay.d.ts +27 -0
  78. package/hmr/client/hmr-pending-overlay.js +50 -0
  79. package/hmr/client/hmr-pending-overlay.js.map +1 -0
  80. package/hmr/client/index.js +767 -24
  81. package/hmr/client/index.js.map +1 -1
  82. package/hmr/client/utils.d.ts +5 -0
  83. package/hmr/client/utils.js +283 -12
  84. package/hmr/client/utils.js.map +1 -1
  85. package/hmr/entry-runtime.d.ts +10 -0
  86. package/hmr/entry-runtime.js +330 -42
  87. package/hmr/entry-runtime.js.map +1 -1
  88. package/hmr/frameworks/angular/client/index.d.ts +3 -1
  89. package/hmr/frameworks/angular/client/index.js +821 -25
  90. package/hmr/frameworks/angular/client/index.js.map +1 -1
  91. package/hmr/frameworks/angular/server/linker.js +37 -6
  92. package/hmr/frameworks/angular/server/linker.js.map +1 -1
  93. package/hmr/frameworks/angular/server/strategy.js +30 -6
  94. package/hmr/frameworks/angular/server/strategy.js.map +1 -1
  95. package/hmr/frameworks/typescript/server/strategy.js +8 -2
  96. package/hmr/frameworks/typescript/server/strategy.js.map +1 -1
  97. package/hmr/frameworks/vue/client/index.js +18 -42
  98. package/hmr/frameworks/vue/client/index.js.map +1 -1
  99. package/hmr/helpers/ast-normalizer.js +22 -10
  100. package/hmr/helpers/ast-normalizer.js.map +1 -1
  101. package/hmr/helpers/cjs-named-exports.d.ts +23 -0
  102. package/hmr/helpers/cjs-named-exports.js +152 -0
  103. package/hmr/helpers/cjs-named-exports.js.map +1 -0
  104. package/hmr/server/constants.d.ts +1 -0
  105. package/hmr/server/constants.js +14 -3
  106. package/hmr/server/constants.js.map +1 -1
  107. package/hmr/server/core-sanitize.d.ts +49 -2
  108. package/hmr/server/core-sanitize.js +267 -24
  109. package/hmr/server/core-sanitize.js.map +1 -1
  110. package/hmr/server/import-map.d.ts +65 -0
  111. package/hmr/server/import-map.js +222 -0
  112. package/hmr/server/import-map.js.map +1 -0
  113. package/hmr/server/index.d.ts +2 -1
  114. package/hmr/server/index.js.map +1 -1
  115. package/hmr/server/ns-core-cjs-shape.d.ts +204 -0
  116. package/hmr/server/ns-core-cjs-shape.js +271 -0
  117. package/hmr/server/ns-core-cjs-shape.js.map +1 -0
  118. package/hmr/server/perf-instrumentation.d.ts +114 -0
  119. package/hmr/server/perf-instrumentation.js +195 -0
  120. package/hmr/server/perf-instrumentation.js.map +1 -0
  121. package/hmr/server/runtime-graph-filter.d.ts +5 -0
  122. package/hmr/server/runtime-graph-filter.js +21 -0
  123. package/hmr/server/runtime-graph-filter.js.map +1 -0
  124. package/hmr/server/shared-transform-request.d.ts +12 -0
  125. package/hmr/server/shared-transform-request.js +144 -0
  126. package/hmr/server/shared-transform-request.js.map +1 -0
  127. package/hmr/server/vite-plugin.d.ts +21 -1
  128. package/hmr/server/vite-plugin.js +461 -22
  129. package/hmr/server/vite-plugin.js.map +1 -1
  130. package/hmr/server/websocket-angular-entry.d.ts +2 -0
  131. package/hmr/server/websocket-angular-entry.js +68 -0
  132. package/hmr/server/websocket-angular-entry.js.map +1 -0
  133. package/hmr/server/websocket-angular-hot-update.d.ts +78 -0
  134. package/hmr/server/websocket-angular-hot-update.js +413 -0
  135. package/hmr/server/websocket-angular-hot-update.js.map +1 -0
  136. package/hmr/server/websocket-core-bridge.d.ts +21 -0
  137. package/hmr/server/websocket-core-bridge.js +357 -0
  138. package/hmr/server/websocket-core-bridge.js.map +1 -0
  139. package/hmr/server/websocket-graph-upsert.d.ts +21 -0
  140. package/hmr/server/websocket-graph-upsert.js +33 -0
  141. package/hmr/server/websocket-graph-upsert.js.map +1 -0
  142. package/hmr/server/websocket-hmr-pending.d.ts +43 -0
  143. package/hmr/server/websocket-hmr-pending.js +55 -0
  144. package/hmr/server/websocket-hmr-pending.js.map +1 -0
  145. package/hmr/server/websocket-module-bindings.d.ts +6 -0
  146. package/hmr/server/websocket-module-bindings.js +471 -0
  147. package/hmr/server/websocket-module-bindings.js.map +1 -0
  148. package/hmr/server/websocket-module-specifiers.d.ts +101 -0
  149. package/hmr/server/websocket-module-specifiers.js +820 -0
  150. package/hmr/server/websocket-module-specifiers.js.map +1 -0
  151. package/hmr/server/websocket-ns-m-finalize.d.ts +22 -0
  152. package/hmr/server/websocket-ns-m-finalize.js +88 -0
  153. package/hmr/server/websocket-ns-m-finalize.js.map +1 -0
  154. package/hmr/server/websocket-ns-m-paths.d.ts +3 -0
  155. package/hmr/server/websocket-ns-m-paths.js +92 -0
  156. package/hmr/server/websocket-ns-m-paths.js.map +1 -0
  157. package/hmr/server/websocket-ns-m-request.d.ts +45 -0
  158. package/hmr/server/websocket-ns-m-request.js +196 -0
  159. package/hmr/server/websocket-ns-m-request.js.map +1 -0
  160. package/hmr/server/websocket-runtime-compat.d.ts +19 -0
  161. package/hmr/server/websocket-runtime-compat.js +287 -0
  162. package/hmr/server/websocket-runtime-compat.js.map +1 -0
  163. package/hmr/server/websocket-served-module-helpers.d.ts +36 -0
  164. package/hmr/server/websocket-served-module-helpers.js +631 -0
  165. package/hmr/server/websocket-served-module-helpers.js.map +1 -0
  166. package/hmr/server/websocket-txn.d.ts +6 -0
  167. package/hmr/server/websocket-txn.js +45 -0
  168. package/hmr/server/websocket-txn.js.map +1 -0
  169. package/hmr/server/websocket-vendor-unifier.d.ts +10 -0
  170. package/hmr/server/websocket-vendor-unifier.js +51 -0
  171. package/hmr/server/websocket-vendor-unifier.js.map +1 -0
  172. package/hmr/server/websocket-vue-sfc.d.ts +27 -0
  173. package/hmr/server/websocket-vue-sfc.js +1069 -0
  174. package/hmr/server/websocket-vue-sfc.js.map +1 -0
  175. package/hmr/server/websocket.d.ts +26 -3
  176. package/hmr/server/websocket.js +2492 -798
  177. package/hmr/server/websocket.js.map +1 -1
  178. package/hmr/shared/package-classifier.d.ts +9 -0
  179. package/hmr/shared/package-classifier.js +58 -0
  180. package/hmr/shared/package-classifier.js.map +1 -0
  181. package/hmr/shared/runtime/boot-timeline.d.ts +17 -0
  182. package/hmr/shared/runtime/boot-timeline.js +51 -0
  183. package/hmr/shared/runtime/boot-timeline.js.map +1 -0
  184. package/hmr/shared/runtime/browser-runtime-contract.d.ts +64 -0
  185. package/hmr/shared/runtime/browser-runtime-contract.js +54 -0
  186. package/hmr/shared/runtime/browser-runtime-contract.js.map +1 -0
  187. package/hmr/shared/runtime/dev-overlay.d.ts +85 -0
  188. package/hmr/shared/runtime/dev-overlay.js +1236 -0
  189. package/hmr/shared/runtime/dev-overlay.js.map +1 -0
  190. package/hmr/shared/runtime/http-only-boot.d.ts +1 -0
  191. package/hmr/shared/runtime/http-only-boot.js +53 -6
  192. package/hmr/shared/runtime/http-only-boot.js.map +1 -1
  193. package/hmr/shared/runtime/module-provenance.d.ts +1 -0
  194. package/hmr/shared/runtime/module-provenance.js +63 -0
  195. package/hmr/shared/runtime/module-provenance.js.map +1 -0
  196. package/hmr/shared/runtime/platform-polyfills.d.ts +26 -0
  197. package/hmr/shared/runtime/platform-polyfills.js +122 -0
  198. package/hmr/shared/runtime/platform-polyfills.js.map +1 -0
  199. package/hmr/shared/runtime/root-placeholder.d.ts +1 -0
  200. package/hmr/shared/runtime/root-placeholder.js +552 -82
  201. package/hmr/shared/runtime/root-placeholder.js.map +1 -1
  202. package/hmr/shared/runtime/session-bootstrap.d.ts +1 -0
  203. package/hmr/shared/runtime/session-bootstrap.js +195 -0
  204. package/hmr/shared/runtime/session-bootstrap.js.map +1 -0
  205. package/hmr/shared/runtime/vendor-bootstrap.js +52 -15
  206. package/hmr/shared/runtime/vendor-bootstrap.js.map +1 -1
  207. package/hmr/shared/vendor/manifest.d.ts +37 -0
  208. package/hmr/shared/vendor/manifest.js +677 -57
  209. package/hmr/shared/vendor/manifest.js.map +1 -1
  210. package/hmr/shared/vendor/registry.js +104 -7
  211. package/hmr/shared/vendor/registry.js.map +1 -1
  212. package/index.d.ts +1 -0
  213. package/index.js +5 -0
  214. package/index.js.map +1 -1
  215. package/package.json +14 -2
  216. package/runtime/core-aliases-early.js +94 -67
  217. package/runtime/core-aliases-early.js.map +1 -1
  218. package/shims/solid-jsx-runtime.d.ts +7 -0
  219. package/shims/solid-jsx-runtime.js +17 -0
  220. package/shims/solid-jsx-runtime.js.map +1 -0
@@ -0,0 +1,1236 @@
1
+ const BOOT_TITLE = 'NativeScript Vite preparing dev session...';
2
+ const DEFAULT_SNAPSHOT = {
3
+ visible: false,
4
+ mode: 'hidden',
5
+ badge: 'HMR',
6
+ title: BOOT_TITLE,
7
+ phase: '',
8
+ detail: '',
9
+ progress: null,
10
+ busy: false,
11
+ blocking: false,
12
+ tone: 'info',
13
+ };
14
+ function getOverlayGlobal() {
15
+ return globalThis;
16
+ }
17
+ function getRuntimeState() {
18
+ const g = getOverlayGlobal();
19
+ if (!g.__NS_HMR_DEV_OVERLAY_STATE__) {
20
+ g.__NS_HMR_DEV_OVERLAY_STATE__ = {
21
+ snapshot: { ...DEFAULT_SNAPSHOT },
22
+ bootRefs: null,
23
+ liveRefs: null,
24
+ iosRefs: null,
25
+ iosBuildFailed: false,
26
+ verbose: false,
27
+ updateAutoHideTimer: null,
28
+ updateCycleStartedAt: 0,
29
+ };
30
+ }
31
+ const state = g.__NS_HMR_DEV_OVERLAY_STATE__;
32
+ // Backfill newer fields for legacy state objects (e.g. after hot reload)
33
+ // so we never observe an undefined iosRefs/iosBuildFailed at runtime.
34
+ if (typeof state.iosRefs === 'undefined')
35
+ state.iosRefs = null;
36
+ if (typeof state.iosBuildFailed === 'undefined')
37
+ state.iosBuildFailed = false;
38
+ if (typeof state.updateAutoHideTimer === 'undefined')
39
+ state.updateAutoHideTimer = null;
40
+ if (typeof state.updateCycleStartedAt !== 'number')
41
+ state.updateCycleStartedAt = 0;
42
+ return state;
43
+ }
44
+ function describeAttempt(info) {
45
+ const attempt = Number(info?.attempt || 0);
46
+ const attempts = Number(info?.attempts || 0);
47
+ if (!attempt || !attempts) {
48
+ return '';
49
+ }
50
+ return `Attempt ${attempt}/${attempts}`;
51
+ }
52
+ export function createBootOverlaySnapshot(stage, info) {
53
+ const attemptText = describeAttempt(info);
54
+ const phaseInfo = {
55
+ placeholder: {
56
+ visible: true,
57
+ mode: 'boot',
58
+ badge: 'BOOT',
59
+ title: BOOT_TITLE,
60
+ phase: 'Preparing the HTTP HMR bootstrap',
61
+ progress: 4,
62
+ busy: true,
63
+ blocking: true,
64
+ tone: 'info',
65
+ },
66
+ 'probing-origin': {
67
+ visible: true,
68
+ mode: 'boot',
69
+ badge: 'BOOT',
70
+ title: BOOT_TITLE,
71
+ phase: 'Contacting the Vite dev server',
72
+ progress: 8,
73
+ busy: true,
74
+ blocking: true,
75
+ tone: 'info',
76
+ },
77
+ 'loading-entry-runtime': {
78
+ visible: true,
79
+ mode: 'boot',
80
+ badge: 'BOOT',
81
+ title: BOOT_TITLE,
82
+ phase: 'Loading the entry runtime bridge',
83
+ progress: 16,
84
+ busy: true,
85
+ blocking: true,
86
+ tone: 'info',
87
+ },
88
+ 'configuring-import-map': {
89
+ visible: true,
90
+ mode: 'boot',
91
+ badge: 'BOOT',
92
+ title: BOOT_TITLE,
93
+ phase: 'Configuring the import map',
94
+ progress: 26,
95
+ busy: true,
96
+ blocking: true,
97
+ tone: 'info',
98
+ },
99
+ 'loading-runtime-bridge': {
100
+ visible: true,
101
+ mode: 'boot',
102
+ badge: 'BOOT',
103
+ title: BOOT_TITLE,
104
+ phase: 'Loading the NativeScript runtime bridge',
105
+ progress: 40,
106
+ busy: true,
107
+ blocking: true,
108
+ tone: 'info',
109
+ },
110
+ 'loading-core-bridge': {
111
+ visible: true,
112
+ mode: 'boot',
113
+ badge: 'BOOT',
114
+ title: BOOT_TITLE,
115
+ phase: 'Loading the unified core bridge',
116
+ progress: 54,
117
+ busy: true,
118
+ blocking: true,
119
+ tone: 'info',
120
+ },
121
+ 'preloading-style-scope': {
122
+ visible: true,
123
+ mode: 'boot',
124
+ badge: 'BOOT',
125
+ title: BOOT_TITLE,
126
+ phase: 'Preparing the shared style scope',
127
+ progress: 62,
128
+ busy: true,
129
+ blocking: true,
130
+ tone: 'info',
131
+ },
132
+ 'installing-css': {
133
+ visible: true,
134
+ mode: 'boot',
135
+ badge: 'BOOT',
136
+ title: BOOT_TITLE,
137
+ phase: 'Applying the app stylesheet',
138
+ progress: 70,
139
+ busy: true,
140
+ blocking: true,
141
+ tone: 'info',
142
+ },
143
+ 'importing-main': {
144
+ visible: true,
145
+ mode: 'boot',
146
+ badge: 'BOOT',
147
+ title: BOOT_TITLE,
148
+ phase: 'Importing the app entry',
149
+ progress: 82,
150
+ busy: true,
151
+ blocking: true,
152
+ tone: 'info',
153
+ },
154
+ 'waiting-for-app': {
155
+ visible: true,
156
+ mode: 'boot',
157
+ badge: 'BOOT',
158
+ title: BOOT_TITLE,
159
+ phase: 'Waiting for the app root view',
160
+ progress: 94,
161
+ busy: true,
162
+ blocking: true,
163
+ tone: 'info',
164
+ },
165
+ 'app-root-committed': {
166
+ visible: true,
167
+ mode: 'boot',
168
+ badge: 'READY',
169
+ title: BOOT_TITLE,
170
+ phase: 'Real app root committed',
171
+ progress: 100,
172
+ busy: false,
173
+ blocking: true,
174
+ tone: 'info',
175
+ },
176
+ ready: {
177
+ visible: true,
178
+ mode: 'boot',
179
+ badge: 'READY',
180
+ title: BOOT_TITLE,
181
+ phase: 'Boot complete',
182
+ progress: 100,
183
+ busy: false,
184
+ blocking: true,
185
+ tone: 'info',
186
+ },
187
+ error: {
188
+ visible: true,
189
+ mode: 'boot',
190
+ badge: 'RETRY',
191
+ title: BOOT_TITLE,
192
+ phase: 'Retrying after a boot failure',
193
+ progress: null,
194
+ busy: true,
195
+ blocking: true,
196
+ tone: 'error',
197
+ },
198
+ };
199
+ const base = phaseInfo[stage];
200
+ let detail = info?.detail || '';
201
+ if (!detail && stage === 'probing-origin' && info?.origin) {
202
+ detail = `Trying ${info.origin}`;
203
+ }
204
+ if (attemptText) {
205
+ detail = detail ? `${detail} ${attemptText}` : attemptText;
206
+ }
207
+ return {
208
+ ...base,
209
+ detail,
210
+ progress: typeof info?.progress === 'number' || info?.progress === null ? info.progress : base.progress,
211
+ };
212
+ }
213
+ export function createConnectionOverlaySnapshot(stage, info) {
214
+ if (stage === 'healthy') {
215
+ return { ...DEFAULT_SNAPSHOT };
216
+ }
217
+ const phaseInfo = {
218
+ connecting: {
219
+ visible: true,
220
+ mode: 'connection',
221
+ badge: 'SOCKET',
222
+ title: 'Waiting for Vite dev server',
223
+ phase: 'Opening websocket connection',
224
+ detail: 'Live updates are paused until the connection is healthy.',
225
+ progress: null,
226
+ busy: true,
227
+ blocking: false,
228
+ tone: 'warn',
229
+ },
230
+ reconnecting: {
231
+ visible: true,
232
+ mode: 'connection',
233
+ badge: 'SOCKET',
234
+ title: 'HMR connection lost',
235
+ phase: 'Trying to reconnect Vite websocket',
236
+ detail: 'The app may be stale until the dev server reconnects.',
237
+ progress: null,
238
+ busy: true,
239
+ blocking: false,
240
+ tone: 'warn',
241
+ },
242
+ synchronizing: {
243
+ visible: true,
244
+ mode: 'connection',
245
+ badge: 'SYNC',
246
+ title: 'HMR connection restored',
247
+ phase: 'Synchronizing live-update state',
248
+ detail: 'Finalizing the module graph before dismissing the overlay.',
249
+ progress: 95,
250
+ busy: true,
251
+ blocking: false,
252
+ tone: 'info',
253
+ },
254
+ offline: {
255
+ visible: true,
256
+ mode: 'connection',
257
+ badge: 'OFFLINE',
258
+ title: 'Vite dev server offline',
259
+ phase: 'Please check your terminal.',
260
+ detail: 'The websocket and HTTP HMR path are both unavailable right now.',
261
+ progress: null,
262
+ busy: true,
263
+ blocking: false,
264
+ tone: 'error',
265
+ },
266
+ };
267
+ const base = phaseInfo[stage];
268
+ return {
269
+ ...base,
270
+ detail: info?.detail || base.detail,
271
+ progress: typeof info?.progress === 'number' || info?.progress === null ? info.progress : base.progress,
272
+ };
273
+ }
274
+ // Snapshot factory for the HMR-applying overlay. Each stage owns a
275
+ // fixed phase string, badge, and progress %. We pick the percentages
276
+ // so users see continuous forward motion: the cheap stages (mutex
277
+ // acquire, eviction call) advance fast; the long tail (entry
278
+ // re-import + Angular reboot) sits at 60→90 so the bar keeps moving
279
+ // even when the V8 ESM walk dominates wall time.
280
+ //
281
+ // The 'complete' stage holds for a brief moment (the API auto-hides
282
+ // it via setUpdateStage) so the user gets visual closure ("the update
283
+ // landed") without staring at a frozen overlay; tone stays 'success'
284
+ // throughout so the colour scheme never flickers between phases.
285
+ const HMR_UPDATE_TITLE = 'HMR update applying...';
286
+ const HMR_UPDATE_DONE_TITLE = 'HMR update applied';
287
+ export function createUpdateOverlaySnapshot(stage, info) {
288
+ const phaseInfo = {
289
+ received: {
290
+ visible: true,
291
+ mode: 'update',
292
+ badge: 'HMR',
293
+ title: HMR_UPDATE_TITLE,
294
+ phase: 'Preparing update',
295
+ detail: '',
296
+ progress: 5,
297
+ busy: true,
298
+ blocking: false,
299
+ tone: 'success',
300
+ },
301
+ evicting: {
302
+ visible: true,
303
+ mode: 'update',
304
+ badge: 'HMR',
305
+ title: HMR_UPDATE_TITLE,
306
+ phase: 'Invalidating module cache',
307
+ detail: '',
308
+ progress: 30,
309
+ busy: true,
310
+ blocking: false,
311
+ tone: 'success',
312
+ },
313
+ reimporting: {
314
+ visible: true,
315
+ mode: 'update',
316
+ badge: 'HMR',
317
+ title: HMR_UPDATE_TITLE,
318
+ phase: 'Re-importing entry',
319
+ detail: '',
320
+ progress: 60,
321
+ busy: true,
322
+ blocking: false,
323
+ tone: 'success',
324
+ },
325
+ rebooting: {
326
+ visible: true,
327
+ mode: 'update',
328
+ badge: 'HMR',
329
+ title: HMR_UPDATE_TITLE,
330
+ phase: 'Rebooting Angular',
331
+ detail: '',
332
+ progress: 90,
333
+ busy: true,
334
+ blocking: false,
335
+ tone: 'success',
336
+ },
337
+ complete: {
338
+ visible: true,
339
+ mode: 'update',
340
+ badge: 'HMR',
341
+ title: HMR_UPDATE_DONE_TITLE,
342
+ phase: 'Update applied',
343
+ detail: '',
344
+ progress: 100,
345
+ busy: false,
346
+ blocking: false,
347
+ tone: 'success',
348
+ },
349
+ };
350
+ const base = phaseInfo[stage];
351
+ return {
352
+ ...base,
353
+ detail: info?.detail || base.detail,
354
+ progress: typeof info?.progress === 'number' || info?.progress === null ? info.progress : base.progress,
355
+ };
356
+ }
357
+ function resolveCoreExport(name) {
358
+ const g = getOverlayGlobal();
359
+ try {
360
+ const reg = g.__nsVendorRegistry;
361
+ if (reg && typeof reg.get === 'function') {
362
+ const mod = reg.get('@nativescript/core');
363
+ const ns = (mod && (mod.default ?? mod)) || mod;
364
+ if (ns && ns[name]) {
365
+ return ns[name];
366
+ }
367
+ }
368
+ }
369
+ catch { }
370
+ try {
371
+ if (g && g[name]) {
372
+ return g[name];
373
+ }
374
+ }
375
+ catch { }
376
+ try {
377
+ const req = g.__nsVendorRequire || g.__nsRequire || g.require;
378
+ if (typeof req === 'function') {
379
+ const mod = req('@nativescript/core');
380
+ const ns = (mod && (mod.default ?? mod)) || mod;
381
+ if (ns && ns[name]) {
382
+ return ns[name];
383
+ }
384
+ }
385
+ }
386
+ catch { }
387
+ try {
388
+ const nativeReq = g.__nativeRequire;
389
+ if (typeof nativeReq === 'function') {
390
+ const mod = nativeReq('@nativescript/core', '/');
391
+ const ns = (mod && (mod.default ?? mod)) || mod;
392
+ if (ns && ns[name]) {
393
+ return ns[name];
394
+ }
395
+ }
396
+ }
397
+ catch { }
398
+ return undefined;
399
+ }
400
+ function asColor(value) {
401
+ try {
402
+ const Color = resolveCoreExport('Color');
403
+ if (Color) {
404
+ return new Color(value);
405
+ }
406
+ }
407
+ catch { }
408
+ return value;
409
+ }
410
+ function formatStatusText(snapshot) {
411
+ const progressText = typeof snapshot.progress === 'number' ? ` (${Math.round(Number(snapshot.progress))}%)` : '';
412
+ const primary = `${snapshot.phase || ''}${progressText}`.trim();
413
+ return [primary, snapshot.detail].filter(Boolean).join('\n');
414
+ }
415
+ function buildBootOverlayRefs(snapshot) {
416
+ const Page = resolveCoreExport('Page');
417
+ const StackLayout = resolveCoreExport('StackLayout');
418
+ const Label = resolveCoreExport('Label');
419
+ const ActivityIndicator = resolveCoreExport('ActivityIndicator');
420
+ if (!Page || !StackLayout || !Label) {
421
+ return null;
422
+ }
423
+ const page = new Page();
424
+ page.actionBarHidden = true;
425
+ const root = new StackLayout();
426
+ root.padding = 24;
427
+ root.verticalAlignment = 'middle';
428
+ root.horizontalAlignment = 'stretch';
429
+ const titleLabel = new Label();
430
+ titleLabel.text = BOOT_TITLE;
431
+ titleLabel.textAlignment = 'center';
432
+ titleLabel.textWrap = true;
433
+ titleLabel.fontSize = 22;
434
+ titleLabel.fontWeight = '600';
435
+ const statusLabel = new Label();
436
+ statusLabel.textAlignment = 'center';
437
+ statusLabel.textWrap = true;
438
+ statusLabel.fontSize = 14;
439
+ statusLabel.marginTop = 12;
440
+ const activityIndicator = ActivityIndicator
441
+ ? (() => {
442
+ const indicator = new ActivityIndicator();
443
+ indicator.marginTop = 16;
444
+ indicator.width = 28;
445
+ indicator.height = 28;
446
+ indicator.horizontalAlignment = 'center';
447
+ return indicator;
448
+ })()
449
+ : null;
450
+ root.addChild(titleLabel);
451
+ root.addChild(statusLabel);
452
+ if (activityIndicator) {
453
+ root.addChild(activityIndicator);
454
+ }
455
+ page.content = root;
456
+ const refs = {
457
+ page,
458
+ root,
459
+ titleLabel,
460
+ statusLabel,
461
+ activityIndicator,
462
+ };
463
+ applySnapshotToBootRefs(refs, snapshot);
464
+ return refs;
465
+ }
466
+ function applySnapshotToBootRefs(refs, snapshot) {
467
+ if (!refs) {
468
+ return;
469
+ }
470
+ refs.page.actionBarHidden = true;
471
+ refs.page.backgroundColor = asColor(snapshot.tone === 'error' ? '#b4181068' : '#a1771683');
472
+ refs.root.visibility = snapshot.visible && snapshot.mode === 'boot' ? 'visible' : 'collapse';
473
+ refs.titleLabel.text = BOOT_TITLE;
474
+ refs.titleLabel.color = asColor(snapshot.tone === 'error' ? '#b41810e6' : '#563e3fb1');
475
+ refs.statusLabel.text = formatStatusText(snapshot) || 'Preparing the HTTP HMR bootstrap (4%)';
476
+ refs.statusLabel.color = asColor(snapshot.tone === 'error' ? '#b41810e6' : '#563e3fb1');
477
+ if (refs.activityIndicator) {
478
+ refs.activityIndicator.busy = !!snapshot.busy;
479
+ refs.activityIndicator.visibility = snapshot.visible && snapshot.mode === 'boot' && snapshot.busy ? 'visible' : 'collapse';
480
+ }
481
+ }
482
+ function findBootActivityIndicator() {
483
+ const g = getOverlayGlobal();
484
+ if (g.__NS_DEV_BOOT_ACTIVITY_INDICATOR__) {
485
+ return g.__NS_DEV_BOOT_ACTIVITY_INDICATOR__;
486
+ }
487
+ try {
488
+ const frame = g.__NS_DEV_PLACEHOLDER_ROOT_VIEW__;
489
+ if (!frame)
490
+ return null;
491
+ const page = frame.currentPage || frame._currentEntry?.resolvedPage;
492
+ const content = page?.content;
493
+ const children = content?.getChildrenCount?.() || 0;
494
+ if (children >= 3) {
495
+ const indicator = content.getChildAt?.(2);
496
+ if (indicator && typeof indicator.busy !== 'undefined') {
497
+ g.__NS_DEV_BOOT_ACTIVITY_INDICATOR__ = indicator;
498
+ return indicator;
499
+ }
500
+ }
501
+ }
502
+ catch { }
503
+ return null;
504
+ }
505
+ function findBootStatusLabel() {
506
+ const g = getOverlayGlobal();
507
+ // Try direct reference first
508
+ if (g.__NS_DEV_BOOT_STATUS_LABEL__) {
509
+ return g.__NS_DEV_BOOT_STATUS_LABEL__;
510
+ }
511
+ // Fallback: walk from placeholder root view
512
+ try {
513
+ const frame = g.__NS_DEV_PLACEHOLDER_ROOT_VIEW__;
514
+ if (!frame)
515
+ return null;
516
+ const page = frame.currentPage || frame._currentEntry?.resolvedPage;
517
+ if (!page)
518
+ return null;
519
+ const content = page.content;
520
+ if (!content)
521
+ return null;
522
+ // StackLayout with children: titleLabel, statusLabel
523
+ const children = content.getChildrenCount?.() || 0;
524
+ if (children >= 2) {
525
+ const label = content.getChildAt?.(1);
526
+ if (label && typeof label.text !== 'undefined') {
527
+ // Cache for next time
528
+ g.__NS_DEV_BOOT_STATUS_LABEL__ = label;
529
+ return label;
530
+ }
531
+ }
532
+ }
533
+ catch { }
534
+ return null;
535
+ }
536
+ function updateBootStatusLabel(snapshot) {
537
+ const newText = formatStatusText(snapshot) || 'Preparing the HTTP HMR bootstrap (4%)';
538
+ const statusLabel = findBootStatusLabel();
539
+ const activityIndicator = findBootActivityIndicator();
540
+ if (!statusLabel) {
541
+ if (activityIndicator) {
542
+ try {
543
+ activityIndicator.busy = !!snapshot.busy;
544
+ activityIndicator.visibility = snapshot.visible && snapshot.mode === 'boot' && snapshot.busy ? 'visible' : 'collapse';
545
+ }
546
+ catch { }
547
+ }
548
+ return;
549
+ }
550
+ try {
551
+ statusLabel.text = newText;
552
+ statusLabel.color = asColor(snapshot.tone === 'error' ? '#b41810e6' : '#563e3fb1');
553
+ if (typeof statusLabel.requestLayout === 'function') {
554
+ statusLabel.requestLayout();
555
+ }
556
+ const parent = statusLabel.parent;
557
+ if (parent && typeof parent.requestLayout === 'function') {
558
+ parent.requestLayout();
559
+ }
560
+ }
561
+ catch { }
562
+ if (activityIndicator) {
563
+ try {
564
+ activityIndicator.busy = !!snapshot.busy;
565
+ activityIndicator.visibility = snapshot.visible && snapshot.mode === 'boot' && snapshot.busy ? 'visible' : 'collapse';
566
+ }
567
+ catch { }
568
+ }
569
+ }
570
+ function resolveActivePage() {
571
+ try {
572
+ const Frame = resolveCoreExport('Frame');
573
+ const frame = Frame?.topmost?.() || null;
574
+ const currentPage = frame?.currentPage || frame?._currentEntry?.resolvedPage || null;
575
+ if (currentPage) {
576
+ return currentPage;
577
+ }
578
+ }
579
+ catch { }
580
+ try {
581
+ const Application = resolveCoreExport('Application');
582
+ const rootView = Application?.getRootView?.() || null;
583
+ if (rootView?.currentPage) {
584
+ return rootView.currentPage;
585
+ }
586
+ if (typeof rootView?.content !== 'undefined') {
587
+ return rootView;
588
+ }
589
+ }
590
+ catch { }
591
+ return null;
592
+ }
593
+ function buildLiveOverlayView(snapshot) {
594
+ const GridLayout = resolveCoreExport('GridLayout');
595
+ const StackLayout = resolveCoreExport('StackLayout');
596
+ const Label = resolveCoreExport('Label');
597
+ if (!GridLayout || !StackLayout || !Label) {
598
+ return null;
599
+ }
600
+ const overlay = new GridLayout();
601
+ overlay.visibility = 'collapse';
602
+ overlay.width = '100%';
603
+ overlay.height = '100%';
604
+ overlay.horizontalAlignment = 'stretch';
605
+ overlay.verticalAlignment = 'stretch';
606
+ const panel = new StackLayout();
607
+ panel.horizontalAlignment = 'center';
608
+ panel.verticalAlignment = 'middle';
609
+ panel.width = 320;
610
+ panel.margin = 24;
611
+ panel.padding = 16;
612
+ const titleLabel = new Label();
613
+ titleLabel.textAlignment = 'center';
614
+ titleLabel.textWrap = true;
615
+ titleLabel.fontSize = 18;
616
+ titleLabel.fontWeight = '600';
617
+ const statusLabel = new Label();
618
+ statusLabel.textAlignment = 'center';
619
+ statusLabel.textWrap = true;
620
+ statusLabel.fontSize = 13;
621
+ statusLabel.marginTop = 10;
622
+ panel.addChild(titleLabel);
623
+ panel.addChild(statusLabel);
624
+ overlay.addChild(panel);
625
+ const refs = {
626
+ overlay,
627
+ titleLabel,
628
+ statusLabel,
629
+ };
630
+ applySnapshotToLiveRefs(refs, snapshot);
631
+ return refs;
632
+ }
633
+ function ensureLiveOverlayRefs(snapshot) {
634
+ const state = getRuntimeState();
635
+ const page = resolveActivePage();
636
+ if (!page) {
637
+ return null;
638
+ }
639
+ if (state.liveRefs) {
640
+ if (state.liveRefs.page === page && state.liveRefs.overlay?.parent) {
641
+ return state.liveRefs;
642
+ }
643
+ try {
644
+ state.liveRefs.overlay.visibility = 'collapse';
645
+ }
646
+ catch { }
647
+ state.liveRefs = null;
648
+ }
649
+ const GridLayout = resolveCoreExport('GridLayout');
650
+ if (!GridLayout || typeof page.content === 'undefined') {
651
+ return null;
652
+ }
653
+ let wrapper = page.content;
654
+ if (!wrapper || !wrapper.__ns_hmr_overlay_wrapper__) {
655
+ const currentContent = page.content || null;
656
+ wrapper = new GridLayout();
657
+ wrapper.width = '100%';
658
+ wrapper.height = '100%';
659
+ wrapper.__ns_hmr_overlay_wrapper__ = true;
660
+ try {
661
+ page.content = null;
662
+ }
663
+ catch { }
664
+ if (currentContent) {
665
+ wrapper.addChild(currentContent);
666
+ }
667
+ page.content = wrapper;
668
+ }
669
+ const liveView = buildLiveOverlayView(snapshot);
670
+ if (!liveView) {
671
+ return null;
672
+ }
673
+ wrapper.addChild(liveView.overlay);
674
+ state.liveRefs = {
675
+ page,
676
+ wrapper,
677
+ ...liveView,
678
+ };
679
+ return state.liveRefs;
680
+ }
681
+ function applySnapshotToLiveRefs(refs, snapshot) {
682
+ if (!refs) {
683
+ return;
684
+ }
685
+ // 'update' mode shares the live (in-tree) overlay chrome with
686
+ // 'connection'. Both render a centered panel inside the page;
687
+ // only the colours and text change with the snapshot's tone.
688
+ const visible = snapshot.visible && (snapshot.mode === 'connection' || snapshot.mode === 'update');
689
+ refs.overlay.visibility = visible ? 'visible' : 'collapse';
690
+ // Backdrop tints by tone:
691
+ // error → red wash (matches existing UX)
692
+ // success → richer green wash; the previous 18% alpha was so
693
+ // subtle on light app backgrounds that users couldn't
694
+ // tell the overlay had even fired. 50% alpha keeps the
695
+ // underlying app legible while making the apply event
696
+ // unmistakable during the (often <300ms) cycle.
697
+ // default → warm orange (existing connection-overlay look)
698
+ const overlayBg = snapshot.tone === 'error' ? '#b4181068' : snapshot.tone === 'success' ? '#1f883d80' : '#a1771683';
699
+ refs.overlay.backgroundColor = asColor(overlayBg);
700
+ refs.titleLabel.text = snapshot.title;
701
+ const textColor = snapshot.tone === 'error' ? '#b41810e6' : snapshot.tone === 'success' ? '#0e6e2fff' : '#563e3fb1';
702
+ refs.titleLabel.color = asColor(textColor);
703
+ refs.statusLabel.text = formatStatusText(snapshot);
704
+ refs.statusLabel.color = asColor(textColor);
705
+ try {
706
+ const panel = refs.titleLabel.parent;
707
+ if (panel) {
708
+ // Slightly richer green-tinted panel for HMR-apply so the
709
+ // title/status text reads at a glance against the brighter
710
+ // backdrop wash. White panel for connection/error keeps
711
+ // existing UX intact.
712
+ const panelBg = snapshot.tone === 'success' ? '#E6F8E9FF' : '#FFFFFFFF';
713
+ panel.backgroundColor = asColor(panelBg);
714
+ panel.opacity = 1;
715
+ panel.padding = 16;
716
+ try {
717
+ panel.borderRadius = 12;
718
+ }
719
+ catch { }
720
+ }
721
+ }
722
+ catch { }
723
+ }
724
+ // pure helpers for iOS window promotion. Factored out so the layout
725
+ // math and window-level selection stay unit-testable without booting a
726
+ // simulator. See `dev-overlay.spec.ts`.
727
+ /**
728
+ * Returns the UIWindow level we use for the live/connection overlay. We lift
729
+ * above `UIWindowLevelAlert` so system alerts (and any app-presented modal)
730
+ * stack underneath. When the platform does not expose `UIWindowLevelAlert`
731
+ * we fall back to the documented constant value (2000).
732
+ */
733
+ export function computeIosOverlayWindowLevel(baseAlert) {
734
+ if (typeof baseAlert === 'number' && Number.isFinite(baseAlert)) {
735
+ return baseAlert + 1;
736
+ }
737
+ return 2000 + 1;
738
+ }
739
+ /**
740
+ * Layout math for the live overlay when it runs inside its own UIWindow.
741
+ * Pure, deterministic and independent of UIKit so we can verify the rules
742
+ * (max panel width, centered placement, safe-area clamping, sane defaults)
743
+ * from tests.
744
+ */
745
+ export function computeIosOverlayLayout(input) {
746
+ const viewWidth = Math.max(0, Number(input.viewWidth) || 0);
747
+ const viewHeight = Math.max(0, Number(input.viewHeight) || 0);
748
+ const safeInsets = {
749
+ top: Math.max(0, Number(input.safeInsets?.top ?? 0) || 0),
750
+ bottom: Math.max(0, Number(input.safeInsets?.bottom ?? 0) || 0),
751
+ left: Math.max(0, Number(input.safeInsets?.left ?? 0) || 0),
752
+ right: Math.max(0, Number(input.safeInsets?.right ?? 0) || 0),
753
+ };
754
+ const titleHeight = Math.max(0, Number(input.titleHeight) || 0);
755
+ const statusHeight = Math.max(0, Number(input.statusHeight) || 0);
756
+ const horizontalMargin = Math.max(0, Number(input.horizontalMargin ?? 24));
757
+ const maxPanelWidth = Math.max(0, Number(input.maxPanelWidth ?? 340));
758
+ const panelPadding = Math.max(0, Number(input.panelPadding ?? 16));
759
+ const interLabelSpacing = Math.max(0, Number(input.interLabelSpacing ?? 10));
760
+ const minTopInset = Math.max(0, Number(input.minTopInset ?? 20));
761
+ const available = Math.max(0, viewWidth - 2 * horizontalMargin - safeInsets.left - safeInsets.right);
762
+ const panelWidth = Math.min(maxPanelWidth, available);
763
+ const innerWidth = Math.max(0, panelWidth - 2 * panelPadding);
764
+ const spacing = titleHeight > 0 && statusHeight > 0 ? interLabelSpacing : 0;
765
+ const panelHeight = panelPadding * 2 + titleHeight + spacing + statusHeight;
766
+ const panelX = Math.max(0, (viewWidth - panelWidth) / 2);
767
+ // Center vertically, but never cross the top safe-area inset (notch/Dynamic Island).
768
+ const centered = (viewHeight - panelHeight) / 2;
769
+ const panelY = Math.max(safeInsets.top + minTopInset, centered);
770
+ return {
771
+ backdrop: { x: 0, y: 0, width: viewWidth, height: viewHeight },
772
+ panel: { x: panelX, y: panelY, width: panelWidth, height: panelHeight },
773
+ title: { x: panelPadding, y: panelPadding, width: innerWidth, height: titleHeight },
774
+ status: {
775
+ x: panelPadding,
776
+ y: panelPadding + titleHeight + spacing,
777
+ width: innerWidth,
778
+ height: statusHeight,
779
+ },
780
+ };
781
+ }
782
+ /**
783
+ * Returns the iOS UIKit symbols we rely on if we're running on an iOS runtime
784
+ * with the metadata bridge available. Returns null on Android, web, or in
785
+ * tests so callers can gracefully fall back to the in-tree overlay.
786
+ */
787
+ function getIosOverlayHost() {
788
+ const g = getOverlayGlobal();
789
+ 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') {
790
+ return null;
791
+ }
792
+ return {
793
+ UIWindow: g.UIWindow,
794
+ UIViewController: g.UIViewController,
795
+ UIView: g.UIView,
796
+ UILabel: g.UILabel,
797
+ UIColor: g.UIColor,
798
+ UIFont: g.UIFont,
799
+ UIApplication: g.UIApplication,
800
+ UIScreen: g.UIScreen,
801
+ UIWindowLevelAlert: typeof g.UIWindowLevelAlert === 'number' ? g.UIWindowLevelAlert : undefined,
802
+ };
803
+ }
804
+ /**
805
+ * Walks UIApplication.sharedApplication windows and returns the first active
806
+ * UIWindowScene we can locate. On iOS 13+ every UIWindow is attached to a
807
+ * scene, and we must initialise our overlay window the same way or the OS
808
+ * will silently refuse to render it. Returns null when no scene is found
809
+ * (older iOS versions or non-UI environments).
810
+ */
811
+ function findActiveWindowScene(host) {
812
+ try {
813
+ const app = host.UIApplication.sharedApplication;
814
+ const windows = app?.windows;
815
+ if (!windows || typeof windows.count !== 'number')
816
+ return null;
817
+ for (let i = 0; i < windows.count; i++) {
818
+ const w = windows.objectAtIndex(i);
819
+ const scene = w && w.windowScene;
820
+ if (scene)
821
+ return scene;
822
+ }
823
+ }
824
+ catch { }
825
+ return null;
826
+ }
827
+ function buildIosOverlayRefs(state) {
828
+ const host = getIosOverlayHost();
829
+ if (!host)
830
+ return null;
831
+ // Without a scene we can't build a modern UIWindow that actually renders.
832
+ // Fall back to the in-tree overlay rather than show nothing.
833
+ const scene = findActiveWindowScene(host);
834
+ if (!scene) {
835
+ if (state.verbose) {
836
+ console.info('[ns-hmr-overlay] no active UIWindowScene; skipping iOS overlay promotion');
837
+ }
838
+ return null;
839
+ }
840
+ try {
841
+ const { UIWindow, UIViewController, UIView, UILabel, UIColor, UIFont } = host;
842
+ const window = UIWindow.alloc().initWithWindowScene(scene);
843
+ window.windowLevel = computeIosOverlayWindowLevel(host.UIWindowLevelAlert ?? null);
844
+ window.backgroundColor = UIColor.clearColor;
845
+ window.hidden = true;
846
+ const controller = UIViewController.new();
847
+ controller.view.backgroundColor = UIColor.clearColor;
848
+ window.rootViewController = controller;
849
+ // UIViewAutoresizing bit masks. We mirror the UIKit constants here to
850
+ // avoid depending on symbols the metadata bridge does not always
851
+ // expose as top-level globals.
852
+ const FLEXIBLE_LEFT_MARGIN = 1 << 0;
853
+ const FLEXIBLE_WIDTH = 1 << 1;
854
+ const FLEXIBLE_RIGHT_MARGIN = 1 << 2;
855
+ const FLEXIBLE_TOP_MARGIN = 1 << 3;
856
+ const FLEXIBLE_HEIGHT = 1 << 4;
857
+ const FLEXIBLE_BOTTOM_MARGIN = 1 << 5;
858
+ const backdrop = UIView.new();
859
+ backdrop.backgroundColor = UIColor.colorWithRedGreenBlueAlpha(0, 0, 0, 0.35);
860
+ backdrop.autoresizingMask = FLEXIBLE_WIDTH | FLEXIBLE_HEIGHT;
861
+ controller.view.addSubview(backdrop);
862
+ const panel = UIView.new();
863
+ panel.backgroundColor = UIColor.whiteColor;
864
+ panel.autoresizingMask = FLEXIBLE_LEFT_MARGIN | FLEXIBLE_RIGHT_MARGIN | FLEXIBLE_TOP_MARGIN | FLEXIBLE_BOTTOM_MARGIN;
865
+ try {
866
+ panel.layer.cornerRadius = 14;
867
+ panel.layer.masksToBounds = true;
868
+ }
869
+ catch { }
870
+ controller.view.addSubview(panel);
871
+ const titleLabel = UILabel.new();
872
+ titleLabel.numberOfLines = 0;
873
+ titleLabel.textAlignment = 1; // NSTextAlignmentCenter
874
+ titleLabel.font = UIFont.boldSystemFontOfSize(16);
875
+ titleLabel.textColor = UIColor.blackColor;
876
+ panel.addSubview(titleLabel);
877
+ const statusLabel = UILabel.new();
878
+ statusLabel.numberOfLines = 0;
879
+ statusLabel.textAlignment = 1;
880
+ statusLabel.font = UIFont.systemFontOfSize(13);
881
+ statusLabel.textColor = UIColor.darkGrayColor;
882
+ panel.addSubview(statusLabel);
883
+ return { window, controller, backdrop, panel, titleLabel, statusLabel };
884
+ }
885
+ catch (err) {
886
+ console.warn('[ns-hmr-overlay] iOS overlay construction failed:', err?.message || err);
887
+ return null;
888
+ }
889
+ }
890
+ function ensureIosOverlayRefs(state) {
891
+ if (state.iosRefs)
892
+ return state.iosRefs;
893
+ if (state.iosBuildFailed)
894
+ return null;
895
+ const built = buildIosOverlayRefs(state);
896
+ if (built) {
897
+ state.iosRefs = built;
898
+ }
899
+ else {
900
+ // Remember failure so we don't hammer construction on every snapshot
901
+ // update — the in-tree path will take over for this session.
902
+ state.iosBuildFailed = true;
903
+ }
904
+ return state.iosRefs;
905
+ }
906
+ function layoutIosOverlayRefs(refs) {
907
+ try {
908
+ const bounds = refs.controller.view.bounds;
909
+ const viewWidth = Number(bounds?.size?.width) || 0;
910
+ const viewHeight = Number(bounds?.size?.height) || 0;
911
+ const raw = refs.controller.view.safeAreaInsets;
912
+ const safeInsets = raw
913
+ ? {
914
+ top: Number(raw.top) || 0,
915
+ bottom: Number(raw.bottom) || 0,
916
+ left: Number(raw.left) || 0,
917
+ right: Number(raw.right) || 0,
918
+ }
919
+ : { top: 0, bottom: 0, left: 0, right: 0 };
920
+ // Ask UIKit what the labels want given the panel inner width. We use a
921
+ // generous height bound so nothing clips on long reconnect strings.
922
+ const panelPadding = 16;
923
+ const horizontalMargin = 24;
924
+ const maxPanelWidth = 340;
925
+ const innerWidth = Math.max(0, Math.min(maxPanelWidth, viewWidth - 2 * horizontalMargin - safeInsets.left - safeInsets.right) - 2 * panelPadding);
926
+ const titleFit = refs.titleLabel.sizeThatFits({ width: innerWidth, height: 10000 }) || { height: 0 };
927
+ const statusFit = refs.statusLabel.sizeThatFits({ width: innerWidth, height: 10000 }) || { height: 0 };
928
+ const layout = computeIosOverlayLayout({
929
+ viewWidth,
930
+ viewHeight,
931
+ safeInsets,
932
+ titleHeight: Number(titleFit.height) || 0,
933
+ statusHeight: Number(statusFit.height) || 0,
934
+ maxPanelWidth,
935
+ horizontalMargin,
936
+ panelPadding,
937
+ });
938
+ const toCgRect = (rect) => ({
939
+ origin: { x: rect.x, y: rect.y },
940
+ size: { width: rect.width, height: rect.height },
941
+ });
942
+ refs.backdrop.frame = toCgRect(layout.backdrop);
943
+ refs.panel.frame = toCgRect(layout.panel);
944
+ refs.titleLabel.frame = toCgRect(layout.title);
945
+ refs.statusLabel.frame = toCgRect(layout.status);
946
+ }
947
+ catch (err) {
948
+ console.warn('[ns-hmr-overlay] iOS overlay layout failed:', err?.message || err);
949
+ }
950
+ }
951
+ function applySnapshotToIosRefs(refs, snapshot) {
952
+ if (!refs)
953
+ return false;
954
+ try {
955
+ // 'update' mode rides the same dedicated UIWindow as
956
+ // 'connection' so the HMR apply overlay always stacks above
957
+ // modals/sheets/system alerts. The window is constructed
958
+ // lazily (ensureIosOverlayRefs) and reused for the lifetime of
959
+ // the dev session.
960
+ const visible = snapshot.visible && (snapshot.mode === 'connection' || snapshot.mode === 'update');
961
+ refs.window.hidden = !visible;
962
+ if (!visible)
963
+ return true;
964
+ refs.titleLabel.text = snapshot.title || '';
965
+ refs.statusLabel.text = formatStatusText(snapshot);
966
+ const host = getIosOverlayHost();
967
+ if (host) {
968
+ const { UIColor } = host;
969
+ const isError = snapshot.tone === 'error';
970
+ const isSuccess = snapshot.tone === 'success';
971
+ try {
972
+ if (isError) {
973
+ // Red panel + dark red text (existing UX).
974
+ refs.panel.backgroundColor = UIColor.colorWithRedGreenBlueAlpha(1, 0.96, 0.96, 1);
975
+ refs.titleLabel.textColor = UIColor.colorWithRedGreenBlueAlpha(0.7, 0.1, 0.06, 1);
976
+ refs.statusLabel.textColor = UIColor.colorWithRedGreenBlueAlpha(0.7, 0.1, 0.06, 0.9);
977
+ // Slightly stronger dimming on errors; users need to
978
+ // notice these.
979
+ refs.backdrop.backgroundColor = UIColor.colorWithRedGreenBlueAlpha(0, 0, 0, 0.35);
980
+ }
981
+ else if (isSuccess) {
982
+ // Slightly more saturated green panel + dark-green
983
+ // text. The previous 0.94/0.99/0.95 background was
984
+ // nearly indistinguishable from white on most
985
+ // devices; this bump keeps long detail strings
986
+ // readable while making the apply event obviously
987
+ // "happening right now".
988
+ refs.panel.backgroundColor = UIColor.colorWithRedGreenBlueAlpha(0.9, 0.97, 0.91, 1);
989
+ refs.titleLabel.textColor = UIColor.colorWithRedGreenBlueAlpha(0.05, 0.43, 0.18, 1);
990
+ refs.statusLabel.textColor = UIColor.colorWithRedGreenBlueAlpha(0.05, 0.43, 0.18, 1);
991
+ // Bumped from 0.12 to 0.28. The 0.12 wash was so
992
+ // faint on bright app backgrounds that the overlay
993
+ // was effectively invisible during a fast cycle.
994
+ // 0.28 still keeps the app readable underneath but
995
+ // makes the HMR event visually unmistakable.
996
+ refs.backdrop.backgroundColor = UIColor.colorWithRedGreenBlueAlpha(0, 0.15, 0.05, 0.28);
997
+ }
998
+ else {
999
+ // Default (info / warn) — existing connection look.
1000
+ refs.panel.backgroundColor = UIColor.whiteColor;
1001
+ refs.titleLabel.textColor = UIColor.blackColor;
1002
+ refs.statusLabel.textColor = UIColor.darkGrayColor;
1003
+ refs.backdrop.backgroundColor = UIColor.colorWithRedGreenBlueAlpha(0, 0, 0, 0.35);
1004
+ }
1005
+ }
1006
+ catch { }
1007
+ }
1008
+ layoutIosOverlayRefs(refs);
1009
+ return true;
1010
+ }
1011
+ catch (err) {
1012
+ console.warn('[ns-hmr-overlay] iOS overlay apply failed:', err?.message || err);
1013
+ return false;
1014
+ }
1015
+ }
1016
+ function applyRuntimeSnapshot(snapshot) {
1017
+ const state = getRuntimeState();
1018
+ state.snapshot = snapshot;
1019
+ // Update the boot status label created by root-placeholder.ts
1020
+ if (snapshot.mode === 'boot') {
1021
+ updateBootStatusLabel(snapshot);
1022
+ }
1023
+ applySnapshotToBootRefs(state.bootRefs, snapshot);
1024
+ // prefer the dedicated UIWindow
1025
+ // path so the live/update overlays always stack on top of modals,
1026
+ // sheets, and other windows. Fall back to the in-tree overlay when
1027
+ // iOS APIs aren't available (Android, tests, or when scene
1028
+ // construction fails).
1029
+ let handledByIos = false;
1030
+ // Both 'connection' and 'update' use the small-panel surface
1031
+ // (UIWindow on iOS, in-tree overlay everywhere else). 'boot' uses
1032
+ // the placeholder root via applySnapshotToBootRefs above; 'hidden'
1033
+ // hides everything.
1034
+ const wantsOverlay = snapshot.visible && (snapshot.mode === 'connection' || snapshot.mode === 'update');
1035
+ if (getIosOverlayHost()) {
1036
+ if (wantsOverlay) {
1037
+ const iosRefs = ensureIosOverlayRefs(state);
1038
+ handledByIos = applySnapshotToIosRefs(iosRefs, snapshot);
1039
+ }
1040
+ else if (state.iosRefs) {
1041
+ handledByIos = applySnapshotToIosRefs(state.iosRefs, snapshot);
1042
+ }
1043
+ }
1044
+ if (!handledByIos) {
1045
+ if (wantsOverlay) {
1046
+ const liveRefs = ensureLiveOverlayRefs(snapshot);
1047
+ applySnapshotToLiveRefs(liveRefs, snapshot);
1048
+ }
1049
+ else {
1050
+ applySnapshotToLiveRefs(state.liveRefs, snapshot);
1051
+ }
1052
+ }
1053
+ return state.snapshot;
1054
+ }
1055
+ // How long the 'complete' frame stays on screen before we auto-hide.
1056
+ // The original 350ms was too tight: many HMR cycles complete in
1057
+ // 50–250ms, so the *total* overlay lifetime (received → complete +
1058
+ // 350ms) was often under 500ms, which is faster than the human eye
1059
+ // can comfortably register. 600ms gives the user time to read the
1060
+ // "Total Xms" line and confirm visually that something happened.
1061
+ const UPDATE_AUTO_HIDE_MS = 600;
1062
+ // Minimum perceptible duration for an entire update overlay cycle
1063
+ // (from 'received' to hide). If the cycle finished in 50ms (e.g., a
1064
+ // tiny HTML edit on a warm cache), we still hold for ~MIN_VISIBLE_MS
1065
+ // total before hiding so the overlay is actually seen. Combined with
1066
+ // UPDATE_AUTO_HIDE_MS, the *effective* hold-after-complete =
1067
+ // max(UPDATE_AUTO_HIDE_MS, MIN_VISIBLE_MS - elapsed-since-received).
1068
+ const UPDATE_MIN_VISIBLE_MS = 800;
1069
+ function clearUpdateAutoHideTimer(state) {
1070
+ if (state.updateAutoHideTimer) {
1071
+ try {
1072
+ clearTimeout(state.updateAutoHideTimer);
1073
+ }
1074
+ catch { }
1075
+ state.updateAutoHideTimer = null;
1076
+ }
1077
+ }
1078
+ function scheduleUpdateAutoHide(state) {
1079
+ clearUpdateAutoHideTimer(state);
1080
+ // Compute how much longer we need to hold the overlay so that the
1081
+ // total cycle visibility is at least UPDATE_MIN_VISIBLE_MS. For
1082
+ // fast cycles (50ms reboot) this stretches the hide; for slow
1083
+ // cycles (>UPDATE_MIN_VISIBLE_MS) it falls back to the standard
1084
+ // UPDATE_AUTO_HIDE_MS so we don't truncate the celebratory hold.
1085
+ const startedAt = state.updateCycleStartedAt || 0;
1086
+ const elapsed = startedAt > 0 ? Math.max(0, Date.now() - startedAt) : 0;
1087
+ const minRemainder = elapsed > 0 ? Math.max(0, UPDATE_MIN_VISIBLE_MS - elapsed) : UPDATE_MIN_VISIBLE_MS;
1088
+ const holdMs = Math.max(UPDATE_AUTO_HIDE_MS, minRemainder);
1089
+ try {
1090
+ state.updateAutoHideTimer = setTimeout(() => {
1091
+ state.updateAutoHideTimer = null;
1092
+ // Critical: only auto-hide if we're still on the 'complete'
1093
+ // frame. If a new HMR cycle has rotated the snapshot back
1094
+ // to 'update' / 'received' (e.g., user saved twice in
1095
+ // quick succession), the new cycle owns the overlay and
1096
+ // our timer must not steal it.
1097
+ const current = state.snapshot;
1098
+ if (current.mode === 'update' && current.tone === 'success' && current.progress === 100) {
1099
+ state.updateCycleStartedAt = 0;
1100
+ applyRuntimeSnapshot({ ...DEFAULT_SNAPSHOT });
1101
+ }
1102
+ }, holdMs);
1103
+ }
1104
+ catch {
1105
+ // setTimeout missing (extremely rare; some test envs). Fall
1106
+ // back to immediate hide so we never leave the overlay visible
1107
+ // forever after a 'complete'.
1108
+ state.updateCycleStartedAt = 0;
1109
+ applyRuntimeSnapshot({ ...DEFAULT_SNAPSHOT });
1110
+ }
1111
+ }
1112
+ function logUpdateStageTransition(state, stage, info) {
1113
+ if (!state.verbose)
1114
+ return;
1115
+ try {
1116
+ const detail = info?.detail || '';
1117
+ const progress = typeof info?.progress === 'number' ? info.progress : null;
1118
+ const progressTag = progress !== null ? ` (${Math.round(progress)}%)` : '';
1119
+ // Single-line breadcrumb so a developer can correlate
1120
+ // overlay frames with the [ns-hmr][angular] timing log when
1121
+ // debugging "I don't see the overlay" reports.
1122
+ console.info(`[ns-hmr-overlay] update stage=${stage}${progressTag}${detail ? ` detail=${detail}` : ''}`);
1123
+ }
1124
+ catch { }
1125
+ }
1126
+ function createOverlayApi() {
1127
+ return {
1128
+ ensureBootPage(verbose) {
1129
+ const state = getRuntimeState();
1130
+ state.verbose = state.verbose || !!verbose;
1131
+ if (!state.snapshot.visible || state.snapshot.mode !== 'boot') {
1132
+ state.snapshot = createBootOverlaySnapshot('placeholder');
1133
+ }
1134
+ if (!state.bootRefs) {
1135
+ state.bootRefs = buildBootOverlayRefs(state.snapshot);
1136
+ }
1137
+ applySnapshotToBootRefs(state.bootRefs, state.snapshot);
1138
+ return state.bootRefs?.page || null;
1139
+ },
1140
+ setBootStage(stage, info) {
1141
+ // A boot transition cancels any pending HMR auto-hide so
1142
+ // the boot phase always wins.
1143
+ const state = getRuntimeState();
1144
+ clearUpdateAutoHideTimer(state);
1145
+ state.updateCycleStartedAt = 0;
1146
+ return applyRuntimeSnapshot(createBootOverlaySnapshot(stage, info));
1147
+ },
1148
+ setConnectionStage(stage, info) {
1149
+ const state = getRuntimeState();
1150
+ clearUpdateAutoHideTimer(state);
1151
+ state.updateCycleStartedAt = 0;
1152
+ return applyRuntimeSnapshot(createConnectionOverlaySnapshot(stage, info));
1153
+ },
1154
+ setUpdateStage(stage, info) {
1155
+ const state = getRuntimeState();
1156
+ // Each new in-progress stage cancels any pending auto-hide
1157
+ // from a previous cycle. Without this, two saves in quick
1158
+ // succession could see cycle-2's progress overlay yanked
1159
+ // off by cycle-1's already-scheduled hide.
1160
+ clearUpdateAutoHideTimer(state);
1161
+ // Stamp the cycle start on 'received', but distinguish
1162
+ // between two cases:
1163
+ //
1164
+ // (a) Re-assertion of the SAME cycle (e.g., the server
1165
+ // emits both `ns:hmr-pending` AND `ns:angular-update`,
1166
+ // both of which call `setUpdateStage('received')`).
1167
+ // We must PRESERVE the original timestamp so the
1168
+ // minimum-visible-window math measures the FIRST
1169
+ // 'received' the user actually saw.
1170
+ //
1171
+ // (b) Genuinely-new cycle starting either from a hidden
1172
+ // overlay OR while the previous cycle is still on
1173
+ // its 'complete' frame (pre auto-hide). In both
1174
+ // sub-cases we MUST stamp a fresh start so the
1175
+ // new cycle's auto-hide math is sane.
1176
+ //
1177
+ // We treat the previous snapshot as "in-progress for the
1178
+ // same cycle" iff mode==='update' AND progress!==100.
1179
+ // 'complete' frames are a sign that the cycle finished;
1180
+ // any subsequent 'received' is a NEW cycle.
1181
+ if (stage === 'received') {
1182
+ const prev = state.snapshot;
1183
+ const isMidCycleReassertion = prev.mode === 'update' && prev.progress !== 100;
1184
+ if (!isMidCycleReassertion) {
1185
+ state.updateCycleStartedAt = Date.now();
1186
+ }
1187
+ }
1188
+ logUpdateStageTransition(state, stage, info);
1189
+ const snapshot = applyRuntimeSnapshot(createUpdateOverlaySnapshot(stage, info));
1190
+ if (stage === 'complete') {
1191
+ scheduleUpdateAutoHide(state);
1192
+ }
1193
+ return snapshot;
1194
+ },
1195
+ hide() {
1196
+ const state = getRuntimeState();
1197
+ clearUpdateAutoHideTimer(state);
1198
+ state.updateCycleStartedAt = 0;
1199
+ applyRuntimeSnapshot({ ...DEFAULT_SNAPSHOT });
1200
+ },
1201
+ getSnapshot() {
1202
+ return getRuntimeState().snapshot;
1203
+ },
1204
+ };
1205
+ }
1206
+ export function ensureHmrDevOverlayRuntimeInstalled(verbose) {
1207
+ const g = getOverlayGlobal();
1208
+ const state = getRuntimeState();
1209
+ state.verbose = state.verbose || !!verbose;
1210
+ if (!g.__NS_HMR_DEV_OVERLAY__) {
1211
+ g.__NS_HMR_DEV_OVERLAY__ = createOverlayApi();
1212
+ }
1213
+ return g.__NS_HMR_DEV_OVERLAY__;
1214
+ }
1215
+ export function createHmrBootOverlayPage(verbose) {
1216
+ return ensureHmrDevOverlayRuntimeInstalled(verbose).ensureBootPage(verbose);
1217
+ }
1218
+ export function setHmrBootStage(stage, info) {
1219
+ return ensureHmrDevOverlayRuntimeInstalled().setBootStage(stage, info);
1220
+ }
1221
+ export function setHmrConnectionStage(stage, info) {
1222
+ return ensureHmrDevOverlayRuntimeInstalled().setConnectionStage(stage, info);
1223
+ }
1224
+ // Public entry point for driving the HMR-applying overlay. Callers
1225
+ // walk through stages (received → evicting → reimporting → rebooting
1226
+ // → complete); 'complete' auto-hides after a short interval.
1227
+ // Soft-fails (no-op) if the runtime overlay was never installed
1228
+ // (e.g., production builds, test environments).
1229
+ export function setHmrUpdateStage(stage, info) {
1230
+ return ensureHmrDevOverlayRuntimeInstalled().setUpdateStage(stage, info);
1231
+ }
1232
+ export function hideHmrDevOverlay(reason) {
1233
+ void reason;
1234
+ ensureHmrDevOverlayRuntimeInstalled().hide(reason);
1235
+ }
1236
+ //# sourceMappingURL=dev-overlay.js.map