@nativescript/vite 8.0.0-alpha.3 → 8.0.0-alpha.31

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 (356) hide show
  1. package/README.md +51 -11
  2. package/configuration/angular.d.ts +34 -1
  3. package/configuration/angular.js +373 -163
  4. package/configuration/angular.js.map +1 -1
  5. package/configuration/base.js +184 -14
  6. package/configuration/base.js.map +1 -1
  7. package/configuration/javascript.js +5 -72
  8. package/configuration/javascript.js.map +1 -1
  9. package/configuration/solid.js +27 -1
  10. package/configuration/solid.js.map +1 -1
  11. package/configuration/typescript.js +5 -75
  12. package/configuration/typescript.js.map +1 -1
  13. package/helpers/app-components.d.ts +2 -1
  14. package/helpers/app-components.js.map +1 -1
  15. package/helpers/app-css-state.d.ts +8 -0
  16. package/helpers/app-css-state.js +8 -0
  17. package/helpers/app-css-state.js.map +1 -0
  18. package/helpers/bundler-context.d.ts +11 -0
  19. package/helpers/bundler-context.js +71 -0
  20. package/helpers/bundler-context.js.map +1 -0
  21. package/helpers/config-as-json.js +10 -0
  22. package/helpers/config-as-json.js.map +1 -1
  23. package/helpers/dev-host.d.ts +341 -0
  24. package/helpers/dev-host.js +617 -0
  25. package/helpers/dev-host.js.map +1 -0
  26. package/helpers/esbuild-platform-resolver.js +4 -1
  27. package/helpers/esbuild-platform-resolver.js.map +1 -1
  28. package/helpers/global-defines.d.ts +51 -0
  29. package/helpers/global-defines.js +77 -0
  30. package/helpers/global-defines.js.map +1 -1
  31. package/helpers/hmr-scope.d.ts +26 -0
  32. package/helpers/hmr-scope.js +67 -0
  33. package/helpers/hmr-scope.js.map +1 -0
  34. package/helpers/init.js +0 -18
  35. package/helpers/init.js.map +1 -1
  36. package/helpers/logging.d.ts +1 -0
  37. package/helpers/logging.js +63 -3
  38. package/helpers/logging.js.map +1 -1
  39. package/helpers/main-entry.d.ts +2 -1
  40. package/helpers/main-entry.js +430 -47
  41. package/helpers/main-entry.js.map +1 -1
  42. package/helpers/nativeclass-esbuild-plugin.d.ts +2 -1
  43. package/helpers/nativeclass-esbuild-plugin.js.map +1 -1
  44. package/helpers/nativeclass-transform.js.map +1 -1
  45. package/helpers/nativeclass-transformer-plugin.d.ts +9 -2
  46. package/helpers/nativeclass-transformer-plugin.js +157 -14
  47. package/helpers/nativeclass-transformer-plugin.js.map +1 -1
  48. package/helpers/nativescript-package-resolver.js +8 -3
  49. package/helpers/nativescript-package-resolver.js.map +1 -1
  50. package/helpers/normalize-id.d.ts +42 -0
  51. package/helpers/normalize-id.js +60 -0
  52. package/helpers/normalize-id.js.map +1 -0
  53. package/helpers/ns-core-url.d.ts +88 -0
  54. package/helpers/ns-core-url.js +191 -0
  55. package/helpers/ns-core-url.js.map +1 -0
  56. package/helpers/package-platform-aliases.js +4 -3
  57. package/helpers/package-platform-aliases.js.map +1 -1
  58. package/helpers/platform-types.d.ts +2 -0
  59. package/helpers/platform-types.js +2 -0
  60. package/helpers/platform-types.js.map +1 -0
  61. package/helpers/project.d.ts +35 -0
  62. package/helpers/project.js +120 -2
  63. package/helpers/project.js.map +1 -1
  64. package/helpers/resolver.js +17 -2
  65. package/helpers/resolver.js.map +1 -1
  66. package/helpers/ts-config-paths.d.ts +14 -0
  67. package/helpers/ts-config-paths.js +89 -8
  68. package/helpers/ts-config-paths.js.map +1 -1
  69. package/helpers/typescript-check.d.ts +2 -1
  70. package/helpers/typescript-check.js.map +1 -1
  71. package/helpers/workers.d.ts +20 -19
  72. package/helpers/workers.js +624 -4
  73. package/helpers/workers.js.map +1 -1
  74. package/hmr/client/css-handler.d.ts +1 -0
  75. package/hmr/client/css-handler.js +33 -20
  76. package/hmr/client/css-handler.js.map +1 -1
  77. package/hmr/client/css-update-overlay.d.ts +18 -0
  78. package/hmr/client/css-update-overlay.js +27 -0
  79. package/hmr/client/css-update-overlay.js.map +1 -0
  80. package/hmr/client/framework-client-strategy.d.ts +73 -0
  81. package/hmr/client/framework-client-strategy.js +19 -0
  82. package/hmr/client/framework-client-strategy.js.map +1 -0
  83. package/hmr/client/hmr-pending-overlay.d.ts +27 -0
  84. package/hmr/client/hmr-pending-overlay.js +50 -0
  85. package/hmr/client/hmr-pending-overlay.js.map +1 -0
  86. package/hmr/client/index.js +459 -164
  87. package/hmr/client/index.js.map +1 -1
  88. package/hmr/client/utils.d.ts +6 -1
  89. package/hmr/client/utils.js +184 -8
  90. package/hmr/client/utils.js.map +1 -1
  91. package/hmr/entry-runtime.d.ts +2 -1
  92. package/hmr/entry-runtime.js +252 -65
  93. package/hmr/entry-runtime.js.map +1 -1
  94. package/hmr/frameworks/angular/build/angular-linker.d.ts +12 -0
  95. package/hmr/frameworks/angular/build/angular-linker.js +109 -0
  96. package/hmr/frameworks/angular/build/angular-linker.js.map +1 -0
  97. package/hmr/frameworks/angular/build/inject-component-hmr-registration.d.ts +112 -0
  98. package/hmr/frameworks/angular/build/inject-component-hmr-registration.js +291 -0
  99. package/hmr/frameworks/angular/build/inject-component-hmr-registration.js.map +1 -0
  100. package/hmr/frameworks/angular/build/inject-hmr-vite-ignore.d.ts +75 -0
  101. package/hmr/frameworks/angular/build/inject-hmr-vite-ignore.js +221 -0
  102. package/hmr/frameworks/angular/build/inject-hmr-vite-ignore.js.map +1 -0
  103. package/{helpers/angular → hmr/frameworks/angular/build}/inline-decorator-component-templates.js +1 -170
  104. package/hmr/frameworks/angular/build/inline-decorator-component-templates.js.map +1 -0
  105. package/hmr/frameworks/angular/build/js-lexer.d.ts +4 -0
  106. package/{helpers/angular/synthesize-decorator-ctor-parameters.js → hmr/frameworks/angular/build/js-lexer.js} +22 -96
  107. package/hmr/frameworks/angular/build/js-lexer.js.map +1 -0
  108. package/hmr/frameworks/angular/build/shared-linker.d.ts +39 -0
  109. package/hmr/frameworks/angular/build/shared-linker.js +128 -0
  110. package/hmr/frameworks/angular/build/shared-linker.js.map +1 -0
  111. package/hmr/frameworks/angular/build/synthesize-decorator-ctor-parameters.js +88 -0
  112. package/hmr/frameworks/angular/build/synthesize-decorator-ctor-parameters.js.map +1 -0
  113. package/{helpers/angular → hmr/frameworks/angular/build}/synthesize-injectable-factories.js +1 -174
  114. package/hmr/frameworks/angular/build/synthesize-injectable-factories.js.map +1 -0
  115. package/{helpers/angular → hmr/frameworks/angular/build}/util.d.ts +1 -0
  116. package/hmr/frameworks/angular/build/util.js +155 -0
  117. package/hmr/frameworks/angular/build/util.js.map +1 -0
  118. package/hmr/frameworks/angular/client/index.d.ts +1 -0
  119. package/hmr/frameworks/angular/client/index.js +778 -20
  120. package/hmr/frameworks/angular/client/index.js.map +1 -1
  121. package/hmr/frameworks/angular/client/strategy.d.ts +9 -0
  122. package/hmr/frameworks/angular/client/strategy.js +19 -0
  123. package/hmr/frameworks/angular/client/strategy.js.map +1 -0
  124. package/hmr/frameworks/angular/server/angular-root-component.d.ts +79 -0
  125. package/hmr/frameworks/angular/server/angular-root-component.js +149 -0
  126. package/hmr/frameworks/angular/server/angular-root-component.js.map +1 -0
  127. package/hmr/frameworks/angular/server/linker.js +1 -4
  128. package/hmr/frameworks/angular/server/linker.js.map +1 -1
  129. package/hmr/frameworks/angular/server/strategy.js +448 -12
  130. package/hmr/frameworks/angular/server/strategy.js.map +1 -1
  131. package/hmr/{server → frameworks/angular/server}/websocket-angular-entry.js +2 -2
  132. package/hmr/frameworks/angular/server/websocket-angular-entry.js.map +1 -0
  133. package/hmr/{server → frameworks/angular/server}/websocket-angular-hot-update.d.ts +17 -11
  134. package/hmr/frameworks/angular/server/websocket-angular-hot-update.js +336 -0
  135. package/hmr/frameworks/angular/server/websocket-angular-hot-update.js.map +1 -0
  136. package/hmr/frameworks/solid/build/solid-jsx-deps.d.ts +15 -0
  137. package/hmr/frameworks/solid/build/solid-jsx-deps.js +178 -0
  138. package/hmr/frameworks/solid/build/solid-jsx-deps.js.map +1 -0
  139. package/hmr/frameworks/solid/server/strategy.js +360 -16
  140. package/hmr/frameworks/solid/server/strategy.js.map +1 -1
  141. package/hmr/frameworks/typescript/server/strategy.js +28 -14
  142. package/hmr/frameworks/typescript/server/strategy.js.map +1 -1
  143. package/hmr/frameworks/vue/client/index.js +30 -199
  144. package/hmr/frameworks/vue/client/index.js.map +1 -1
  145. package/hmr/frameworks/vue/client/strategy.d.ts +7 -0
  146. package/hmr/frameworks/vue/client/strategy.js +83 -0
  147. package/hmr/frameworks/vue/client/strategy.js.map +1 -0
  148. package/hmr/frameworks/vue/client/vue-sfc-update-overlay.d.ts +82 -0
  149. package/hmr/frameworks/vue/client/vue-sfc-update-overlay.js +133 -0
  150. package/hmr/frameworks/vue/client/vue-sfc-update-overlay.js.map +1 -0
  151. package/hmr/frameworks/vue/server/sfc-route-assemble.d.ts +7 -0
  152. package/hmr/frameworks/vue/server/sfc-route-assemble.js +706 -0
  153. package/hmr/frameworks/vue/server/sfc-route-assemble.js.map +1 -0
  154. package/hmr/frameworks/vue/server/sfc-route-meta.d.ts +7 -0
  155. package/hmr/frameworks/vue/server/sfc-route-meta.js +80 -0
  156. package/hmr/frameworks/vue/server/sfc-route-meta.js.map +1 -0
  157. package/hmr/frameworks/vue/server/sfc-route-serve.d.ts +8 -0
  158. package/hmr/frameworks/vue/server/sfc-route-serve.js +457 -0
  159. package/hmr/frameworks/vue/server/sfc-route-serve.js.map +1 -0
  160. package/hmr/frameworks/vue/server/sfc-route-shared.d.ts +19 -0
  161. package/hmr/frameworks/vue/server/sfc-route-shared.js +14 -0
  162. package/hmr/frameworks/vue/server/sfc-route-shared.js.map +1 -0
  163. package/hmr/frameworks/vue/server/strategy.js +244 -0
  164. package/hmr/frameworks/vue/server/strategy.js.map +1 -1
  165. package/hmr/frameworks/vue/server/websocket-sfc.d.ts +15 -0
  166. package/hmr/frameworks/vue/server/websocket-sfc.js +20 -0
  167. package/hmr/frameworks/vue/server/websocket-sfc.js.map +1 -0
  168. package/hmr/helpers/ast-normalizer.js +52 -5
  169. package/hmr/helpers/ast-normalizer.js.map +1 -1
  170. package/hmr/helpers/cjs-named-exports.d.ts +23 -0
  171. package/hmr/helpers/cjs-named-exports.js +152 -0
  172. package/hmr/helpers/cjs-named-exports.js.map +1 -0
  173. package/hmr/helpers/package-exports.d.ts +16 -0
  174. package/hmr/helpers/package-exports.js +396 -0
  175. package/hmr/helpers/package-exports.js.map +1 -0
  176. package/hmr/server/constants.js +13 -4
  177. package/hmr/server/constants.js.map +1 -1
  178. package/hmr/server/core-sanitize.d.ts +90 -7
  179. package/hmr/server/core-sanitize.js +211 -56
  180. package/hmr/server/core-sanitize.js.map +1 -1
  181. package/hmr/server/device-transform-helpers.d.ts +24 -0
  182. package/hmr/server/device-transform-helpers.js +327 -0
  183. package/hmr/server/device-transform-helpers.js.map +1 -0
  184. package/hmr/server/framework-strategy.d.ts +95 -11
  185. package/hmr/server/hmr-module-graph.d.ts +37 -0
  186. package/hmr/server/hmr-module-graph.js +214 -0
  187. package/hmr/server/hmr-module-graph.js.map +1 -0
  188. package/hmr/server/import-map.d.ts +11 -2
  189. package/hmr/server/import-map.js +59 -40
  190. package/hmr/server/import-map.js.map +1 -1
  191. package/hmr/server/index.js +7 -16
  192. package/hmr/server/index.js.map +1 -1
  193. package/hmr/server/ns-core-cjs-shape.d.ts +204 -0
  194. package/hmr/server/ns-core-cjs-shape.js +271 -0
  195. package/hmr/server/ns-core-cjs-shape.js.map +1 -0
  196. package/hmr/server/ns-rt-bridge.d.ts +51 -0
  197. package/hmr/server/ns-rt-bridge.js +131 -0
  198. package/hmr/server/ns-rt-bridge.js.map +1 -0
  199. package/hmr/server/ns-rt-route.d.ts +5 -0
  200. package/hmr/server/ns-rt-route.js +35 -0
  201. package/hmr/server/ns-rt-route.js.map +1 -0
  202. package/hmr/server/perf-instrumentation.d.ts +114 -0
  203. package/hmr/server/perf-instrumentation.js +195 -0
  204. package/hmr/server/perf-instrumentation.js.map +1 -0
  205. package/hmr/server/process-code-for-device.d.ts +15 -0
  206. package/hmr/server/process-code-for-device.js +654 -0
  207. package/hmr/server/process-code-for-device.js.map +1 -0
  208. package/hmr/server/require-guard.d.ts +1 -0
  209. package/hmr/server/require-guard.js +12 -0
  210. package/hmr/server/require-guard.js.map +1 -0
  211. package/hmr/server/rewrite-imports.d.ts +2 -0
  212. package/hmr/server/rewrite-imports.js +604 -0
  213. package/hmr/server/rewrite-imports.js.map +1 -0
  214. package/hmr/server/route-helpers.d.ts +7 -0
  215. package/hmr/server/route-helpers.js +13 -0
  216. package/hmr/server/route-helpers.js.map +1 -0
  217. package/hmr/server/server-origin.d.ts +12 -0
  218. package/hmr/server/server-origin.js +66 -0
  219. package/hmr/server/server-origin.js.map +1 -0
  220. package/hmr/server/shared-transform-request.js +12 -5
  221. package/hmr/server/shared-transform-request.js.map +1 -1
  222. package/hmr/server/transform-cache-invalidation.d.ts +11 -0
  223. package/hmr/server/transform-cache-invalidation.js +84 -0
  224. package/hmr/server/transform-cache-invalidation.js.map +1 -0
  225. package/hmr/server/vendor-bare-module-shims.d.ts +4 -0
  226. package/hmr/server/vendor-bare-module-shims.js +80 -0
  227. package/hmr/server/vendor-bare-module-shims.js.map +1 -0
  228. package/hmr/server/vite-plugin.js +60 -42
  229. package/hmr/server/vite-plugin.js.map +1 -1
  230. package/hmr/server/websocket-core-bridge.d.ts +41 -6
  231. package/hmr/server/websocket-core-bridge.js +72 -75
  232. package/hmr/server/websocket-core-bridge.js.map +1 -1
  233. package/hmr/server/websocket-css-hot-update.d.ts +33 -0
  234. package/hmr/server/websocket-css-hot-update.js +65 -0
  235. package/hmr/server/websocket-css-hot-update.js.map +1 -0
  236. package/hmr/server/websocket-device-transform.d.ts +3 -0
  237. package/hmr/server/websocket-device-transform.js +7 -0
  238. package/hmr/server/websocket-device-transform.js.map +1 -0
  239. package/hmr/server/websocket-graph-upsert.d.ts +15 -0
  240. package/hmr/server/websocket-graph-upsert.js +20 -0
  241. package/hmr/server/websocket-graph-upsert.js.map +1 -1
  242. package/hmr/server/websocket-hmr-pending.d.ts +37 -0
  243. package/hmr/server/websocket-hmr-pending.js +55 -0
  244. package/hmr/server/websocket-hmr-pending.js.map +1 -0
  245. package/hmr/server/websocket-hot-update.d.ts +77 -0
  246. package/hmr/server/websocket-hot-update.js +330 -0
  247. package/hmr/server/websocket-hot-update.js.map +1 -0
  248. package/hmr/server/websocket-import-map-route.d.ts +15 -0
  249. package/hmr/server/websocket-import-map-route.js +46 -0
  250. package/hmr/server/websocket-import-map-route.js.map +1 -0
  251. package/hmr/server/websocket-module-bindings.js +3 -3
  252. package/hmr/server/websocket-module-bindings.js.map +1 -1
  253. package/hmr/server/websocket-module-specifiers.d.ts +66 -2
  254. package/hmr/server/websocket-module-specifiers.js +202 -19
  255. package/hmr/server/websocket-module-specifiers.js.map +1 -1
  256. package/hmr/server/websocket-ns-core.d.ts +21 -0
  257. package/hmr/server/websocket-ns-core.js +306 -0
  258. package/hmr/server/websocket-ns-core.js.map +1 -0
  259. package/hmr/server/websocket-ns-entry.d.ts +21 -0
  260. package/hmr/server/websocket-ns-entry.js +150 -0
  261. package/hmr/server/websocket-ns-entry.js.map +1 -0
  262. package/hmr/server/websocket-ns-m-paths.d.ts +3 -0
  263. package/hmr/server/websocket-ns-m-paths.js +92 -0
  264. package/hmr/server/websocket-ns-m-paths.js.map +1 -0
  265. package/hmr/server/websocket-ns-m-request.d.ts +45 -0
  266. package/hmr/server/websocket-ns-m-request.js +196 -0
  267. package/hmr/server/websocket-ns-m-request.js.map +1 -0
  268. package/hmr/server/websocket-ns-m.d.ts +33 -0
  269. package/hmr/server/websocket-ns-m.js +748 -0
  270. package/hmr/server/websocket-ns-m.js.map +1 -0
  271. package/hmr/server/websocket-served-module-helpers.d.ts +39 -0
  272. package/hmr/server/websocket-served-module-helpers.js +654 -0
  273. package/hmr/server/websocket-served-module-helpers.js.map +1 -0
  274. package/hmr/server/websocket-txn.d.ts +6 -0
  275. package/hmr/server/websocket-txn.js +39 -0
  276. package/hmr/server/websocket-txn.js.map +1 -0
  277. package/hmr/server/websocket-vendor-unifier.d.ts +9 -0
  278. package/hmr/server/websocket-vendor-unifier.js +46 -0
  279. package/hmr/server/websocket-vendor-unifier.js.map +1 -0
  280. package/hmr/server/websocket.d.ts +8 -39
  281. package/hmr/server/websocket.js +602 -6049
  282. package/hmr/server/websocket.js.map +1 -1
  283. package/hmr/shared/ns-globals.d.ts +118 -0
  284. package/hmr/shared/ns-globals.js +27 -0
  285. package/hmr/shared/ns-globals.js.map +1 -0
  286. package/hmr/shared/protocol.d.ts +136 -0
  287. package/hmr/shared/protocol.js +28 -0
  288. package/hmr/shared/protocol.js.map +1 -0
  289. package/hmr/shared/runtime/boot-placeholder-ui.d.ts +69 -0
  290. package/hmr/shared/runtime/boot-placeholder-ui.js +101 -0
  291. package/hmr/shared/runtime/boot-placeholder-ui.js.map +1 -0
  292. package/hmr/shared/runtime/boot-progress.d.ts +40 -0
  293. package/hmr/shared/runtime/boot-progress.js +128 -0
  294. package/hmr/shared/runtime/boot-progress.js.map +1 -0
  295. package/hmr/shared/runtime/boot-timeline.d.ts +18 -0
  296. package/hmr/shared/runtime/boot-timeline.js +52 -0
  297. package/hmr/shared/runtime/boot-timeline.js.map +1 -0
  298. package/hmr/shared/runtime/dev-overlay-snapshots.d.ts +31 -0
  299. package/hmr/shared/runtime/dev-overlay-snapshots.js +324 -0
  300. package/hmr/shared/runtime/dev-overlay-snapshots.js.map +1 -0
  301. package/hmr/shared/runtime/dev-overlay.d.ts +75 -26
  302. package/hmr/shared/runtime/dev-overlay.js +990 -260
  303. package/hmr/shared/runtime/dev-overlay.js.map +1 -1
  304. package/hmr/shared/runtime/module-provenance.js +1 -4
  305. package/hmr/shared/runtime/module-provenance.js.map +1 -1
  306. package/hmr/shared/runtime/root-placeholder-view.d.ts +19 -0
  307. package/hmr/shared/runtime/root-placeholder-view.js +310 -0
  308. package/hmr/shared/runtime/root-placeholder-view.js.map +1 -0
  309. package/hmr/shared/runtime/root-placeholder.js +352 -194
  310. package/hmr/shared/runtime/root-placeholder.js.map +1 -1
  311. package/hmr/shared/runtime/session-bootstrap.js +164 -1
  312. package/hmr/shared/runtime/session-bootstrap.js.map +1 -1
  313. package/hmr/shared/runtime/vendor-bootstrap.js +1 -9
  314. package/hmr/shared/runtime/vendor-bootstrap.js.map +1 -1
  315. package/hmr/shared/vendor/manifest-collect.d.ts +4 -0
  316. package/hmr/shared/vendor/manifest-collect.js +512 -0
  317. package/hmr/shared/vendor/manifest-collect.js.map +1 -0
  318. package/hmr/shared/vendor/manifest-loader.d.ts +2 -1
  319. package/hmr/shared/vendor/manifest-loader.js +3 -2
  320. package/hmr/shared/vendor/manifest-loader.js.map +1 -1
  321. package/hmr/shared/vendor/manifest.d.ts +1 -7
  322. package/hmr/shared/vendor/manifest.js +102 -741
  323. package/hmr/shared/vendor/manifest.js.map +1 -1
  324. package/hmr/shared/vendor/vendor-device-shim.d.ts +1 -0
  325. package/hmr/shared/vendor/vendor-device-shim.js +208 -0
  326. package/hmr/shared/vendor/vendor-device-shim.js.map +1 -0
  327. package/hmr/shared/vendor/vendor-esbuild-plugins.d.ts +16 -0
  328. package/hmr/shared/vendor/vendor-esbuild-plugins.js +203 -0
  329. package/hmr/shared/vendor/vendor-esbuild-plugins.js.map +1 -0
  330. package/index.d.ts +1 -0
  331. package/index.js +5 -0
  332. package/index.js.map +1 -1
  333. package/package.json +55 -11
  334. package/runtime/core-aliases-early.js +17 -41
  335. package/runtime/core-aliases-early.js.map +1 -1
  336. package/helpers/angular/angular-linker.d.ts +0 -13
  337. package/helpers/angular/angular-linker.js +0 -194
  338. package/helpers/angular/angular-linker.js.map +0 -1
  339. package/helpers/angular/inline-decorator-component-templates.js.map +0 -1
  340. package/helpers/angular/shared-linker.d.ts +0 -11
  341. package/helpers/angular/shared-linker.js +0 -75
  342. package/helpers/angular/shared-linker.js.map +0 -1
  343. package/helpers/angular/synthesize-decorator-ctor-parameters.js.map +0 -1
  344. package/helpers/angular/synthesize-injectable-factories.js.map +0 -1
  345. package/helpers/angular/util.js +0 -67
  346. package/helpers/angular/util.js.map +0 -1
  347. package/helpers/prelink-angular.d.ts +0 -2
  348. package/helpers/prelink-angular.js +0 -117
  349. package/helpers/prelink-angular.js.map +0 -1
  350. package/hmr/server/websocket-angular-entry.js.map +0 -1
  351. package/hmr/server/websocket-angular-hot-update.js +0 -239
  352. package/hmr/server/websocket-angular-hot-update.js.map +0 -1
  353. /package/{helpers/angular → hmr/frameworks/angular/build}/inline-decorator-component-templates.d.ts +0 -0
  354. /package/{helpers/angular → hmr/frameworks/angular/build}/synthesize-decorator-ctor-parameters.d.ts +0 -0
  355. /package/{helpers/angular → hmr/frameworks/angular/build}/synthesize-injectable-factories.d.ts +0 -0
  356. /package/hmr/{server → frameworks/angular/server}/websocket-angular-entry.d.ts +0 -0
@@ -1,19 +1,42 @@
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
- };
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
+ // Re-export the snapshot model so existing `./dev-overlay.js` importers keep working.
4
+ export { createBootOverlaySnapshot, createConnectionOverlaySnapshot, createUpdateOverlaySnapshot } from './dev-overlay-snapshots.js';
5
+ const DEFAULT_OVERLAY_POSITION = 'top';
14
6
  function getOverlayGlobal() {
15
7
  return globalThis;
16
8
  }
9
+ /**
10
+ * Resolve the configured live-overlay position.
11
+ *
12
+ * Reads `globalThis.__NS_HMR_OVERLAY_POSITION__` so a project can
13
+ * override the default at boot time (e.g. inside `app.ts` before the
14
+ * Vite session bootstraps). Falls back to 'top' which gives the
15
+ * toast-style chip with a slide-in animation and safe-area padding.
16
+ */
17
+ export function getHmrDevOverlayPosition() {
18
+ const g = getOverlayGlobal();
19
+ const stored = g.__NS_HMR_OVERLAY_POSITION__;
20
+ if (stored === 'top' || stored === 'bottom' || stored === 'center') {
21
+ return stored;
22
+ }
23
+ return DEFAULT_OVERLAY_POSITION;
24
+ }
25
+ /**
26
+ * Imperative setter for the live-overlay position. Re-applies the
27
+ * current snapshot so the change is visible without waiting for the
28
+ * next HMR cycle. Useful during dev to A/B between top/bottom/center
29
+ * without restarting the app.
30
+ */
31
+ export function setHmrDevOverlayPosition(position) {
32
+ if (position !== 'top' && position !== 'bottom' && position !== 'center') {
33
+ return;
34
+ }
35
+ const g = getOverlayGlobal();
36
+ g.__NS_HMR_OVERLAY_POSITION__ = position;
37
+ const state = getRuntimeState();
38
+ applyRuntimeSnapshot(state.snapshot);
39
+ }
17
40
  function getRuntimeState() {
18
41
  const g = getOverlayGlobal();
19
42
  if (!g.__NS_HMR_DEV_OVERLAY_STATE__) {
@@ -21,240 +44,25 @@ function getRuntimeState() {
21
44
  snapshot: { ...DEFAULT_SNAPSHOT },
22
45
  bootRefs: null,
23
46
  liveRefs: null,
47
+ iosRefs: null,
48
+ iosBuildFailed: false,
24
49
  verbose: false,
50
+ updateAutoHideTimer: null,
51
+ updateCycleStartedAt: 0,
25
52
  };
26
53
  }
27
- return g.__NS_HMR_DEV_OVERLAY_STATE__;
28
- }
29
- function describeAttempt(info) {
30
- const attempt = Number(info?.attempt || 0);
31
- const attempts = Number(info?.attempts || 0);
32
- if (!attempt || !attempts) {
33
- return '';
34
- }
35
- return `Attempt ${attempt}/${attempts}`;
36
- }
37
- export function createBootOverlaySnapshot(stage, info) {
38
- const attemptText = describeAttempt(info);
39
- const phaseInfo = {
40
- placeholder: {
41
- visible: true,
42
- mode: 'boot',
43
- badge: 'BOOT',
44
- title: BOOT_TITLE,
45
- phase: 'Preparing the HTTP HMR bootstrap',
46
- progress: 4,
47
- busy: true,
48
- blocking: true,
49
- tone: 'info',
50
- },
51
- 'probing-origin': {
52
- visible: true,
53
- mode: 'boot',
54
- badge: 'BOOT',
55
- title: BOOT_TITLE,
56
- phase: 'Contacting the Vite dev server',
57
- progress: 8,
58
- busy: true,
59
- blocking: true,
60
- tone: 'info',
61
- },
62
- 'loading-entry-runtime': {
63
- visible: true,
64
- mode: 'boot',
65
- badge: 'BOOT',
66
- title: BOOT_TITLE,
67
- phase: 'Loading the entry runtime bridge',
68
- progress: 16,
69
- busy: true,
70
- blocking: true,
71
- tone: 'info',
72
- },
73
- 'configuring-import-map': {
74
- visible: true,
75
- mode: 'boot',
76
- badge: 'BOOT',
77
- title: BOOT_TITLE,
78
- phase: 'Configuring the import map',
79
- progress: 26,
80
- busy: true,
81
- blocking: true,
82
- tone: 'info',
83
- },
84
- 'loading-runtime-bridge': {
85
- visible: true,
86
- mode: 'boot',
87
- badge: 'BOOT',
88
- title: BOOT_TITLE,
89
- phase: 'Loading the NativeScript runtime bridge',
90
- progress: 40,
91
- busy: true,
92
- blocking: true,
93
- tone: 'info',
94
- },
95
- 'loading-core-bridge': {
96
- visible: true,
97
- mode: 'boot',
98
- badge: 'BOOT',
99
- title: BOOT_TITLE,
100
- phase: 'Loading the unified core bridge',
101
- progress: 54,
102
- busy: true,
103
- blocking: true,
104
- tone: 'info',
105
- },
106
- 'preloading-style-scope': {
107
- visible: true,
108
- mode: 'boot',
109
- badge: 'BOOT',
110
- title: BOOT_TITLE,
111
- phase: 'Preparing the shared style scope',
112
- progress: 62,
113
- busy: true,
114
- blocking: true,
115
- tone: 'info',
116
- },
117
- 'installing-css': {
118
- visible: true,
119
- mode: 'boot',
120
- badge: 'BOOT',
121
- title: BOOT_TITLE,
122
- phase: 'Applying the app stylesheet',
123
- progress: 70,
124
- busy: true,
125
- blocking: true,
126
- tone: 'info',
127
- },
128
- 'importing-main': {
129
- visible: true,
130
- mode: 'boot',
131
- badge: 'BOOT',
132
- title: BOOT_TITLE,
133
- phase: 'Importing the app entry',
134
- progress: 82,
135
- busy: true,
136
- blocking: true,
137
- tone: 'info',
138
- },
139
- 'waiting-for-app': {
140
- visible: true,
141
- mode: 'boot',
142
- badge: 'BOOT',
143
- title: BOOT_TITLE,
144
- phase: 'Waiting for the app root view',
145
- progress: 94,
146
- busy: true,
147
- blocking: true,
148
- tone: 'info',
149
- },
150
- 'app-root-committed': {
151
- visible: true,
152
- mode: 'boot',
153
- badge: 'READY',
154
- title: BOOT_TITLE,
155
- phase: 'Real app root committed',
156
- progress: 100,
157
- busy: false,
158
- blocking: true,
159
- tone: 'info',
160
- },
161
- ready: {
162
- visible: true,
163
- mode: 'boot',
164
- badge: 'READY',
165
- title: BOOT_TITLE,
166
- phase: 'Boot complete',
167
- progress: 100,
168
- busy: false,
169
- blocking: true,
170
- tone: 'info',
171
- },
172
- error: {
173
- visible: true,
174
- mode: 'boot',
175
- badge: 'RETRY',
176
- title: BOOT_TITLE,
177
- phase: 'Retrying after a boot failure',
178
- progress: null,
179
- busy: true,
180
- blocking: true,
181
- tone: 'error',
182
- },
183
- };
184
- const base = phaseInfo[stage];
185
- let detail = info?.detail || '';
186
- if (!detail && stage === 'probing-origin' && info?.origin) {
187
- detail = `Trying ${info.origin}`;
188
- }
189
- if (attemptText) {
190
- detail = detail ? `${detail} ${attemptText}` : attemptText;
191
- }
192
- return {
193
- ...base,
194
- detail,
195
- progress: typeof info?.progress === 'number' || info?.progress === null ? info.progress : base.progress,
196
- };
197
- }
198
- export function createConnectionOverlaySnapshot(stage, info) {
199
- if (stage === 'healthy') {
200
- return { ...DEFAULT_SNAPSHOT };
201
- }
202
- const phaseInfo = {
203
- connecting: {
204
- visible: true,
205
- mode: 'connection',
206
- badge: 'SOCKET',
207
- title: 'Waiting for Vite dev server',
208
- phase: 'Opening websocket connection',
209
- detail: 'Live updates are paused until the connection is healthy.',
210
- progress: null,
211
- busy: true,
212
- blocking: false,
213
- tone: 'warn',
214
- },
215
- reconnecting: {
216
- visible: true,
217
- mode: 'connection',
218
- badge: 'SOCKET',
219
- title: 'HMR connection lost',
220
- phase: 'Reconnecting Vite websocket',
221
- detail: 'The app may be stale until the dev server reconnects.',
222
- progress: null,
223
- busy: true,
224
- blocking: false,
225
- tone: 'warn',
226
- },
227
- synchronizing: {
228
- visible: true,
229
- mode: 'connection',
230
- badge: 'SYNC',
231
- title: 'HMR connection restored',
232
- phase: 'Synchronizing live-update state',
233
- detail: 'Finalizing the module graph before dismissing the overlay.',
234
- progress: 95,
235
- busy: true,
236
- blocking: false,
237
- tone: 'info',
238
- },
239
- offline: {
240
- visible: true,
241
- mode: 'connection',
242
- badge: 'OFFLINE',
243
- title: 'Vite dev server offline',
244
- phase: 'Idle...',
245
- detail: 'The websocket and HTTP HMR path are both unavailable right now.',
246
- progress: null,
247
- busy: true,
248
- blocking: false,
249
- tone: 'error',
250
- },
251
- };
252
- const base = phaseInfo[stage];
253
- return {
254
- ...base,
255
- detail: info?.detail || base.detail,
256
- progress: typeof info?.progress === 'number' || info?.progress === null ? info.progress : base.progress,
257
- };
54
+ const state = g.__NS_HMR_DEV_OVERLAY_STATE__;
55
+ // Backfill newer fields for legacy state objects (e.g. after hot reload)
56
+ // so we never observe an undefined iosRefs/iosBuildFailed at runtime.
57
+ if (typeof state.iosRefs === 'undefined')
58
+ state.iosRefs = null;
59
+ if (typeof state.iosBuildFailed === 'undefined')
60
+ state.iosBuildFailed = false;
61
+ if (typeof state.updateAutoHideTimer === 'undefined')
62
+ state.updateAutoHideTimer = null;
63
+ if (typeof state.updateCycleStartedAt !== 'number')
64
+ state.updateCycleStartedAt = 0;
65
+ return state;
258
66
  }
259
67
  function resolveCoreExport(name) {
260
68
  const g = getOverlayGlobal();
@@ -435,10 +243,28 @@ function findBootStatusLabel() {
435
243
  catch { }
436
244
  return null;
437
245
  }
246
+ function findBootDetailLabel() {
247
+ const g = getOverlayGlobal();
248
+ return g.__NS_DEV_BOOT_DETAIL_LABEL__ || null;
249
+ }
250
+ function findBootProgressFill() {
251
+ const g = getOverlayGlobal();
252
+ return g.__NS_DEV_BOOT_PROGRESS_FILL__ || null;
253
+ }
438
254
  function updateBootStatusLabel(snapshot) {
439
- const newText = formatStatusText(snapshot) || 'Preparing the HTTP HMR bootstrap (4%)';
440
255
  const statusLabel = findBootStatusLabel();
256
+ const detailLabel = findBootDetailLabel();
257
+ const progressFill = findBootProgressFill();
441
258
  const activityIndicator = findBootActivityIndicator();
259
+ // New (card) layout: phase line + detail line live in separate
260
+ // labels so the typography can differ. Legacy (single-label)
261
+ // layout: keep the original combined "phase (X%)\ndetail" text so
262
+ // nothing visually regresses for runtimes still attached to the
263
+ // older placeholder shape.
264
+ const hasSplitLabels = !!detailLabel;
265
+ const phaseLine = formatBootPrimaryLine(snapshot);
266
+ const detailLine = formatBootDetailLine(snapshot);
267
+ const combinedText = formatStatusText(snapshot) || 'Preparing the HTTP HMR bootstrap (4%)';
442
268
  if (!statusLabel) {
443
269
  if (activityIndicator) {
444
270
  try {
@@ -447,11 +273,16 @@ function updateBootStatusLabel(snapshot) {
447
273
  }
448
274
  catch { }
449
275
  }
276
+ applyBootProgressFill(progressFill, snapshot);
450
277
  return;
451
278
  }
452
279
  try {
453
- statusLabel.text = newText;
454
- statusLabel.color = asColor(snapshot.tone === 'error' ? '#b41810e6' : '#563e3fb1');
280
+ statusLabel.text = hasSplitLabels ? phaseLine || 'Preparing the HTTP HMR bootstrap' : combinedText;
281
+ // Card layout uses the calibrated phase-text colour from the
282
+ // palette; legacy single-label layout keeps the original muted
283
+ // brown so we don't visually regress mid-session.
284
+ const phaseColorHex = snapshot.tone === 'error' ? '#B91C1C' : hasSplitLabels ? '#475569' : '#563e3fb1';
285
+ statusLabel.color = asColor(phaseColorHex);
455
286
  if (typeof statusLabel.requestLayout === 'function') {
456
287
  statusLabel.requestLayout();
457
288
  }
@@ -461,6 +292,15 @@ function updateBootStatusLabel(snapshot) {
461
292
  }
462
293
  }
463
294
  catch { }
295
+ if (detailLabel) {
296
+ try {
297
+ detailLabel.text = detailLine;
298
+ detailLabel.color = asColor(snapshot.tone === 'error' ? '#DC2626' : '#94A3B8');
299
+ detailLabel.visibility = detailLine ? 'visible' : 'collapse';
300
+ }
301
+ catch { }
302
+ }
303
+ applyBootProgressFill(progressFill, snapshot);
464
304
  if (activityIndicator) {
465
305
  try {
466
306
  activityIndicator.busy = !!snapshot.busy;
@@ -469,6 +309,50 @@ function updateBootStatusLabel(snapshot) {
469
309
  catch { }
470
310
  }
471
311
  }
312
+ // Drive the progress fill scaleX from the snapshot. Uses NS's view
313
+ // animate API for a smooth 220 ms easeOut between heartbeat ticks; a
314
+ // monotonic ratchet on `globalThis.__NS_DEV_BOOT_PROGRESS_LAST_SCALE__`
315
+ // guards against the fill snapping backwards if a less-progressed
316
+ // snapshot ever lands between ticks (mirrors the JS-side
317
+ // `applyMonotonicBootProgress` contract).
318
+ function applyBootProgressFill(progressFill, snapshot) {
319
+ if (!progressFill)
320
+ return;
321
+ const g = getOverlayGlobal();
322
+ const isError = snapshot.tone === 'error';
323
+ progressFill.backgroundColor = asColor(isError ? '#B41810' : '#3B6FE5');
324
+ const targetScale = computeBootProgressFillScale(snapshot.progress ?? null);
325
+ const previousRaw = Number(g.__NS_DEV_BOOT_PROGRESS_LAST_SCALE__);
326
+ const previous = Number.isFinite(previousRaw) ? previousRaw : 0;
327
+ const next = Math.max(previous, targetScale);
328
+ g.__NS_DEV_BOOT_PROGRESS_LAST_SCALE__ = next;
329
+ try {
330
+ // NS view.animate scales around `originX`/`originY`; the
331
+ // placeholder builder pins `originX = 0` so the fill grows
332
+ // rightward. animate() may be unavailable in some headless
333
+ // test environments — fall through to a direct property set.
334
+ if (typeof progressFill.animate === 'function') {
335
+ progressFill
336
+ .animate({
337
+ scale: { x: next, y: 1 },
338
+ duration: BOOT_PLACEHOLDER_MOTION.progressDurationMs,
339
+ curve: 'easeOut',
340
+ })
341
+ .catch(() => {
342
+ try {
343
+ progressFill.scaleX = next;
344
+ }
345
+ catch { }
346
+ });
347
+ }
348
+ else {
349
+ progressFill.scaleX = next;
350
+ }
351
+ }
352
+ catch {
353
+ progressFill.scaleX = next;
354
+ }
355
+ }
472
356
  function resolveActivePage() {
473
357
  try {
474
358
  const Frame = resolveCoreExport('Frame');
@@ -505,8 +389,18 @@ function buildLiveOverlayView(snapshot) {
505
389
  overlay.height = '100%';
506
390
  overlay.horizontalAlignment = 'stretch';
507
391
  overlay.verticalAlignment = 'stretch';
392
+ // Toast mode lets touches reach the underlying app. We flip
393
+ // isUserInteractionEnabled in applySnapshotToLiveRefs based on
394
+ // the resolved position, but keep it false here as a safe default
395
+ // (the panel itself is purely informational).
396
+ try {
397
+ overlay.isUserInteractionEnabled = false;
398
+ }
399
+ catch { }
508
400
  const panel = new StackLayout();
509
401
  panel.horizontalAlignment = 'center';
402
+ // Vertical alignment is overridden in applySnapshotToLiveRefs
403
+ // based on getHmrDevOverlayPosition(); 'middle' is the default
510
404
  panel.verticalAlignment = 'middle';
511
405
  panel.width = 320;
512
406
  panel.margin = 24;
@@ -528,6 +422,8 @@ function buildLiveOverlayView(snapshot) {
528
422
  overlay,
529
423
  titleLabel,
530
424
  statusLabel,
425
+ wasVisible: false,
426
+ currentPosition: getHmrDevOverlayPosition(),
531
427
  };
532
428
  applySnapshotToLiveRefs(refs, snapshot);
533
429
  return refs;
@@ -584,27 +480,698 @@ function applySnapshotToLiveRefs(refs, snapshot) {
584
480
  if (!refs) {
585
481
  return;
586
482
  }
587
- const visible = snapshot.visible && snapshot.mode === 'connection';
588
- refs.overlay.visibility = visible ? 'visible' : 'collapse';
589
- refs.overlay.backgroundColor = asColor(snapshot.tone === 'error' ? '#b4181068' : '#a1771683');
483
+ // 'update' mode shares the live (in-tree) overlay chrome with
484
+ // 'connection'. Both render a small panel inside the page; only
485
+ // the colours, text, and (now) panel position change with the
486
+ // snapshot's tone and the configured overlay position.
487
+ const visible = snapshot.visible && (snapshot.mode === 'connection' || snapshot.mode === 'update');
488
+ const wasVisible = !!refs.wasVisible;
489
+ const position = getHmrDevOverlayPosition();
490
+ const previousPosition = refs.currentPosition || position;
491
+ const isToast = position !== 'center';
590
492
  refs.titleLabel.text = snapshot.title;
591
- refs.titleLabel.color = asColor(snapshot.tone === 'error' ? '#b41810e6' : '#563e3fb1');
592
493
  refs.statusLabel.text = formatStatusText(snapshot);
593
- refs.statusLabel.color = asColor(snapshot.tone === 'error' ? '#b41810e6' : '#563e3fb1');
494
+ const textColor = snapshot.tone === 'error' ? '#b41810e6' : snapshot.tone === 'success' ? '#0e6e2fff' : '#563e3fb1';
495
+ refs.titleLabel.color = asColor(textColor);
496
+ refs.statusLabel.color = asColor(textColor);
497
+ // Backdrop tints (centered modal only). Toast modes use a fully
498
+ // transparent backdrop so the rest of the app stays visible AND
499
+ // reachable; the panel itself carries enough colour to stand out.
500
+ if (isToast) {
501
+ refs.overlay.backgroundColor = asColor('transparent');
502
+ }
503
+ else {
504
+ // Original wash-by-tone for centered:
505
+ // error → red wash (matches existing UX)
506
+ // success → richer green wash so the apply event is visible
507
+ // on bright app backgrounds
508
+ // default → warm orange (existing connection-overlay look)
509
+ const overlayBg = snapshot.tone === 'error' ? '#b4181068' : snapshot.tone === 'success' ? '#1f883d80' : '#a1771683';
510
+ refs.overlay.backgroundColor = asColor(overlayBg);
511
+ }
512
+ // Panel chrome — toast and centered share the same chip look,
513
+ // just position differs. We keep the slightly richer green tint
514
+ // for the HMR success state so it pops without needing the
515
+ // backdrop wash.
516
+ let panel = null;
594
517
  try {
595
- const panel = refs.titleLabel.parent;
518
+ panel = refs.titleLabel.parent;
596
519
  if (panel) {
597
- panel.backgroundColor = asColor('#FFFFFFFF');
520
+ const panelBg = snapshot.tone === 'success' ? '#E6F8E9FF' : '#FFFFFFFF';
521
+ panel.backgroundColor = asColor(panelBg);
598
522
  panel.opacity = 1;
599
523
  panel.padding = 16;
600
524
  try {
601
525
  panel.borderRadius = 12;
602
526
  }
603
527
  catch { }
528
+ // Position-aware alignment. The wrapper GridLayout fills
529
+ // the page content area, which on iOS is already inside
530
+ // the safe area; we add a small extra margin so the chip
531
+ // doesn't kiss the notch / home indicator.
532
+ try {
533
+ if (position === 'top') {
534
+ panel.verticalAlignment = 'top';
535
+ panel.margin = '12 16 0 16';
536
+ }
537
+ else if (position === 'bottom') {
538
+ panel.verticalAlignment = 'bottom';
539
+ panel.margin = '0 16 12 16';
540
+ }
541
+ else {
542
+ panel.verticalAlignment = 'middle';
543
+ panel.margin = 24;
544
+ }
545
+ }
546
+ catch { }
547
+ }
548
+ }
549
+ catch { }
550
+ // Touch passthrough for toast; centered mode keeps the
551
+ // blocking modal so the dim backdrop is meaningful.
552
+ try {
553
+ refs.overlay.isUserInteractionEnabled = !isToast;
554
+ }
555
+ catch { }
556
+ const positionChanged = previousPosition !== position;
557
+ const justAppeared = visible && (!wasVisible || positionChanged);
558
+ const justDismissed = !visible && wasVisible;
559
+ if (justAppeared) {
560
+ refs.overlay.visibility = 'visible';
561
+ if (isToast && panel && typeof panel.animate === 'function') {
562
+ animateLivePanelIn(panel, position);
563
+ }
564
+ else if (panel) {
565
+ try {
566
+ panel.translateY = 0;
567
+ panel.opacity = 1;
568
+ }
569
+ catch { }
570
+ }
571
+ }
572
+ else if (justDismissed) {
573
+ if (isToast && panel && typeof panel.animate === 'function') {
574
+ animateLivePanelOut(panel, previousPosition, () => {
575
+ try {
576
+ refs.overlay.visibility = 'collapse';
577
+ }
578
+ catch { }
579
+ });
580
+ }
581
+ else {
582
+ refs.overlay.visibility = 'collapse';
583
+ }
584
+ }
585
+ else {
586
+ refs.overlay.visibility = visible ? 'visible' : 'collapse';
587
+ }
588
+ if (typeof refs.wasVisible !== 'undefined')
589
+ refs.wasVisible = visible;
590
+ if (typeof refs.currentPosition !== 'undefined')
591
+ refs.currentPosition = position;
592
+ }
593
+ /**
594
+ * Slide-in animation for the in-tree toast panel.
595
+ *
596
+ * NativeScript's `View.animate({ translate, opacity, duration, curve })`
597
+ * is widely available across Core versions, so we don't depend on any
598
+ * specific curve enum being importable here. We use a moderate-to-snappy
599
+ * 320ms ease-out which feels close to a UIView spring without needing
600
+ * platform-specific APIs.
601
+ */
602
+ function animateLivePanelIn(panel, position) {
603
+ if (!panel || typeof panel.animate !== 'function')
604
+ return;
605
+ try {
606
+ const startY = position === 'bottom' ? 80 : -80;
607
+ panel.translateY = startY;
608
+ panel.opacity = 0;
609
+ const result = panel.animate({
610
+ translate: { x: 0, y: 0 },
611
+ opacity: 1,
612
+ duration: 320,
613
+ curve: 'easeOut',
614
+ });
615
+ if (result && typeof result.catch === 'function') {
616
+ result.catch(() => {
617
+ try {
618
+ panel.translateY = 0;
619
+ panel.opacity = 1;
620
+ }
621
+ catch { }
622
+ });
623
+ }
624
+ }
625
+ catch {
626
+ try {
627
+ panel.translateY = 0;
628
+ panel.opacity = 1;
629
+ }
630
+ catch { }
631
+ }
632
+ }
633
+ function animateLivePanelOut(panel, position, onComplete) {
634
+ if (!panel || typeof panel.animate !== 'function') {
635
+ onComplete();
636
+ return;
637
+ }
638
+ try {
639
+ const targetY = position === 'bottom' ? 80 : -80;
640
+ const result = panel.animate({
641
+ translate: { x: 0, y: targetY },
642
+ opacity: 0,
643
+ duration: 220,
644
+ curve: 'easeIn',
645
+ });
646
+ const finish = () => {
647
+ try {
648
+ panel.translateY = 0;
649
+ panel.opacity = 1;
650
+ }
651
+ catch { }
652
+ onComplete();
653
+ };
654
+ if (result && typeof result.then === 'function') {
655
+ result.then(finish, finish);
656
+ }
657
+ else {
658
+ finish();
659
+ }
660
+ }
661
+ catch {
662
+ onComplete();
663
+ }
664
+ }
665
+ // pure helpers for iOS window promotion. Factored out so the layout
666
+ // math and window-level selection stay unit-testable without booting a
667
+ // simulator. See `dev-overlay.spec.ts`.
668
+ /**
669
+ * Returns the UIWindow level we use for the live/connection overlay. We lift
670
+ * above `UIWindowLevelAlert` so system alerts (and any app-presented modal)
671
+ * stack underneath. When the platform does not expose `UIWindowLevelAlert`
672
+ * we fall back to the documented constant value (2000).
673
+ */
674
+ export function computeIosOverlayWindowLevel(baseAlert) {
675
+ if (typeof baseAlert === 'number' && Number.isFinite(baseAlert)) {
676
+ return baseAlert + 1;
677
+ }
678
+ return 2000 + 1;
679
+ }
680
+ /**
681
+ * Layout math for the live overlay when it runs inside its own UIWindow.
682
+ * Pure, deterministic and independent of UIKit so we can verify the rules
683
+ * (max panel width, position-aware placement, safe-area clamping, sane
684
+ * defaults) from tests.
685
+ *
686
+ * `position` controls where the panel sits vertically:
687
+ * - 'top': hugs `safeInsets.top + toastVerticalInset` so the chip
688
+ * sits just below the notch / Dynamic Island.
689
+ * - 'bottom': hugs `viewHeight - safeInsets.bottom - panelHeight -
690
+ * toastVerticalInset` so the chip sits just above the
691
+ * home indicator / nav bar.
692
+ * - 'center': original modal placement (vertically centered, clamped
693
+ * so it never crosses the top safe-area inset).
694
+ */
695
+ export function computeIosOverlayLayout(input) {
696
+ const viewWidth = Math.max(0, Number(input.viewWidth) || 0);
697
+ const viewHeight = Math.max(0, Number(input.viewHeight) || 0);
698
+ const safeInsets = {
699
+ top: Math.max(0, Number(input.safeInsets?.top ?? 0) || 0),
700
+ bottom: Math.max(0, Number(input.safeInsets?.bottom ?? 0) || 0),
701
+ left: Math.max(0, Number(input.safeInsets?.left ?? 0) || 0),
702
+ right: Math.max(0, Number(input.safeInsets?.right ?? 0) || 0),
703
+ };
704
+ const titleHeight = Math.max(0, Number(input.titleHeight) || 0);
705
+ const statusHeight = Math.max(0, Number(input.statusHeight) || 0);
706
+ const horizontalMargin = Math.max(0, Number(input.horizontalMargin ?? 24));
707
+ const maxPanelWidth = Math.max(0, Number(input.maxPanelWidth ?? 340));
708
+ const panelPadding = Math.max(0, Number(input.panelPadding ?? 16));
709
+ const interLabelSpacing = Math.max(0, Number(input.interLabelSpacing ?? 10));
710
+ const minTopInset = Math.max(0, Number(input.minTopInset ?? 20));
711
+ // Default to 'center' on the pure function so the existing
712
+ // snapshot/layout tests remain stable; the runtime call site
713
+ // (layoutIosOverlayRefs) reads the configured position from
714
+ // `getHmrDevOverlayPosition()` and forwards it explicitly.
715
+ const position = input.position ?? 'center';
716
+ // Distance between the panel and the safe-area edge in toast
717
+ // modes. 8pt mirrors the typical iOS notification chip inset and
718
+ // keeps the chip from hugging the notch / home indicator.
719
+ const toastVerticalInset = Math.max(0, Number(input.toastVerticalInset ?? 8));
720
+ const available = Math.max(0, viewWidth - 2 * horizontalMargin - safeInsets.left - safeInsets.right);
721
+ const panelWidth = Math.min(maxPanelWidth, available);
722
+ const innerWidth = Math.max(0, panelWidth - 2 * panelPadding);
723
+ const spacing = titleHeight > 0 && statusHeight > 0 ? interLabelSpacing : 0;
724
+ const panelHeight = panelPadding * 2 + titleHeight + spacing + statusHeight;
725
+ const panelX = Math.max(0, (viewWidth - panelWidth) / 2);
726
+ let panelY;
727
+ if (position === 'top') {
728
+ // Pin to the top safe-area inset (just below notch / Dynamic
729
+ // Island). Clamp non-negative for fully-NaN input.
730
+ panelY = Math.max(0, safeInsets.top + toastVerticalInset);
731
+ }
732
+ else if (position === 'bottom') {
733
+ // Pin to the bottom safe-area inset (just above home indicator
734
+ // / nav bar). If the panel can't fit between the safe-area
735
+ // insets we fall back to the top safe-area edge so the chip is
736
+ // always visible (rather than getting clipped off-screen).
737
+ const desired = viewHeight - safeInsets.bottom - panelHeight - toastVerticalInset;
738
+ panelY = Math.max(safeInsets.top + minTopInset, desired);
739
+ }
740
+ else {
741
+ // Center vertically, but never cross the top safe-area inset
742
+ // (notch/Dynamic Island). Original modal placement.
743
+ const centered = (viewHeight - panelHeight) / 2;
744
+ panelY = Math.max(safeInsets.top + minTopInset, centered);
745
+ }
746
+ return {
747
+ backdrop: { x: 0, y: 0, width: viewWidth, height: viewHeight },
748
+ panel: { x: panelX, y: panelY, width: panelWidth, height: panelHeight },
749
+ title: { x: panelPadding, y: panelPadding, width: innerWidth, height: titleHeight },
750
+ status: {
751
+ x: panelPadding,
752
+ y: panelPadding + titleHeight + spacing,
753
+ width: innerWidth,
754
+ height: statusHeight,
755
+ },
756
+ };
757
+ }
758
+ /**
759
+ * Returns the iOS UIKit symbols we rely on if we're running on an iOS runtime
760
+ * with the metadata bridge available. Returns null on Android, web, or in
761
+ * tests so callers can gracefully fall back to the in-tree overlay.
762
+ */
763
+ function getIosOverlayHost() {
764
+ const g = getOverlayGlobal();
765
+ 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') {
766
+ return null;
767
+ }
768
+ return {
769
+ UIWindow: g.UIWindow,
770
+ UIViewController: g.UIViewController,
771
+ UIView: g.UIView,
772
+ UILabel: g.UILabel,
773
+ UIColor: g.UIColor,
774
+ UIFont: g.UIFont,
775
+ UIApplication: g.UIApplication,
776
+ UIScreen: g.UIScreen,
777
+ UIWindowLevelAlert: typeof g.UIWindowLevelAlert === 'number' ? g.UIWindowLevelAlert : undefined,
778
+ };
779
+ }
780
+ /**
781
+ * Walks UIApplication.sharedApplication windows and returns the first active
782
+ * UIWindowScene we can locate. On iOS 13+ every UIWindow is attached to a
783
+ * scene, and we must initialise our overlay window the same way or the OS
784
+ * will silently refuse to render it. Returns null when no scene is found
785
+ * (older iOS versions or non-UI environments).
786
+ */
787
+ function findActiveWindowScene(host) {
788
+ try {
789
+ const app = host.UIApplication.sharedApplication;
790
+ const windows = app?.windows;
791
+ if (!windows || typeof windows.count !== 'number')
792
+ return null;
793
+ for (let i = 0; i < windows.count; i++) {
794
+ const w = windows.objectAtIndex(i);
795
+ const scene = w && w.windowScene;
796
+ if (scene)
797
+ return scene;
798
+ }
799
+ }
800
+ catch { }
801
+ return null;
802
+ }
803
+ function buildIosOverlayRefs(state) {
804
+ const host = getIosOverlayHost();
805
+ if (!host)
806
+ return null;
807
+ // Without a scene we can't build a modern UIWindow that actually renders.
808
+ // Fall back to the in-tree overlay rather than show nothing.
809
+ const scene = findActiveWindowScene(host);
810
+ if (!scene) {
811
+ if (state.verbose) {
812
+ console.info('[ns-hmr-overlay] no active UIWindowScene; skipping iOS overlay promotion');
813
+ }
814
+ return null;
815
+ }
816
+ try {
817
+ const { UIWindow, UIViewController, UIView, UILabel, UIColor, UIFont } = host;
818
+ const window = UIWindow.alloc().initWithWindowScene(scene);
819
+ window.windowLevel = computeIosOverlayWindowLevel(host.UIWindowLevelAlert ?? null);
820
+ window.backgroundColor = UIColor.clearColor;
821
+ window.hidden = true;
822
+ const controller = UIViewController.new();
823
+ controller.view.backgroundColor = UIColor.clearColor;
824
+ window.rootViewController = controller;
825
+ // UIViewAutoresizing bit masks. We mirror the UIKit constants here to
826
+ // avoid depending on symbols the metadata bridge does not always
827
+ // expose as top-level globals.
828
+ const FLEXIBLE_LEFT_MARGIN = 1 << 0;
829
+ const FLEXIBLE_WIDTH = 1 << 1;
830
+ const FLEXIBLE_RIGHT_MARGIN = 1 << 2;
831
+ const FLEXIBLE_TOP_MARGIN = 1 << 3;
832
+ const FLEXIBLE_HEIGHT = 1 << 4;
833
+ const FLEXIBLE_BOTTOM_MARGIN = 1 << 5;
834
+ const backdrop = UIView.new();
835
+ backdrop.backgroundColor = UIColor.colorWithRedGreenBlueAlpha(0, 0, 0, 0.35);
836
+ backdrop.autoresizingMask = FLEXIBLE_WIDTH | FLEXIBLE_HEIGHT;
837
+ controller.view.addSubview(backdrop);
838
+ const panel = UIView.new();
839
+ panel.backgroundColor = UIColor.whiteColor;
840
+ panel.autoresizingMask = FLEXIBLE_LEFT_MARGIN | FLEXIBLE_RIGHT_MARGIN | FLEXIBLE_TOP_MARGIN | FLEXIBLE_BOTTOM_MARGIN;
841
+ try {
842
+ panel.layer.cornerRadius = 14;
843
+ panel.layer.masksToBounds = true;
844
+ }
845
+ catch { }
846
+ controller.view.addSubview(panel);
847
+ const titleLabel = UILabel.new();
848
+ titleLabel.numberOfLines = 0;
849
+ titleLabel.textAlignment = 1; // NSTextAlignmentCenter
850
+ titleLabel.font = UIFont.boldSystemFontOfSize(16);
851
+ titleLabel.textColor = UIColor.blackColor;
852
+ panel.addSubview(titleLabel);
853
+ const statusLabel = UILabel.new();
854
+ statusLabel.numberOfLines = 0;
855
+ statusLabel.textAlignment = 1;
856
+ statusLabel.font = UIFont.systemFontOfSize(13);
857
+ statusLabel.textColor = UIColor.darkGrayColor;
858
+ panel.addSubview(statusLabel);
859
+ // Subtle drop-shadow so the toast chip reads against light app
860
+ // content (white-on-white is invisible). The error / centered
861
+ // branches still get the dim backdrop, so the shadow is mostly
862
+ // a no-op for them — but it's a one-time setup.
863
+ try {
864
+ panel.layer.shadowColor = UIColor.blackColor.CGColor;
865
+ panel.layer.shadowOpacity = 0.18;
866
+ panel.layer.shadowRadius = 8;
867
+ panel.layer.shadowOffset = { width: 0, height: 2 };
868
+ panel.layer.masksToBounds = false;
869
+ }
870
+ catch { }
871
+ // `wasVisible` / `currentPosition` are mutated by
872
+ // applySnapshotToIosRefs when the snapshot triggers a slide-in
873
+ // or slide-out. They start in the "hidden" state so the very
874
+ // first visible snapshot animates in cleanly.
875
+ return {
876
+ window,
877
+ controller,
878
+ backdrop,
879
+ panel,
880
+ titleLabel,
881
+ statusLabel,
882
+ wasVisible: false,
883
+ currentPosition: getHmrDevOverlayPosition(),
884
+ };
885
+ }
886
+ catch (err) {
887
+ console.warn('[ns-hmr-overlay] iOS overlay construction failed:', err?.message || err);
888
+ return null;
889
+ }
890
+ }
891
+ function ensureIosOverlayRefs(state) {
892
+ if (state.iosRefs)
893
+ return state.iosRefs;
894
+ if (state.iosBuildFailed)
895
+ return null;
896
+ const built = buildIosOverlayRefs(state);
897
+ if (built) {
898
+ state.iosRefs = built;
899
+ }
900
+ else {
901
+ // Remember failure so we don't hammer construction on every snapshot
902
+ // update — the in-tree path will take over for this session.
903
+ state.iosBuildFailed = true;
904
+ }
905
+ return state.iosRefs;
906
+ }
907
+ function layoutIosOverlayRefs(refs, position) {
908
+ try {
909
+ const bounds = refs.controller.view.bounds;
910
+ const viewWidth = Number(bounds?.size?.width) || 0;
911
+ const viewHeight = Number(bounds?.size?.height) || 0;
912
+ const raw = refs.controller.view.safeAreaInsets;
913
+ const safeInsets = raw
914
+ ? {
915
+ top: Number(raw.top) || 0,
916
+ bottom: Number(raw.bottom) || 0,
917
+ left: Number(raw.left) || 0,
918
+ right: Number(raw.right) || 0,
919
+ }
920
+ : { top: 0, bottom: 0, left: 0, right: 0 };
921
+ // Ask UIKit what the labels want given the panel inner width. We use a
922
+ // generous height bound so nothing clips on long reconnect strings.
923
+ const panelPadding = 16;
924
+ const horizontalMargin = 24;
925
+ const maxPanelWidth = 340;
926
+ const innerWidth = Math.max(0, Math.min(maxPanelWidth, viewWidth - 2 * horizontalMargin - safeInsets.left - safeInsets.right) - 2 * panelPadding);
927
+ const titleFit = refs.titleLabel.sizeThatFits({ width: innerWidth, height: 10000 }) || { height: 0 };
928
+ const statusFit = refs.statusLabel.sizeThatFits({ width: innerWidth, height: 10000 }) || { height: 0 };
929
+ const layout = computeIosOverlayLayout({
930
+ viewWidth,
931
+ viewHeight,
932
+ safeInsets,
933
+ titleHeight: Number(titleFit.height) || 0,
934
+ statusHeight: Number(statusFit.height) || 0,
935
+ maxPanelWidth,
936
+ horizontalMargin,
937
+ panelPadding,
938
+ position,
939
+ });
940
+ const toCgRect = (rect) => ({
941
+ origin: { x: rect.x, y: rect.y },
942
+ size: { width: rect.width, height: rect.height },
943
+ });
944
+ refs.backdrop.frame = toCgRect(layout.backdrop);
945
+ refs.panel.frame = toCgRect(layout.panel);
946
+ refs.titleLabel.frame = toCgRect(layout.title);
947
+ refs.statusLabel.frame = toCgRect(layout.status);
948
+ return layout;
949
+ }
950
+ catch (err) {
951
+ console.warn('[ns-hmr-overlay] iOS overlay layout failed:', err?.message || err);
952
+ return null;
953
+ }
954
+ }
955
+ /**
956
+ * Slide-in animation for the iOS toast panel. Off-screen start frame
957
+ * lives just above (top) or below (bottom) the visible area; the panel
958
+ * snaps to its target frame with a spring so the motion feels physical
959
+ * without the heavy "settle" overshoot of a hard spring (damping 0.85
960
+ * lands quickly with a small overshoot).
961
+ */
962
+ function animateIosPanelIn(refs, position, layout) {
963
+ const g = getOverlayGlobal();
964
+ const UIView = g?.UIView;
965
+ if (!UIView)
966
+ return;
967
+ try {
968
+ const targetFrame = {
969
+ origin: { x: layout.panel.x, y: layout.panel.y },
970
+ size: { width: layout.panel.width, height: layout.panel.height },
971
+ };
972
+ // Off-screen start: distance includes a small fudge so the
973
+ // shadow blur tail isn't visible at t=0.
974
+ const startY = position === 'bottom' ? layout.backdrop.height + 24 : -(layout.panel.height + 24);
975
+ refs.panel.frame = {
976
+ origin: { x: layout.panel.x, y: startY },
977
+ size: { width: layout.panel.width, height: layout.panel.height },
978
+ };
979
+ refs.panel.alpha = 0;
980
+ try {
981
+ if (typeof UIView.animateWithDurationDelayUsingSpringWithDampingInitialSpringVelocityOptionsAnimationsCompletion === 'function') {
982
+ UIView.animateWithDurationDelayUsingSpringWithDampingInitialSpringVelocityOptionsAnimationsCompletion(0.42, 0, 0.85, 0.7, 0, () => {
983
+ refs.panel.frame = targetFrame;
984
+ refs.panel.alpha = 1;
985
+ }, null);
986
+ }
987
+ else if (typeof UIView.animateWithDurationAnimations === 'function') {
988
+ UIView.animateWithDurationAnimations(0.32, () => {
989
+ refs.panel.frame = targetFrame;
990
+ refs.panel.alpha = 1;
991
+ });
992
+ }
993
+ else {
994
+ refs.panel.frame = targetFrame;
995
+ refs.panel.alpha = 1;
996
+ }
997
+ }
998
+ catch {
999
+ refs.panel.frame = targetFrame;
1000
+ refs.panel.alpha = 1;
604
1001
  }
605
1002
  }
606
1003
  catch { }
607
1004
  }
1005
+ /**
1006
+ * Slide-out animation for the iOS toast panel. Mirrors animateIosPanelIn:
1007
+ * the panel travels to the nearest off-screen edge while fading out so
1008
+ * the dismissal still feels intentional even on fast HMR cycles.
1009
+ */
1010
+ function animateIosPanelOut(refs, position, onComplete) {
1011
+ const g = getOverlayGlobal();
1012
+ const UIView = g?.UIView;
1013
+ const currentFrame = refs.panel?.frame;
1014
+ if (!UIView || !currentFrame) {
1015
+ onComplete();
1016
+ return;
1017
+ }
1018
+ try {
1019
+ const bounds = refs.controller?.view?.bounds;
1020
+ const viewHeight = Number(bounds?.size?.height) || 0;
1021
+ const targetY = position === 'bottom' ? viewHeight + 24 : -(Number(currentFrame.size?.height) + 24);
1022
+ const startFrame = currentFrame;
1023
+ const targetFrame = {
1024
+ origin: { x: Number(startFrame.origin?.x) || 0, y: targetY },
1025
+ size: startFrame.size,
1026
+ };
1027
+ try {
1028
+ if (typeof UIView.animateWithDurationDelayOptionsAnimationsCompletion === 'function') {
1029
+ // UIViewAnimationOptionCurveEaseIn = 1 << 16 — accelerate
1030
+ // out so the dismissal doesn't drag on screen.
1031
+ UIView.animateWithDurationDelayOptionsAnimationsCompletion(0.22, 0, 1 << 16, () => {
1032
+ refs.panel.frame = targetFrame;
1033
+ refs.panel.alpha = 0;
1034
+ }, () => onComplete());
1035
+ }
1036
+ else if (typeof UIView.animateWithDurationAnimationsCompletion === 'function') {
1037
+ UIView.animateWithDurationAnimationsCompletion(0.22, () => {
1038
+ refs.panel.frame = targetFrame;
1039
+ refs.panel.alpha = 0;
1040
+ }, () => onComplete());
1041
+ }
1042
+ else {
1043
+ refs.panel.alpha = 0;
1044
+ onComplete();
1045
+ }
1046
+ }
1047
+ catch {
1048
+ refs.panel.alpha = 0;
1049
+ onComplete();
1050
+ }
1051
+ }
1052
+ catch {
1053
+ onComplete();
1054
+ }
1055
+ }
1056
+ function applySnapshotToIosRefs(refs, snapshot) {
1057
+ if (!refs)
1058
+ return false;
1059
+ try {
1060
+ // 'update' mode rides the same dedicated UIWindow as
1061
+ // 'connection' so the HMR apply overlay always stacks above
1062
+ // modals/sheets/system alerts. The window is constructed
1063
+ // lazily (ensureIosOverlayRefs) and reused for the lifetime of
1064
+ // the dev session.
1065
+ const visible = snapshot.visible && (snapshot.mode === 'connection' || snapshot.mode === 'update');
1066
+ const wasVisible = !!refs.wasVisible;
1067
+ const position = getHmrDevOverlayPosition();
1068
+ const previousPosition = refs.currentPosition;
1069
+ const isToast = position !== 'center';
1070
+ // Touches pass through the overlay window in toast mode so
1071
+ // the user can keep tapping the app while the HMR chip is
1072
+ // shown. In centered mode we keep the blocking
1073
+ // behaviour (the dim backdrop is itself a hint to wait).
1074
+ try {
1075
+ refs.window.userInteractionEnabled = !isToast;
1076
+ }
1077
+ catch { }
1078
+ if (!visible) {
1079
+ // Animate out before hiding the window so the dismissal
1080
+ // has a discoverable motion. Only animate when previously
1081
+ // visible and in toast mode — centered modal hides instantly.
1082
+ if (wasVisible && isToast) {
1083
+ animateIosPanelOut(refs, previousPosition, () => {
1084
+ try {
1085
+ refs.window.hidden = true;
1086
+ }
1087
+ catch { }
1088
+ });
1089
+ }
1090
+ else {
1091
+ refs.window.hidden = true;
1092
+ }
1093
+ refs.wasVisible = false;
1094
+ refs.currentPosition = position;
1095
+ return true;
1096
+ }
1097
+ refs.window.hidden = false;
1098
+ refs.titleLabel.text = snapshot.title || '';
1099
+ refs.statusLabel.text = formatStatusText(snapshot);
1100
+ const host = getIosOverlayHost();
1101
+ if (host) {
1102
+ const { UIColor } = host;
1103
+ const isError = snapshot.tone === 'error';
1104
+ const isSuccess = snapshot.tone === 'success';
1105
+ try {
1106
+ if (isError) {
1107
+ // Red panel + dark red text (existing UX).
1108
+ refs.panel.backgroundColor = UIColor.colorWithRedGreenBlueAlpha(1, 0.96, 0.96, 1);
1109
+ refs.titleLabel.textColor = UIColor.colorWithRedGreenBlueAlpha(0.7, 0.1, 0.06, 1);
1110
+ refs.statusLabel.textColor = UIColor.colorWithRedGreenBlueAlpha(0.7, 0.1, 0.06, 0.9);
1111
+ }
1112
+ else if (isSuccess) {
1113
+ // Slightly more saturated green panel + dark-green
1114
+ // text. The previous 0.94/0.99/0.95 background was
1115
+ // nearly indistinguishable from white on most
1116
+ // devices; this bump keeps long detail strings
1117
+ // readable while making the apply event obviously
1118
+ // "happening right now".
1119
+ refs.panel.backgroundColor = UIColor.colorWithRedGreenBlueAlpha(0.9, 0.97, 0.91, 1);
1120
+ refs.titleLabel.textColor = UIColor.colorWithRedGreenBlueAlpha(0.05, 0.43, 0.18, 1);
1121
+ refs.statusLabel.textColor = UIColor.colorWithRedGreenBlueAlpha(0.05, 0.43, 0.18, 1);
1122
+ }
1123
+ else {
1124
+ // Default (info / warn) — existing connection look.
1125
+ refs.panel.backgroundColor = UIColor.whiteColor;
1126
+ refs.titleLabel.textColor = UIColor.blackColor;
1127
+ refs.statusLabel.textColor = UIColor.darkGrayColor;
1128
+ }
1129
+ // Backdrop dims only in centered mode; toast mode keeps
1130
+ // the rest of the app fully visible/usable. Errors get
1131
+ // a slightly stronger dim in centered mode because the
1132
+ // user MUST notice them.
1133
+ if (isToast) {
1134
+ refs.backdrop.backgroundColor = UIColor.clearColor;
1135
+ }
1136
+ else if (isError) {
1137
+ refs.backdrop.backgroundColor = UIColor.colorWithRedGreenBlueAlpha(0, 0, 0, 0.35);
1138
+ }
1139
+ else if (isSuccess) {
1140
+ refs.backdrop.backgroundColor = UIColor.colorWithRedGreenBlueAlpha(0, 0.15, 0.05, 0.28);
1141
+ }
1142
+ else {
1143
+ refs.backdrop.backgroundColor = UIColor.colorWithRedGreenBlueAlpha(0, 0, 0, 0.35);
1144
+ }
1145
+ }
1146
+ catch { }
1147
+ }
1148
+ const layout = layoutIosOverlayRefs(refs, position);
1149
+ // Slide-in animation only fires on the actual hidden→visible
1150
+ // transition (or on a position swap — e.g. dev toggling top
1151
+ // to bottom mid-cycle). Subsequent updates within the same
1152
+ // visible cycle just refresh text/colours without re-animating.
1153
+ const positionChanged = previousPosition !== position;
1154
+ const justAppeared = !wasVisible || positionChanged;
1155
+ if (justAppeared && isToast && layout) {
1156
+ animateIosPanelIn(refs, position, layout);
1157
+ }
1158
+ else if (justAppeared && !isToast) {
1159
+ // Centered modal: ensure alpha is reset to 1 in case a
1160
+ // previous toast-mode dismissal left it at 0.
1161
+ try {
1162
+ refs.panel.alpha = 1;
1163
+ }
1164
+ catch { }
1165
+ }
1166
+ refs.wasVisible = true;
1167
+ refs.currentPosition = position;
1168
+ return true;
1169
+ }
1170
+ catch (err) {
1171
+ console.warn('[ns-hmr-overlay] iOS overlay apply failed:', err?.message || err);
1172
+ return false;
1173
+ }
1174
+ }
608
1175
  function applyRuntimeSnapshot(snapshot) {
609
1176
  const state = getRuntimeState();
610
1177
  state.snapshot = snapshot;
@@ -613,15 +1180,108 @@ function applyRuntimeSnapshot(snapshot) {
613
1180
  updateBootStatusLabel(snapshot);
614
1181
  }
615
1182
  applySnapshotToBootRefs(state.bootRefs, snapshot);
616
- if (snapshot.visible && snapshot.mode === 'connection') {
617
- const liveRefs = ensureLiveOverlayRefs(snapshot);
618
- applySnapshotToLiveRefs(liveRefs, snapshot);
1183
+ // prefer the dedicated UIWindow
1184
+ // path so the live/update overlays always stack on top of modals,
1185
+ // sheets, and other windows. Fall back to the in-tree overlay when
1186
+ // iOS APIs aren't available (Android, tests, or when scene
1187
+ // construction fails).
1188
+ let handledByIos = false;
1189
+ // Both 'connection' and 'update' use the small-panel surface
1190
+ // (UIWindow on iOS, in-tree overlay everywhere else). 'boot' uses
1191
+ // the placeholder root via applySnapshotToBootRefs above; 'hidden'
1192
+ // hides everything.
1193
+ const wantsOverlay = snapshot.visible && (snapshot.mode === 'connection' || snapshot.mode === 'update');
1194
+ if (getIosOverlayHost()) {
1195
+ if (wantsOverlay) {
1196
+ const iosRefs = ensureIosOverlayRefs(state);
1197
+ handledByIos = applySnapshotToIosRefs(iosRefs, snapshot);
1198
+ }
1199
+ else if (state.iosRefs) {
1200
+ handledByIos = applySnapshotToIosRefs(state.iosRefs, snapshot);
1201
+ }
619
1202
  }
620
- else {
621
- applySnapshotToLiveRefs(state.liveRefs, snapshot);
1203
+ if (!handledByIos) {
1204
+ if (wantsOverlay) {
1205
+ const liveRefs = ensureLiveOverlayRefs(snapshot);
1206
+ applySnapshotToLiveRefs(liveRefs, snapshot);
1207
+ }
1208
+ else {
1209
+ applySnapshotToLiveRefs(state.liveRefs, snapshot);
1210
+ }
622
1211
  }
623
1212
  return state.snapshot;
624
1213
  }
1214
+ // How long the 'complete' frame stays on screen before we auto-hide.
1215
+ // The original 350ms was too tight: many HMR cycles complete in
1216
+ // 50–250ms, so the *total* overlay lifetime (received → complete +
1217
+ // 350ms) was often under 500ms, which is faster than the human eye
1218
+ // can comfortably register. 600ms gives the user time to read the
1219
+ // "Total Xms" line and confirm visually that something happened.
1220
+ const UPDATE_AUTO_HIDE_MS = 600;
1221
+ // Minimum perceptible duration for an entire update overlay cycle
1222
+ // (from 'received' to hide). If the cycle finished in 50ms (e.g., a
1223
+ // tiny HTML edit on a warm cache), we still hold for ~MIN_VISIBLE_MS
1224
+ // total before hiding so the overlay is actually seen. Combined with
1225
+ // UPDATE_AUTO_HIDE_MS, the *effective* hold-after-complete =
1226
+ // max(UPDATE_AUTO_HIDE_MS, MIN_VISIBLE_MS - elapsed-since-received).
1227
+ const UPDATE_MIN_VISIBLE_MS = 800;
1228
+ function clearUpdateAutoHideTimer(state) {
1229
+ if (state.updateAutoHideTimer) {
1230
+ try {
1231
+ clearTimeout(state.updateAutoHideTimer);
1232
+ }
1233
+ catch { }
1234
+ state.updateAutoHideTimer = null;
1235
+ }
1236
+ }
1237
+ function scheduleUpdateAutoHide(state) {
1238
+ clearUpdateAutoHideTimer(state);
1239
+ // Compute how much longer we need to hold the overlay so that the
1240
+ // total cycle visibility is at least UPDATE_MIN_VISIBLE_MS. For
1241
+ // fast cycles (50ms reboot) this stretches the hide; for slow
1242
+ // cycles (>UPDATE_MIN_VISIBLE_MS) it falls back to the standard
1243
+ // UPDATE_AUTO_HIDE_MS so we don't truncate the celebratory hold.
1244
+ const startedAt = state.updateCycleStartedAt || 0;
1245
+ const elapsed = startedAt > 0 ? Math.max(0, Date.now() - startedAt) : 0;
1246
+ const minRemainder = elapsed > 0 ? Math.max(0, UPDATE_MIN_VISIBLE_MS - elapsed) : UPDATE_MIN_VISIBLE_MS;
1247
+ const holdMs = Math.max(UPDATE_AUTO_HIDE_MS, minRemainder);
1248
+ try {
1249
+ state.updateAutoHideTimer = setTimeout(() => {
1250
+ state.updateAutoHideTimer = null;
1251
+ // Critical: only auto-hide if we're still on the 'complete'
1252
+ // frame. If a new HMR cycle has rotated the snapshot back
1253
+ // to 'update' / 'received' (e.g., user saved twice in
1254
+ // quick succession), the new cycle owns the overlay and
1255
+ // our timer must not steal it.
1256
+ const current = state.snapshot;
1257
+ if (current.mode === 'update' && current.tone === 'success' && current.progress === 100) {
1258
+ state.updateCycleStartedAt = 0;
1259
+ applyRuntimeSnapshot({ ...DEFAULT_SNAPSHOT });
1260
+ }
1261
+ }, holdMs);
1262
+ }
1263
+ catch {
1264
+ // setTimeout missing (extremely rare; some test envs). Fall
1265
+ // back to immediate hide so we never leave the overlay visible
1266
+ // forever after a 'complete'.
1267
+ state.updateCycleStartedAt = 0;
1268
+ applyRuntimeSnapshot({ ...DEFAULT_SNAPSHOT });
1269
+ }
1270
+ }
1271
+ function logUpdateStageTransition(state, stage, info) {
1272
+ if (!state.verbose)
1273
+ return;
1274
+ try {
1275
+ const detail = info?.detail || '';
1276
+ const progress = typeof info?.progress === 'number' ? info.progress : null;
1277
+ const progressTag = progress !== null ? ` (${Math.round(progress)}%)` : '';
1278
+ // Single-line breadcrumb so a developer can correlate
1279
+ // overlay frames with the [ns-hmr][angular] timing log when
1280
+ // debugging "I don't see the overlay" reports.
1281
+ console.info(`[ns-hmr-overlay] update stage=${stage}${progressTag}${detail ? ` detail=${detail}` : ''}`);
1282
+ }
1283
+ catch { }
1284
+ }
625
1285
  function createOverlayApi() {
626
1286
  return {
627
1287
  ensureBootPage(verbose) {
@@ -637,12 +1297,74 @@ function createOverlayApi() {
637
1297
  return state.bootRefs?.page || null;
638
1298
  },
639
1299
  setBootStage(stage, info) {
640
- return applyRuntimeSnapshot(createBootOverlaySnapshot(stage, info));
1300
+ // A boot transition cancels any pending HMR auto-hide so
1301
+ // the boot phase always wins.
1302
+ const state = getRuntimeState();
1303
+ clearUpdateAutoHideTimer(state);
1304
+ state.updateCycleStartedAt = 0;
1305
+ const next = createBootOverlaySnapshot(stage, info);
1306
+ // Monotonic boot-progress ratchet: boot stages can fire out of
1307
+ // order across boot paths (native `__nsStartDevSession` vs the
1308
+ // http-bootloader fallback) and individual bases were tuned
1309
+ // independently, so clamp boot→boot transitions to never go
1310
+ // backwards. Non-boot snapshots (error/ready) bypass — they
1311
+ // genuinely want to reset the visual.
1312
+ if (next.mode === 'boot' && state.snapshot.mode === 'boot' && typeof next.progress === 'number' && typeof state.snapshot.progress === 'number' && next.progress < state.snapshot.progress) {
1313
+ next.progress = state.snapshot.progress;
1314
+ }
1315
+ return applyRuntimeSnapshot(next);
641
1316
  },
642
1317
  setConnectionStage(stage, info) {
1318
+ const state = getRuntimeState();
1319
+ clearUpdateAutoHideTimer(state);
1320
+ state.updateCycleStartedAt = 0;
643
1321
  return applyRuntimeSnapshot(createConnectionOverlaySnapshot(stage, info));
644
1322
  },
1323
+ setUpdateStage(stage, info) {
1324
+ const state = getRuntimeState();
1325
+ // Each new in-progress stage cancels any pending auto-hide
1326
+ // from a previous cycle. Without this, two saves in quick
1327
+ // succession could see cycle-2's progress overlay yanked
1328
+ // off by cycle-1's already-scheduled hide.
1329
+ clearUpdateAutoHideTimer(state);
1330
+ // Stamp the cycle start on 'received', but distinguish
1331
+ // between two cases:
1332
+ //
1333
+ // (a) Re-assertion of the SAME cycle (e.g., the server
1334
+ // emits both `ns:hmr-pending` AND `ns:angular-update`,
1335
+ // both of which call `setUpdateStage('received')`).
1336
+ // We must PRESERVE the original timestamp so the
1337
+ // minimum-visible-window math measures the FIRST
1338
+ // 'received' the user actually saw.
1339
+ //
1340
+ // (b) Genuinely-new cycle starting either from a hidden
1341
+ // overlay OR while the previous cycle is still on
1342
+ // its 'complete' frame (pre auto-hide). In both
1343
+ // sub-cases we MUST stamp a fresh start so the
1344
+ // new cycle's auto-hide math is sane.
1345
+ //
1346
+ // We treat the previous snapshot as "in-progress for the
1347
+ // same cycle" iff mode==='update' AND progress!==100.
1348
+ // 'complete' frames are a sign that the cycle finished;
1349
+ // any subsequent 'received' is a NEW cycle.
1350
+ if (stage === 'received') {
1351
+ const prev = state.snapshot;
1352
+ const isMidCycleReassertion = prev.mode === 'update' && prev.progress !== 100;
1353
+ if (!isMidCycleReassertion) {
1354
+ state.updateCycleStartedAt = Date.now();
1355
+ }
1356
+ }
1357
+ logUpdateStageTransition(state, stage, info);
1358
+ const snapshot = applyRuntimeSnapshot(createUpdateOverlaySnapshot(stage, info));
1359
+ if (stage === 'complete') {
1360
+ scheduleUpdateAutoHide(state);
1361
+ }
1362
+ return snapshot;
1363
+ },
645
1364
  hide() {
1365
+ const state = getRuntimeState();
1366
+ clearUpdateAutoHideTimer(state);
1367
+ state.updateCycleStartedAt = 0;
646
1368
  applyRuntimeSnapshot({ ...DEFAULT_SNAPSHOT });
647
1369
  },
648
1370
  getSnapshot() {
@@ -668,6 +1390,14 @@ export function setHmrBootStage(stage, info) {
668
1390
  export function setHmrConnectionStage(stage, info) {
669
1391
  return ensureHmrDevOverlayRuntimeInstalled().setConnectionStage(stage, info);
670
1392
  }
1393
+ // Public entry point for driving the HMR-applying overlay. Callers
1394
+ // walk through stages (received → evicting → reimporting → rebooting
1395
+ // → complete); 'complete' auto-hides after a short interval.
1396
+ // Soft-fails (no-op) if the runtime overlay was never installed
1397
+ // (e.g., production builds, test environments).
1398
+ export function setHmrUpdateStage(stage, info) {
1399
+ return ensureHmrDevOverlayRuntimeInstalled().setUpdateStage(stage, info);
1400
+ }
671
1401
  export function hideHmrDevOverlay(reason) {
672
1402
  void reason;
673
1403
  ensureHmrDevOverlayRuntimeInstalled().hide(reason);