@kaeawc/auto-mobile 0.0.93 → 0.0.94

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 (228) hide show
  1. package/dist/build-info.json +12 -12
  2. package/dist/src/ble-peripheral.py +153 -0
  3. package/dist/src/{chunk-r6z1eztj.js → chunk-026y62r9.js} +11 -11
  4. package/dist/src/chunk-0jyfg947.js +4 -0
  5. package/dist/src/{chunk-8g1922rp.js → chunk-0zfpp1wh.js} +4 -4
  6. package/dist/src/{chunk-qf6nynk6.js → chunk-13qxf7ym.js} +5 -5
  7. package/dist/src/chunk-1scenrk7.js +4 -0
  8. package/dist/src/chunk-1vb5ph1k.js +4 -0
  9. package/dist/src/{chunk-710rxz9y.js → chunk-20g565q7.js} +3 -3
  10. package/dist/src/chunk-21h6dhdm.js +5 -0
  11. package/dist/src/chunk-25mfpthk.js +78 -0
  12. package/dist/src/{chunk-rbpdk7m0.js → chunk-2b39zyxc.js} +3 -3
  13. package/dist/src/chunk-2gzd6wbd.js +4 -0
  14. package/dist/src/{chunk-xgan0qqp.js → chunk-3dntffy9.js} +1 -1
  15. package/dist/src/chunk-3gfczcsn.js +4 -0
  16. package/dist/src/{chunk-dgrbdpb7.js → chunk-3h5jz1vb.js} +6 -6
  17. package/dist/src/{chunk-t6c8akcm.js → chunk-41zcy6b3.js} +3 -3
  18. package/dist/src/chunk-47n9m2de.js +4 -0
  19. package/dist/src/chunk-4vxeq0vd.js +58 -0
  20. package/dist/src/chunk-59dx6qaf.js +4 -0
  21. package/dist/src/chunk-5sdf9y92.js +4 -0
  22. package/dist/src/{chunk-58wzr3mt.js → chunk-5wk1t46v.js} +3 -3
  23. package/dist/src/{chunk-vy3bwm1d.js → chunk-661c50hh.js} +5 -5
  24. package/dist/src/chunk-68n6bjjd.js +21 -0
  25. package/dist/src/{chunk-v6pya0dm.js → chunk-69pmjdf2.js} +3 -3
  26. package/dist/src/{chunk-e98dqnc9.js → chunk-6hrj3jt6.js} +20 -20
  27. package/dist/src/{chunk-wbh8spm5.js → chunk-6myr26wx.js} +8 -8
  28. package/dist/src/chunk-6n96k09n.js +4 -0
  29. package/dist/src/chunk-6vwrbkre.js +4 -0
  30. package/dist/src/chunk-6wk0yrab.js +4 -0
  31. package/dist/src/chunk-6yhm9een.js +4 -0
  32. package/dist/src/{chunk-mjrps0ac.js → chunk-7k7gjg2t.js} +5 -5
  33. package/dist/src/chunk-7n5fz68h.js +4 -0
  34. package/dist/src/chunk-8cvnpnbp.js +4 -0
  35. package/dist/src/chunk-8kzv7rk7.js +4 -0
  36. package/dist/src/chunk-8nj68t1v.js +167 -0
  37. package/dist/src/{chunk-g7tmjbkt.js → chunk-96r3bpg7.js} +3 -3
  38. package/dist/src/chunk-97x887te.js +4 -0
  39. package/dist/src/{chunk-8ph29g99.js → chunk-99a6tbvd.js} +1 -1
  40. package/dist/src/chunk-9p5d2994.js +5 -0
  41. package/dist/src/{chunk-aabchzxk.js → chunk-9q265er0.js} +4 -4
  42. package/dist/src/{chunk-jh6vhqzx.js → chunk-9xgnrv5w.js} +3 -3
  43. package/dist/src/chunk-9xgsmshb.js +5 -0
  44. package/dist/src/chunk-a02d1wgv.js +4 -0
  45. package/dist/src/{chunk-ndt1ccxm.js → chunk-a9ps00a1.js} +10 -10
  46. package/dist/src/{chunk-5d2hft6c.js → chunk-aewdveet.js} +3 -3
  47. package/dist/src/{chunk-ycaj2sgf.js → chunk-b3tdtq7r.js} +6 -6
  48. package/dist/src/chunk-bn7jx7a2.js +4 -0
  49. package/dist/src/{chunk-3y6eykz9.js → chunk-bnyw27ja.js} +2 -2
  50. package/dist/src/{chunk-rqc9ap3x.js → chunk-bpwsg76p.js} +2 -2
  51. package/dist/src/chunk-bvranw3g.js +4 -0
  52. package/dist/src/chunk-c25a84zb.js +4 -0
  53. package/dist/src/chunk-c27yv1dx.js +5 -0
  54. package/dist/src/chunk-c3ewxm4x.js +5 -0
  55. package/dist/src/{chunk-rvvy1bte.js → chunk-cb1ga9rd.js} +5 -5
  56. package/dist/src/chunk-cn7fwy7k.js +4 -0
  57. package/dist/src/chunk-cnvy4a64.js +4 -0
  58. package/dist/src/{chunk-7rz9898k.js → chunk-cqhw4yr6.js} +3 -3
  59. package/dist/src/{chunk-g04bwyj3.js → chunk-ctmfshjc.js} +2 -2
  60. package/dist/src/chunk-ctwmhz13.js +4 -0
  61. package/dist/src/{chunk-waqwcjyn.js → chunk-d3jx9bz5.js} +2 -2
  62. package/dist/src/{chunk-ayp07wbb.js → chunk-d6rb08ga.js} +1 -1
  63. package/dist/src/chunk-ddqhj4eg.js +5 -0
  64. package/dist/src/{chunk-r7bpkgff.js → chunk-ddrhs1c3.js} +2 -2
  65. package/dist/src/chunk-dnjnh9p6.js +4 -0
  66. package/dist/src/{chunk-wyrkw6hw.js → chunk-dw5rwgya.js} +3 -3
  67. package/dist/src/{chunk-3xkpk44w.js → chunk-e0870khr.js} +3 -3
  68. package/dist/src/chunk-ea3k10kj.js +4 -0
  69. package/dist/src/chunk-emv82m1w.js +7 -0
  70. package/dist/src/chunk-f3m6pdnx.js +4 -0
  71. package/dist/src/chunk-f84z32cb.js +4 -0
  72. package/dist/src/chunk-fpczgf0z.js +4 -0
  73. package/dist/src/{chunk-2tqzq5qh.js → chunk-g2js4c2v.js} +18 -18
  74. package/dist/src/chunk-g3af2qbk.js +5 -0
  75. package/dist/src/{chunk-3qwrzxh0.js → chunk-gadtna7s.js} +6 -6
  76. package/dist/src/{chunk-ybbtagbt.js → chunk-gcyx407v.js} +3 -3
  77. package/dist/src/{chunk-bfw6125s.js → chunk-gfpntwc1.js} +3 -3
  78. package/dist/src/chunk-gmnm5e3e.js +4 -0
  79. package/dist/src/chunk-gp6vb5gf.js +4 -0
  80. package/dist/src/chunk-gppgy7pa.js +4 -0
  81. package/dist/src/chunk-hegfz0ae.js +5 -0
  82. package/dist/src/chunk-hpzxdp1k.js +17 -0
  83. package/dist/src/{chunk-qcxzr2my.js → chunk-j69g8b50.js} +3 -3
  84. package/dist/src/chunk-jvstjnqm.js +19 -0
  85. package/dist/src/chunk-jzqrv78s.js +4 -0
  86. package/dist/src/chunk-k2948b8d.js +4 -0
  87. package/dist/src/chunk-k4ftd40d.js +4 -0
  88. package/dist/src/{chunk-bxmsax22.js → chunk-kd9vfaxh.js} +3 -3
  89. package/dist/src/{chunk-s3jc6cwp.js → chunk-kdczmgwk.js} +3 -3
  90. package/dist/src/{chunk-t6bfvfkg.js → chunk-ke3wn14v.js} +1 -1
  91. package/dist/src/chunk-kh5n7m6w.js +4 -0
  92. package/dist/src/{chunk-6wt9rgjf.js → chunk-khejfph4.js} +3 -3
  93. package/dist/src/chunk-kny2xemh.js +4 -0
  94. package/dist/src/chunk-knyzw9md.js +4 -0
  95. package/dist/src/chunk-mbzpt2hp.js +4 -0
  96. package/dist/src/chunk-mm8rcxad.js +4 -0
  97. package/dist/src/chunk-nc4516tx.js +4 -0
  98. package/dist/src/{chunk-6bznss4t.js → chunk-ntfrqrqn.js} +5 -5
  99. package/dist/src/chunk-nz5h2j52.js +4 -0
  100. package/dist/src/chunk-p22s97qy.js +4 -0
  101. package/dist/src/chunk-p9kt1sgr.js +6 -0
  102. package/dist/src/{chunk-yb97j25b.js → chunk-pf282r5g.js} +4 -4
  103. package/dist/src/chunk-pnd4mrxy.js +4 -0
  104. package/dist/src/chunk-pqqteyj6.js +4 -0
  105. package/dist/src/{chunk-6xmss8er.js → chunk-qj1tbnv2.js} +6 -6
  106. package/dist/src/chunk-qnh9egwr.js +4 -0
  107. package/dist/src/chunk-qy3khhf8.js +8 -0
  108. package/dist/src/{chunk-z637nqg8.js → chunk-r0vp315h.js} +6 -6
  109. package/dist/src/chunk-r4j43b9s.js +237 -0
  110. package/dist/src/{chunk-1p7xf5kb.js → chunk-r54cxxze.js} +3 -3
  111. package/dist/src/{chunk-yf7ympyz.js → chunk-r6pvhj33.js} +92 -92
  112. package/dist/src/chunk-rb7aa5gr.js +54 -0
  113. package/dist/src/chunk-rdj50kb4.js +4 -0
  114. package/dist/src/chunk-rnd4vcas.js +4 -0
  115. package/dist/src/{chunk-w5b2jwk3.js → chunk-s3y3zfcx.js} +8 -8
  116. package/dist/src/chunk-s4g43k75.js +5 -0
  117. package/dist/src/chunk-sfqe223f.js +311 -0
  118. package/dist/src/{chunk-284ptqa4.js → chunk-sqdrxjw5.js} +5 -5
  119. package/dist/src/{chunk-zgja5f3p.js → chunk-t54k47em.js} +4 -4
  120. package/dist/src/{chunk-3sy6ahhz.js → chunk-tek4pnvv.js} +10 -10
  121. package/dist/src/chunk-tn74y7af.js +4 -0
  122. package/dist/src/{chunk-thbh3jew.js → chunk-tvq0nzam.js} +3 -3
  123. package/dist/src/{chunk-0jrrsnc9.js → chunk-tx4f0h9c.js} +4 -4
  124. package/dist/src/chunk-vdqag3fj.js +5 -0
  125. package/dist/src/chunk-vz5zzs57.js +4 -0
  126. package/dist/src/chunk-w4s2vfpj.js +4 -0
  127. package/dist/src/{chunk-4hw3665t.js → chunk-w5yypt53.js} +11 -11
  128. package/dist/src/{chunk-8037tzzj.js → chunk-w68v64vh.js} +3 -3
  129. package/dist/src/chunk-whwj0pdk.js +5 -0
  130. package/dist/src/chunk-wnmqt1dc.js +4 -0
  131. package/dist/src/{chunk-cyy71ggq.js → chunk-wpe46vsa.js} +3 -3
  132. package/dist/src/{chunk-w3vs79ex.js → chunk-wxdjeb4k.js} +19 -19
  133. package/dist/src/chunk-xcqzwf9j.js +247 -0
  134. package/dist/src/chunk-xezekwwn.js +5 -0
  135. package/dist/src/chunk-xf19m15s.js +4 -0
  136. package/dist/src/chunk-xghbxha4.js +4 -0
  137. package/dist/src/{chunk-00167wan.js → chunk-xn9m3xc2.js} +5 -5
  138. package/dist/src/{chunk-26k317v5.js → chunk-yqfz4vts.js} +3 -3
  139. package/dist/src/chunk-z8tvs8nk.js +5 -0
  140. package/dist/src/chunk-zm2rehqb.js +5 -0
  141. package/dist/src/chunk-zs6dqm35.js +4 -0
  142. package/dist/src/daemon/processEntry.js +2 -2
  143. package/dist/src/index.js +4 -4
  144. package/dist/src/runtime/bunRuntimeGuard.js +2 -2
  145. package/dist/src/utils/workers/iosBundleExtractWorker.js +2 -2
  146. package/package.json +2 -2
  147. package/dist/src/chunk-0n6swkwg.js +0 -5
  148. package/dist/src/chunk-0qsy1qsx.js +0 -57
  149. package/dist/src/chunk-11m8wj8j.js +0 -237
  150. package/dist/src/chunk-23p5f095.js +0 -4
  151. package/dist/src/chunk-28re4xrg.js +0 -4
  152. package/dist/src/chunk-2f9rkmhx.js +0 -5
  153. package/dist/src/chunk-2fxdh48t.js +0 -4
  154. package/dist/src/chunk-2tbdrbt6.js +0 -4
  155. package/dist/src/chunk-32gvv9b4.js +0 -21
  156. package/dist/src/chunk-3f3t1x97.js +0 -8
  157. package/dist/src/chunk-3fspkbc7.js +0 -4
  158. package/dist/src/chunk-3vg455e6.js +0 -4
  159. package/dist/src/chunk-49bsqdx3.js +0 -4
  160. package/dist/src/chunk-5snsn9cf.js +0 -4
  161. package/dist/src/chunk-5tm9mn47.js +0 -54
  162. package/dist/src/chunk-5zc77a5t.js +0 -4
  163. package/dist/src/chunk-64j0majr.js +0 -19
  164. package/dist/src/chunk-65y8v4td.js +0 -17
  165. package/dist/src/chunk-6awz7vhq.js +0 -5
  166. package/dist/src/chunk-6ep7f5ge.js +0 -4
  167. package/dist/src/chunk-7cs2da62.js +0 -5
  168. package/dist/src/chunk-7g103b54.js +0 -4
  169. package/dist/src/chunk-7j5vj4pa.js +0 -4
  170. package/dist/src/chunk-7tw8x05v.js +0 -4
  171. package/dist/src/chunk-7zy586cb.js +0 -4
  172. package/dist/src/chunk-84nanf8s.js +0 -5
  173. package/dist/src/chunk-8gvwcv2p.js +0 -4
  174. package/dist/src/chunk-8thrmfat.js +0 -4
  175. package/dist/src/chunk-8zgzzs7k.js +0 -78
  176. package/dist/src/chunk-97b1zmaq.js +0 -4
  177. package/dist/src/chunk-9fth9ayh.js +0 -4
  178. package/dist/src/chunk-9j6anhjr.js +0 -4
  179. package/dist/src/chunk-bfjwqk8q.js +0 -4
  180. package/dist/src/chunk-bnhscm2n.js +0 -4
  181. package/dist/src/chunk-c1vs4g1n.js +0 -4
  182. package/dist/src/chunk-ct3e7yng.js +0 -4
  183. package/dist/src/chunk-ddzkz92z.js +0 -4
  184. package/dist/src/chunk-drdz8hee.js +0 -5
  185. package/dist/src/chunk-eersndy7.js +0 -5
  186. package/dist/src/chunk-eggq53ks.js +0 -4
  187. package/dist/src/chunk-f2dya7cs.js +0 -4
  188. package/dist/src/chunk-fbakd6cw.js +0 -4
  189. package/dist/src/chunk-fgb98zjj.js +0 -5
  190. package/dist/src/chunk-fmpad823.js +0 -5
  191. package/dist/src/chunk-g0xr8zw0.js +0 -4
  192. package/dist/src/chunk-g3rjs80x.js +0 -4
  193. package/dist/src/chunk-g78rtrfv.js +0 -4
  194. package/dist/src/chunk-gabgv3y5.js +0 -4
  195. package/dist/src/chunk-gmr45m8f.js +0 -4
  196. package/dist/src/chunk-gsyfebqj.js +0 -4
  197. package/dist/src/chunk-h5rpn9wr.js +0 -247
  198. package/dist/src/chunk-hb1x7v55.js +0 -4
  199. package/dist/src/chunk-hrfytkxh.js +0 -168
  200. package/dist/src/chunk-j4yjzbak.js +0 -4
  201. package/dist/src/chunk-jd6a5v6p.js +0 -5
  202. package/dist/src/chunk-kn5spq15.js +0 -4
  203. package/dist/src/chunk-m94qs0f0.js +0 -4
  204. package/dist/src/chunk-n03dmn5q.js +0 -5
  205. package/dist/src/chunk-n72qhwd9.js +0 -4
  206. package/dist/src/chunk-nr6y0n4y.js +0 -5
  207. package/dist/src/chunk-pcsen2vs.js +0 -4
  208. package/dist/src/chunk-ppfr0b8h.js +0 -4
  209. package/dist/src/chunk-q10bdnat.js +0 -4
  210. package/dist/src/chunk-q9xmq595.js +0 -4
  211. package/dist/src/chunk-qyqypn28.js +0 -5
  212. package/dist/src/chunk-r2epb56s.js +0 -4
  213. package/dist/src/chunk-r5bxavdx.js +0 -311
  214. package/dist/src/chunk-r8jf3d92.js +0 -4
  215. package/dist/src/chunk-rc0dbypm.js +0 -4
  216. package/dist/src/chunk-rhcty4ms.js +0 -4
  217. package/dist/src/chunk-rpvckv3p.js +0 -4
  218. package/dist/src/chunk-ry0mz41y.js +0 -4
  219. package/dist/src/chunk-sfv61xjb.js +0 -6
  220. package/dist/src/chunk-t7w8rctc.js +0 -4
  221. package/dist/src/chunk-v2wd4n3e.js +0 -5
  222. package/dist/src/chunk-w4zga19f.js +0 -4
  223. package/dist/src/chunk-w7hkv422.js +0 -5
  224. package/dist/src/chunk-xa73x2cx.js +0 -5
  225. package/dist/src/chunk-y2vs6jwg.js +0 -4
  226. package/dist/src/chunk-yattm52s.js +0 -4
  227. package/dist/src/chunk-yjawdzk8.js +0 -4
  228. package/dist/src/chunk-zs1sghs9.js +0 -4
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- import{W,c,ar,dE,lE,xO,Es,Rf,kt,pE,ty,aK,Lu,wa,lae,FO}from"./chunk-4hw3665t.js";import"./chunk-z637nqg8.js";import"./chunk-58wzr3mt.js";import{lu,Uh,uu,CV,DV,yC,fv,YF,C5,D5,vC,O5,JF,OV,M5,N5}from"./chunk-mjrps0ac.js";import{Ze,sb}from"./chunk-r5bxavdx.js";import{Kp,Gp,TG,Nf,dy,Jb,Xb,ly,UE}from"./chunk-11m8wj8j.js";import{SC,ZF,hT}from"./chunk-aabchzxk.js";import{t}from"./chunk-rbpdk7m0.js";var ie='## Destinations of device requests\n\nA `request` action with `via: "device"` is checked on the device before it connects. It is refused\nwith `blocked_destination` when the URL names `localhost` (or a `*.localhost` name), or when the\ndestination resolves to a loopback, unspecified or link-local address. On Android this also covers\nthe emulator\'s addresses for the host machine (`10.0.2.2` and the other `10.0.2.x` aliases), and\nports 3000-3010 and 8765 on a private address are refused too. No `adb reverse` is made for a\nprototype, so a device request cannot reach a server on the host machine through `localhost`: use\n`via: "host"` for a host-local server, or call it by an address the device can reach that is not\nloopback. On iOS, call a Mac-local service by the Mac\'s LAN address.\n\n## Mock backends (no server code)\n\nBuild a backend-driven prototype without running a server. `defineBackend {backendId, backend}`\nregisters an in-process backend for this daemon session (`backendId` is lowercase letters, digits\nand hyphens); a `request` with `via: "host"` to `https://<backendId>.mock.automobile.invalid/<path>`\nis answered by it, with no network, DNS lookup or egress policy. The host must be connected, so it\ndoes not apply to `via: "device"` requests or saved prototypes. `backendLog {backendId, since?}` lists the requests it\nanswered and `clearBackend {backendId}` removes it; defining the same id again replaces it and resets\nits data. Requests to a backend that is not defined fail with `network`.\n\n```json\n{"action":"defineBackend","backendId":"shop","backend":{\n "collections":{"products":[{"id":1,"title":"Mug","price":12},{"id":2,"title":"Tote","price":20}]},\n "routes":[{"method":"GET","path":"/greeting/:name","body":{"text":"Hello {params.name}"}}],\n "latencyMs":{"min":200,"max":600},"faults":[{"path":"/products","method":"POST","status":500,"every":3}],\n "seed":7}}\n```\n\nA `show` can define backends itself: `mock` is `{backendId: definition}` (the same definition shape),\nor `mockPath` names an absolute local JSON file holding that object (up to 2 MiB; the path never\nreaches the device). They are defined, all or none, once the spec is accepted and before it is\nsent, replacing backends of the same id, and a saved prototype does not carry them.\n\n- **Collections** are REST over `{name: [items]}`; every item needs an `id` (a number, or a non-empty\n string other than `.` and `..`). `GET /products?title=Mug`, `q=mug` (case-insensitive substring over\n string and number members), `_sort=price` or `-price`, `_page` and `_limit` (an\n `x-total-count` header reports the total), or `after=<id>&size=<n>` for `{items, next}` cursor paging.\n `GET /products/1`, `POST` (201; the id is generated when absent, 409 if it exists), `PATCH` (merge),\n `PUT` (replace) and `DELETE`. Writes persist for the session; a backend holds at most 1 MiB.\n- **Routes** are canned responses that win over collections. Paths are literal segments and\n `:name` parameters. A body string that is exactly `{params.x}`, `{query.x}` or `{body:/pointer}`\n takes that value\'s type; inside longer text it is inserted as text; an unbound one is `null` or empty.\n- **Latency and faults** replay identically for the same `seed`: `latencyMs {min,max}` delays every\n answer, and a fault `{path, method?, status, every | rate}` fails every nth matching request or\n that fraction of them with `{error:"mock fault"}`.\n- **Actors** play the other side (at most 16, each up to 64 steps). An actor fires on a matching\n successful request (`on: {route: "POST /messages/:id"}`) or every N ms (`every` with `maxRuns`),\n after an optional `afterMs` lead, and runs `do` in order: `{append: collection, value}`,\n `{update: collection, id, value}` (merge), `{remove: collection, id}`, each with an optional\n `afterMs` after the previous step. `value` and `id` bind `{params.x}`, `{query.x}` and\n `{body:/pointer}` from the triggering request. A failed step is skipped and the run goes on;\n redefining or clearing the backend cancels pending steps. Poll the collection to see the result.\n `publish`, `advance` and `notify` steps are not supported yet.\n- A request\'s `timeoutMs` applies to the mock too, and a response over the 262144-byte cap fails\n with `too_large`.\n- **Rooms** serve realtime channels with `via: "host"`: a WebSocket to\n `wss://<backendId>.mock.automobile.invalid/rooms/general` or an SSE stream to the `https://` form\n joins the first room whose `path` (`/rooms/:roomId`) matches; no match is `handshake_failed`\n with status 404. A room `{path, onConnect?, echo?, relay?, timeline?}` sends its `onConnect` frames\n to each new connection, `timeline [{afterMs, frame}]` frames that long after each connection\n opened, returns a send to its sender with `echo`, and passes it to the room\'s other connections\n with `relay`. Frames bind `{params.x}` and `{query.x}` from the channel URL; a string frame is sent\n as written, other JSON as JSON text.\n- **Broadcasts** `{collection: {room, frame, on?}}` send a successful collection write (`POST` by\n default; `on` takes `POST`, `PUT`, `PATCH`) to a room. `{item}` and `{item:/pointer}` bind the\n stored item, so `{"messages":{"room":"/rooms/{item:/roomId}","frame":{"message":"{item}"}}}` makes a\n message one device posts appear on every device in that room.\n- `publish {backendId, room, frame}` speaks as the other side to every connection in `room` and\n returns `delivered`. `backendLog` lists room frames (`{room, from, data}`, `from` is `client`,\n `publish` or `broadcast`) beside requests. Redefining or clearing a backend closes its channels\n with code 1001.\n\n## Value formatters (#14227)\n\nUse top-level `derive: {priceText: {of: {data: "/price"}, format: {currency: {code: "USD"}}}}`\nand read `{derived:priceText}` in text node text, contentDescription, rich-text spans,\nbutton/FAB labels, topAppBar title, badge text, dialog title/text, and snackbar text.\nOther text-kind fields (request url/headers/body, check messages, and\nselect/segmented/menu/searchBar labels) validate unknown names but show tokens literally on devices.\nThere are at most 32 names (state-key syntax); unknown reads fail validation.\n`of` chooses exactly one data pointer, state key or literal `{value: number|string}`.\nInside a repeat, `repeat.derive` supports at most 8 names, with `of: {item: "field"}`\nor a literal value; the resulting strings bind as `{item.priceText}` using your alias.\nNames cannot collide with original row fields; derived fields cannot reference each other.\nHost validation checks field references; device-dependent key and emit-name value checks wait for the renderer.\n\n`format` has exactly one key with an object: `number {minFraction?,maxFraction?,grouping?}`,\n`compact {style?:short|long}`, `currency {code:USD}`, `percent {minFraction?,maxFraction?}`,\n`date {style?:short|medium|long}`, `time {style?:short|medium|long}`, or\n`relative {style?:narrow|short|long}`. Fraction digits are integers 0..20;\nmin cannot exceed max. Grouping true/default respects locale minimum grouping digits;\nfalse disables grouping entirely. Compact, currency, and percent respect locale minimum grouping digits.\nCompact defaults short, date medium,\ntime short, relative long. Currency codes are three uppercase letters and use CLDR minor units.\nNumber kinds require finite numbers; percent inputs are ratios (0.256 means 25.6%).\nDate/time/relative accept integer epoch milliseconds, YYYY-MM-DD (midnight UTC),\nor ISO date-times with seconds and Z or an explicit +hh:mm offset; fractional seconds work.\nInvalid or missing inputs display empty text. Optional entry `locale` (BCP 47, <=35 characters)\nand `timeZone` (IANA id, <=128) override device defaults.\n\nRelative time uses an injected clock snapshot, truncates toward zero, and picks the largest\nelapsed unit: seconds, minutes (60 s), hours (60 min), days (24 h), months (30 d), years (365 d).\nUnder one second uses localized now. Exact English fixtures normalize U+202F/U+00A0 to spaces;\nforeign currency symbols, narrow relative text, now wording and non-English CLDR data can vary\nby engine and use structural comparisons. Set locale, zone and clock for deterministic previews.\nBoth derive maps require `prototype_format_v1`; use literal strings with older renderers.\n`dateTime`, `duration`, `plural`, and `fileSize` are unsupported follow-ups.\n',de='### Timers\n\nTimer actions and `onAppear` require `prototype_timers_v1`.\nThe root and nodes declaring `visibleWhen` may set `onAppear` to 1..32 ordinary actions.\nThey run on first show if visible, and on each hidden-to-visible edge; visible-to-visible\nand a same-id show without reset do not run them again. Repeat placeholders bind as in onTap.\nEach list is atomic, validates like a tap, and emits change for a net state change, without\nrestarting idle TTL. A handler runs at most once per interaction, bounded by the action-depth limit.\nFor a splash, put `onAppear:[{type:"startTimer",id:"splash",afterMs:2000,\nactions:[{type:"setState",key:"loading",value:false}]}]` on the root and use\n`visibleWhen:{key:"loading",equals:false}` on content. Hiding a node does not cancel its timer.\nStart a timer from any action list:\n`{type:"startTimer",id:"tick",afterMs:100,everyMs:1000,maxRuns:10,actions:[{type:"emit",name:"tick"}]}`.\n`afterMs` is required; it and optional `everyMs` are integers 100..3600000 ms.\n`maxRuns` is 1..10000 total fires, including the first, and requires `everyMs`.\nWithout maxRuns, repeats continue until cancelled or dismissed. Actions contain 1..32 entries,\nwith the same validation and nesting limit as request follow-ups. IDs are literal strings of\n1..64 characters, never repeat-bound; at most eight distinct startTimer ids per spec.\n`{type:"cancelTimer",id:"tick"}` removes a timer; unknown ids are no-ops. The host warns\non show and patch if no startTimer declares the cancelled id; devices do not validate that advisory.\n\nStarting a live id replaces its schedule and resets runs. Each fire is one atomic interaction:\ninvalid planned writes drop the whole fire but count the run; a net state change emits one change.\nSelf-cancellation stops repeats, and a replacement started during a fire wins. Repeats schedule\nfrom planned fire time without drift. Timers pause while hidden or inactive, retaining remaining\ntime (screenshot capture does not pause); dismiss, teardown and any replacing show cancel them.\nPatch preserves them. Fires never restart idle TTL.\n\n`prototype {action:"simulate",id,advanceTimeMs:1000}` advances without waiting (integer\n1..3600000), requiring the same capability. It returns `fired`; paused timers fire zero times.\nDue timers run chronologically, ties by start order, including timers started during advancement.\nExactly 10000 due fires succeed. If another fire is still due, advance stops at the last fire with\nan error and fired=10000, keeping pending timers for the next call. Real scheduling continues from the shifted clock.\nInspect reports `timers:[{id,nextFireInMs,runs}]`, ordered by remaining time then start order;\nremaining time is nonnegative and frozen while paused. A capable device yields an empty array\nwhen no timers are live.\n',le='## Strings and semantics\n\nTop-level `strings` requires `prototype_strings_v1`: `{default: {title: "Cart", items: {one: "{#} item", other: "{#} items"}}, fr: {title: "Panier"}}`.\nUse `{t:title}` or `{t:items|2}` in display text, spans, semantics.label or semantics.hint.\nCounts are non-negative integer literals (1\u20139 digits) or `{data:/pointer}`; state-key counts are unsupported.\nPipeline: data substitution, then translation, then state binding. Translation values may contain state bindings; translated text is never scanned again for translations. `{#}` inserts the decimal count in a plural form. Invalid runtime counts use other.\nshow `locale` (locale tag, max 35 characters) overrides device preferred locales for that show; omitted follows device locales, which re-resolve live.\nEach requested locale tries its full tag then shorter prefixes, deduplicated, then default. Tags match case-insensitively with underscores normalized to hyphens. Lookup is per key; the supplying table language decides plurals (default uses the first requested language, else en).\nInteger plural groups: ja/zh/ko/vi/th/id/ms/lo/km/my use other; fr/pt/hi/bn/fa/gu/kn/am use one for 0\u20131; ru/uk/be and pl use one/few/many; cs/sk use one/few/other; ar uses zero/one/two/few/many/other; remaining languages use one at 1, other otherwise. Missing forms use other; million-multiple Romance many is outside v1.\nLimits: 16 tables, 512 keys per table, 2048 characters per nonempty value; default and every plural other form are required. Keys use letters/underscore followed by letters/digits/underscore/dot/hyphen, at most 64 characters. Values cannot contain translation, data or platform placeholders.\nWith an explicit locale, `platformNotes` construct `strings.fallback` (platform all) lists up to 20 referenced keys reaching default. Advisory lint reports missing-translation and unknown-translation-key.\nPseudo-localisation transforms each table value and plural form while preserving keys, tags, category names and bindings; strings is opaque to node walking. Node translation placeholders stay intact; semantics label and hint are display text.\n\nEvery node accepts `semantics {heading?,liveRegion?: "polite"|"assertive",group?,label?,hint?,traits?: ["button"|"image"|"selected"]}`, requiring `prototype_semantics_v1`. At least one field is required; traits are unique (1\u20133). Label/hint are nonempty and accept translations, data and state bindings. Label conflicts with contentDescription.\nHeading enables screen-reader navigation; empty-heading lint flags headings without text/label. Group contains child traversal. Android maps heading/liveRegion/traversal group, label to contentDescription, hint to onClickLabel for tap actions else stateDescription, and traits to Role.Button/Role.Image/selected. iOS maps header/button/image/selected traits, contain grouping, label/hint, and live-region change announcements (not initial render; assertive has high speech priority).\nAndroid observe exposes heading as the string "true" and live-region as polite/assertive; iOS snapshots currently expose no SwiftUI traits. Logical RTL swipe sides are outside this capability.\n',ce='## User-testing sessions\n\nCall `prototype {action:"testSessionBegin", id, goal?, cohort?}` after showing a prototype.\nThe host records accepted events without consuming `awaitEvent`. A goal is a screen label\nsuch as `checkout/done`; reaching it marks completion. Cohort is a free label, with no\ncross-session aggregation. `testSessionEnd {id}` stops recording and returns JSON plus\nMarkdown. Dismissal or session release also ends recording. `testSessionReport {id}`\nreads the latest session for this scope, or pass `testSessionId` for a stored one.\nOnly one session may be active per device and prototype.\n\nReports include screen visits, time, abandonment, validation failures and pager changes.\nLabels use the deepest stack\'s top screen as `stack/screen` (ties use the lowest stack id),\nor `(root)`. Text values, payloads, context and state snapshots are never stored;\nnon-secure changes retain only keys, string lengths or value types and validity.\nSecure fields are excluded. Up to 5,000 sanitized events are retained per session; extra\nevents are counted as dropped and the report is marked truncated. The newest 50 ended\nsessions are kept per device/prototype.\n\nTo record dead taps, show with `window.deadTaps: true` on a device advertising\n`prototype_dead_tap_v1`; an older agent is refused. Dead taps are single-pointer taps\ninside the prototype window that no interactive node handled, excluding drags, scrolling\nand app taps outside the window. Their payload has only screen ids and coordinates,\nrounded to one decimal in window-local dp (points on iOS). Reports include counts,\nup to 200 points per screen and a 48-unit grid. There is no screenshot overlay yet.\n\nStack transition patterns: `sharedAxisX`, `sharedAxisY`, `fadeThrough` and `zoom` (and a push/replace\n`source`) require `prototype_stack_patterns_v1`; zoom needs a source naming exactly one shared key inside the target stack\'s screens.\nWith `prototype_zoom_transition_v1`, zoom morphs source-to-root on Android and uses native\ninteractive zoom on iOS 18+. Older agents keep fadeThrough (Android) or platform push (iOS 17);\nplatformNotes reports the fallback. The new capability reports rendering, not spec acceptance.\nReduced motion crossfades and motion off snaps; composed iOS patterns pop non-interactively.\n',Y='### Data actions\n\nA tap can change `/data` on the device without the agent answering it. Data actions write a\nmember of `data`; each `path` is `/data` plus a non-empty data pointer (the same syntax and\n`MAX_PROTOTYPE_DATA_POINTER_LENGTH` as `{data: pointer}`), so the whole document cannot be\nwritten:\n\n- `{type: "setData", path, value}` creates or replaces an object member, or replaces an existing\n array element. A missing parent is an error.\n- `{type: "appendData", path, value}` appends to the existing array at `path`.\n- `{type: "prependData", path, value}` inserts one row at the start; list maxItems trims from the end.\n- `{type: "removeData", path}` removes an existing member or element; later elements shift down.\n\n`value` uses the emit.context binding forms except `page`: `{value: json}` is the literal,\n`{state: key}` reads state, `{data: pointer}` reads data, and `{item: field}` and\n`{index: true}` read the repeat row (repeat only). A written value must be data: strings, booleans,\nfinite numbers, arrays and objects, without null. Inside a repeat, a whole `{index}` segment of\n`path`, as in `/data/cart/{index}`, is bound to the current row; `{index}` anywhere else, or\n`{alias.field}`, is rejected.\n\nThe host validates pointer syntax and `{index}` use, rejects a literal value data cannot hold, and\nrejects an `appendData` or `prependData` whose target resolves in the spec\'s own `data` to something other than an\narray. A target that only exists after earlier writes is checked by the device. A write that would\nleave `data` over `MAX_PROTOTYPE_DATA_WRITE_BYTES` (or over a depth or array limit), or whose\ntarget is missing or not an array, changes nothing.\nActions run in order, so a later action sees an earlier write.\n\nEach write is a `change` event with `name` the written `/data/...` path and `payload`\n`{op: "set"|"append"|"remove", value?, revision}`; `awaitEvent` with `kind: "change"` returns it.\n`revision` is the device revision after the write, the same counter as a patch result\'s. The host\napplies each write to its own copy of the spec and to `status` data, so both stay current. The\nagent\'s `/data` patches and a device\'s writes never silently lose each other: once the device\nhas written a prototype\'s data, a patch with `/data` ops needs `baseRevision` (the latest\n`revision` the agent has seen) and is refused with `conflict: {revision, state, data}` when a write\nnewer than that revision touched an overlapping path (`data` lists those paths). A `show` makes its\nown `data` authoritative again and clears the history.\n\nData actions require `prototype_data_actions_v1`. A show or patch result that contains one is\nrefused on a device without it, before anything is shown or sent.\n\n\n### Realtime channels\n\nThe validated contract requires `prototype_platform_realtime_v1`; current Android and iOS\nruntimes do not advertise it. A root `channels` map holds at most four named channels and\nrequires `requires.platform:["realtime"]`. Each channel declares `kind:"websocket"|"sse"`\nand `url` (ws/wss or http/https respectively), using request URL placeholders and length rules.\nOptional fields: `protocols` (WebSocket only, up to four), `headers` (up to 16, request rules\nplus refusing Sec-WebSocket-* and Last-Event-ID), `via:"host"|"device"` (host default),\n`connect:"onShow"|"manual"` (onShow default), declared string `statusKey`,\n`decode:"json"|"text"` (json default), `reconnect`, `routes`, `onOpen/onClose/onError`.\nDevice routing additionally requires `prototype_platform_realtime_device_v1`.\nReconnect fields are integer `maxAttempts:0..20`, `initialDelayMs>=100`,\n`maxDelayMs<=60000`, with initial <= max. Up to 16 routes use `when:{pointer,equals}`,\n`{pointer,exists:boolean}`, `{event:SSE-name}`, or omit when. Their actions are state/data\nwrites; request and navigation are refused. In channel callbacks and platform handlers,\n`{event:"/message"}` reads decoded event data and `{event:""}` reads the whole event.\nCalls use `{type:"platform",call:"realtime.connect"|"realtime.disconnect",args:{channel:"chat"}}`\nand `realtime.send` with `args:{channel:"chat",body:{value:json}}` (WebSocket only).\nRead live counters with `{platform:"/realtime/chat/framesIn"}`; state keys are status, attempt,\nframesIn, framesDropped, closeCode, closeReason and error. Events are realtime.status and realtime.frame.\nThe same capability gates `appendData.maxItems` and `upsertData {path,key,value,maxItems?}`.\nUpsert replaces the first row whose key pointer matches the incoming value, otherwise appends;\nmissing incoming identity is an error. maxItems is 1..2000 and drops oldest rows before limits apply.\nFrame/send budgets are 65536/16384 bytes, with eight open channels per device and a 50 ms flush budget.\nTransport, lifecycle and routing execution remain unavailable. Flush-window coalescing is an open\ndecision; no coalescing behavior is implemented.\n',pe="\n### Adaptive panes and navigation\n\n`adaptive` needs `prototype_adaptive_v1` and a closed `kind`:\n- `listDetail`: `list` and `detail` node slots, `selectionKey` naming a declared string state key. Empty string means no selection; selecting pushes detail in compact layout and Back clears the key.\n- `navigationSuite`: 1\u20138 unique `items: [{value,label}]`, `stateKey` naming a declared string whose value matches an item, and a `content` node slot. Item taps write that value. Use conditions on the key to choose content.\n- `supporting`: `main` and `supporting` node slots, `openKey` naming a declared boolean. Close clears the key.\n\nWidths are native per platform. Android Material adaptive scaffolds split panes at expanded and use bar/rail/drawer at compact/medium/expanded. iOS horizontalSizeClass regular uses split/sidebar/inspector, compact uses stack/bottom bar/sheet. There is no shared breakpoint or `minWidth`; `platformNotes` reports the different rules on show. iOS draws Material-styled containers under every design until native platform containers are available.\n",he='## Platform namespaces\n\nChoose styling in this order:\n1. Portable node/window fields express behavior on both platforms: use `button.variant: "outlined"`\n for an outlined button, or `style.cornerRadius` for an authored corner.\n2. Portable theme tokens express a shared visual system: set `theme.colors.primary` once rather\n than repeating a color on each button; use theme typography and corner tokens for shared styles.\n3. `theme.design: "platform"` chooses native platform treatment for covered controls (the default);\n `"material"` keeps the shared Material appearance. Check design fallback notes for custom controls.\n4. Use `ios`/`android` only for semantics with no portable meaning, such as\n `ios: {buttonStyle: "glass"}` or `android: {predictiveBack: {style: "none"}}` on a stack.\n\nNamespaces are closed objects on nodes, window and theme; each field has a contract `why`.\nOwn-platform blocks require `prototype_platform_namespace_v1`, except the four node glass fields,\nwhich require only `prototype_glass_v1` on iOS; mixing another iOS key requires both.\nForeign blocks are accepted and\nignored with paths in `platformNotes`; unknown fields are rejected even in foreign blocks.\niOS glass buttons need 26 and otherwise use bordered buttons; hover needs 17 and is render-only\n(no pointer input). Custom tab bars, app bars and sheets cannot apply `tab.role`,\n`bottomAccessory` (node-only), `navigationTitle` or `sheet.sizing`; these are reported as ignored.\nWindow/theme namespace fields likewise have no applicable node and are reported as ignored.\nAndroid `indication: "ripple"|"none"` controls the node\'s clickable feedback; motion-off still wins.\nStack `predictiveBack.style` uses the existing pop for `default`, horizontal movement with fade\nfor `crossActivity` within the prototype activity, or snaps without predictive scrubbing for `none`.\nUse notes to confirm what the current renderer can apply before relying on a namespace field.',ue='## Platform features\n\nA spec reaches a platform feature through a module. Declare every module it uses in a root `requires: {platform: [<module>], permissions?: [{name, purpose}], background?: [...]}`; a call, handler or read of an undeclared module fails validation with a path, and a call\'s permissions must appear in `requires.permissions`. The host reads `requires` before show and refuses a device that lacks a module\'s capability, before anything is drawn.\n\n- **Call.** `{type:"platform", call:"<module>.<op>", args, into?, statusKey?, timeoutMs?, onSuccess?, onError?}` in any action list. `args` are strict per call; values are `{value}`, `{state}`, `{data}`, `{platform}`, `{item}`/`{index}` in a repeat, or `{event}` in a handler. `into` (a `/data` path) and `statusKey` (a declared string state key that moves loading, then success or error) behave as for `request`. A failure writes one of unsupported, unavailable, permission_denied, invalid_args, cancelled, timeout, too_many_in_flight or platform_error to `<into>__meta.error`. Each finished call is an emit event `prototype_platform` (`{callId, call, outcome, error?, durationMs}`).\n- **Handler.** A root `platform: {on: {"<module>.<event>": [actions]}}`. Inside a handler only, `{event: "/pointer"}` reads the event\'s data. Handlers run atomically like a tap and may use `push`, `pop`, data actions and further calls. A delivered event is an emit event `prototype_platform_event` (`{event, data, truncated?}`).\n- **Read.** `/platform/<module>/<key>` is device-owned, read-only JSON: `{platform:/device/os}` in text, `{platform: "/device/widthClass", equals: "expanded"}` as a condition, `{platform: pointer}` as a value. It is not patchable or stored and is not part of the `/data` revision log.\n- **Names.** Authored emit names cannot start with `prototype_`.\n\nEach module needs `prototype_platform_v1` plus its own capability; a device without either refuses show, patch and `replaceNode` and names the capability. The table below is generated from the contract, so it lists every module, call, event and key a spec may name; a device can run only the modules whose capabilities it advertises:\n',me=`Example: a podcast posts a "new episode" notification, and tapping it deep-links to the episode through a handler \`push\`.
2
+ import{W,c,cr,TE,kE,JO,Is,Lf,Et,IE,by,IK,Yu,ka,Wae,rM}from"./chunk-w5yypt53.js";import"./chunk-r0vp315h.js";import"./chunk-5wk1t46v.js";import{bu,Zh,Su,qV,VV,EC,xv,cL,j5,K5,IC,G5,dL,WV,Y5,J5}from"./chunk-7k7gjg2t.js";import{Qe,Ab}from"./chunk-sfqe223f.js";import{cm,dm,jG,Jf,Ey,uS,pS,Iy,rI}from"./chunk-r4j43b9s.js";import{xC,uL,kT}from"./chunk-9q265er0.js";import{t}from"./chunk-2b39zyxc.js";var ie='## Destinations of device requests\n\nA `request` action with `via: "device"` is checked on the device before it connects. It is refused\nwith `blocked_destination` when the URL names `localhost` (or a `*.localhost` name), or when the\ndestination resolves to a loopback, unspecified or link-local address. On Android this also covers\nthe emulator\'s addresses for the host machine (`10.0.2.2` and the other `10.0.2.x` aliases), and\nports 3000-3010 and 8765 on a private address are refused too. No `adb reverse` is made for a\nprototype, so a device request cannot reach a server on the host machine through `localhost`: use\n`via: "host"` for a host-local server, or call it by an address the device can reach that is not\nloopback. On iOS, call a Mac-local service by the Mac\'s LAN address.\n\n## Mock backends (no server code)\n\nBuild a backend-driven prototype without running a server. `defineBackend {backendId, backend}`\nregisters an in-process backend for this daemon session (`backendId` is lowercase letters, digits\nand hyphens); a `request` with `via: "host"` to `https://<backendId>.mock.automobile.invalid/<path>`\nis answered by it, with no network, DNS lookup or egress policy. The host must be connected, so it\ndoes not apply to `via: "device"` requests or saved prototypes. `backendLog {backendId, since?}` lists the requests it\nanswered and `clearBackend {backendId}` removes it; defining the same id again replaces it and resets\nits data. Requests to a backend that is not defined fail with `network`.\n\n```json\n{"action":"defineBackend","backendId":"shop","backend":{\n "collections":{"products":[{"id":1,"title":"Mug","price":12},{"id":2,"title":"Tote","price":20}]},\n "routes":[{"method":"GET","path":"/greeting/:name","body":{"text":"Hello {params.name}"}}],\n "latencyMs":{"min":200,"max":600},"faults":[{"path":"/products","method":"POST","status":500,"every":3}],\n "seed":7}}\n```\n\nA `show` can define backends itself: `mock` is `{backendId: definition}` (the same definition shape),\nor `mockPath` names an absolute local JSON file holding that object (up to 2 MiB; the path never\nreaches the device). They are defined, all or none, once the spec is accepted and before it is\nsent, replacing backends of the same id, and a saved prototype does not carry them.\n\n- **Collections** are REST over `{name: [items]}`; every item needs an `id` (a number, or a non-empty\n string other than `.` and `..`). `GET /products?title=Mug`, `q=mug` (case-insensitive substring over\n string and number members), `_sort=price` or `-price`, `_page` and `_limit` (an\n `x-total-count` header reports the total), or `after=<id>&size=<n>` for `{items, next}` cursor paging.\n `GET /products/1`, `POST` (201; the id is generated when absent, 409 if it exists), `PATCH` (merge),\n `PUT` (replace) and `DELETE`. Writes persist for the session; a backend holds at most 1 MiB.\n- **Routes** are canned responses that win over collections. Paths are literal segments and\n `:name` parameters. A body string that is exactly `{params.x}`, `{query.x}` or `{body:/pointer}`\n takes that value\'s type; inside longer text it is inserted as text; an unbound one is `null` or empty.\n- **Latency and faults** replay identically for the same `seed`: `latencyMs {min,max}` delays every\n answer, and a fault `{path, method?, status, every | rate}` fails every nth matching request or\n that fraction of them with `{error:"mock fault"}`.\n- **Actors** play the other side (at most 16, each up to 64 steps). An actor fires on a matching\n successful request (`on: {route: "POST /messages/:id"}`) or every N ms (`every` with `maxRuns`),\n after an optional `afterMs` lead, and runs `do` in order: `{append: collection, value}`,\n `{update: collection, id, value}` (merge), `{remove: collection, id}`, each with an optional\n `afterMs` after the previous step. `value` and `id` bind `{params.x}`, `{query.x}` and\n `{body:/pointer}` from the triggering request. A failed step is skipped and the run goes on;\n redefining or clearing the backend cancels pending steps. Poll the collection to see the result.\n `publish`, `advance` and `notify` steps are not supported yet.\n- A request\'s `timeoutMs` applies to the mock too, and a response over the 262144-byte cap fails\n with `too_large`.\n- **Rooms** serve realtime channels with `via: "host"`: a WebSocket to\n `wss://<backendId>.mock.automobile.invalid/rooms/general` or an SSE stream to the `https://` form\n joins the first room whose `path` (`/rooms/:roomId`) matches; no match is `handshake_failed`\n with status 404. A room `{path, onConnect?, echo?, relay?, timeline?}` sends its `onConnect` frames\n to each new connection, `timeline [{afterMs, frame}]` frames that long after each connection\n opened, returns a send to its sender with `echo`, and passes it to the room\'s other connections\n with `relay`. Frames bind `{params.x}` and `{query.x}` from the channel URL; a string frame is sent\n as written, other JSON as JSON text.\n- **Broadcasts** `{collection: {room, frame, on?}}` send a successful collection write (`POST` by\n default; `on` takes `POST`, `PUT`, `PATCH`) to a room. `{item}` and `{item:/pointer}` bind the\n stored item, so `{"messages":{"room":"/rooms/{item:/roomId}","frame":{"message":"{item}"}}}` makes a\n message one device posts appear on every device in that room.\n- `publish {backendId, room, frame}` speaks as the other side to every connection in `room` and\n returns `delivered`. `backendLog` lists room frames (`{room, from, data}`, `from` is `client`,\n `publish` or `broadcast`) beside requests. Redefining or clearing a backend closes its channels\n with code 1001.\n\n## Value formatters (#14227)\n\nUse top-level `derive: {priceText: {of: {data: "/price"}, format: {currency: {code: "USD"}}}}`\nand read `{derived:priceText}` in text node text, contentDescription, rich-text spans,\nbutton/FAB labels, topAppBar title, badge text, dialog title/text, and snackbar text.\nOther text-kind fields (request url/headers/body, check messages, and\nselect/segmented/menu/searchBar labels) validate unknown names but show tokens literally on devices.\nThere are at most 32 names (state-key syntax); unknown reads fail validation.\n`of` chooses exactly one data pointer, state key or literal `{value: number|string}`.\nInside a repeat, `repeat.derive` supports at most 8 names, with `of: {item: "field"}`\nor a literal value; the resulting strings bind as `{item.priceText}` using your alias.\nNames cannot collide with original row fields; derived fields cannot reference each other.\nHost validation checks field references; device-dependent key and emit-name value checks wait for the renderer.\n\n`format` has exactly one key with an object: `number {minFraction?,maxFraction?,grouping?}`,\n`compact {style?:short|long}`, `currency {code:USD}`, `percent {minFraction?,maxFraction?}`,\n`date {style?:short|medium|long}`, `time {style?:short|medium|long}`, or\n`relative {style?:narrow|short|long}`. Fraction digits are integers 0..20;\nmin cannot exceed max. Grouping true/default respects locale minimum grouping digits;\nfalse disables grouping entirely. Compact, currency, and percent respect locale minimum grouping digits.\nCompact defaults short, date medium,\ntime short, relative long. Currency codes are three uppercase letters and use CLDR minor units.\nNumber kinds require finite numbers; percent inputs are ratios (0.256 means 25.6%).\nDate/time/relative accept integer epoch milliseconds, YYYY-MM-DD (midnight UTC),\nor ISO date-times with seconds and Z or an explicit +hh:mm offset; fractional seconds work.\nInvalid or missing inputs display empty text. Optional entry `locale` (BCP 47, <=35 characters)\nand `timeZone` (IANA id, <=128) override device defaults.\n\nRelative time uses an injected clock snapshot, truncates toward zero, and picks the largest\nelapsed unit: seconds, minutes (60 s), hours (60 min), days (24 h), months (30 d), years (365 d).\nUnder one second uses localized now. Exact English fixtures normalize U+202F/U+00A0 to spaces;\nforeign currency symbols, narrow relative text, now wording and non-English CLDR data can vary\nby engine and use structural comparisons. Set locale, zone and clock for deterministic previews.\nBoth derive maps require `prototype_format_v1`; use literal strings with older renderers.\n`dateTime`, `duration`, `plural`, and `fileSize` are unsupported follow-ups.\n',de='### Timers\n\nTimer actions and `onAppear` require `prototype_timers_v1`.\nThe root and nodes declaring `visibleWhen` may set `onAppear` to 1..32 ordinary actions.\nThey run on first show if visible, and on each hidden-to-visible edge; visible-to-visible\nand a same-id show without reset do not run them again. Repeat placeholders bind as in onTap.\nEach list is atomic, validates like a tap, and emits change for a net state change, without\nrestarting idle TTL. A handler runs at most once per interaction, bounded by the action-depth limit.\nFor a splash, put `onAppear:[{type:"startTimer",id:"splash",afterMs:2000,\nactions:[{type:"setState",key:"loading",value:false}]}]` on the root and use\n`visibleWhen:{key:"loading",equals:false}` on content. Hiding a node does not cancel its timer.\nStart a timer from any action list:\n`{type:"startTimer",id:"tick",afterMs:100,everyMs:1000,maxRuns:10,actions:[{type:"emit",name:"tick"}]}`.\n`afterMs` is required; it and optional `everyMs` are integers 100..3600000 ms.\n`maxRuns` is 1..10000 total fires, including the first, and requires `everyMs`.\nWithout maxRuns, repeats continue until cancelled or dismissed. Actions contain 1..32 entries,\nwith the same validation and nesting limit as request follow-ups. IDs are literal strings of\n1..64 characters, never repeat-bound; at most eight distinct startTimer ids per spec.\n`{type:"cancelTimer",id:"tick"}` removes a timer; unknown ids are no-ops. The host warns\non show and patch if no startTimer declares the cancelled id; devices do not validate that advisory.\n\nStarting a live id replaces its schedule and resets runs. Each fire is one atomic interaction:\ninvalid planned writes drop the whole fire but count the run; a net state change emits one change.\nSelf-cancellation stops repeats, and a replacement started during a fire wins. Repeats schedule\nfrom planned fire time without drift. Timers pause while hidden or inactive, retaining remaining\ntime (screenshot capture does not pause); dismiss, teardown and any replacing show cancel them.\nPatch preserves them. Fires never restart idle TTL.\n\n`prototype {action:"simulate",id,advanceTimeMs:1000}` advances without waiting (integer\n1..3600000), requiring the same capability. It returns `fired`; paused timers fire zero times.\nDue timers run chronologically, ties by start order, including timers started during advancement.\nExactly 10000 due fires succeed. If another fire is still due, advance stops at the last fire with\nan error and fired=10000, keeping pending timers for the next call. Real scheduling continues from the shifted clock.\nInspect reports `timers:[{id,nextFireInMs,runs}]`, ordered by remaining time then start order;\nremaining time is nonnegative and frozen while paused. A capable device yields an empty array\nwhen no timers are live.\n',le='## Strings and semantics\n\nTop-level `strings` requires `prototype_strings_v1`: `{default: {title: "Cart", items: {one: "{#} item", other: "{#} items"}}, fr: {title: "Panier"}}`.\nUse `{t:title}` or `{t:items|2}` in display text, spans, semantics.label or semantics.hint.\nCounts are non-negative integer literals (1\u20139 digits) or `{data:/pointer}`; state-key counts are unsupported.\nPipeline: data substitution, then translation, then state binding. Translation values may contain state bindings; translated text is never scanned again for translations. `{#}` inserts the decimal count in a plural form. Invalid runtime counts use other.\nshow `locale` (locale tag, max 35 characters) overrides device preferred locales for that show; omitted follows device locales, which re-resolve live.\nEach requested locale tries its full tag then shorter prefixes, deduplicated, then default. Tags match case-insensitively with underscores normalized to hyphens. Lookup is per key; the supplying table language decides plurals (default uses the first requested language, else en).\nInteger plural groups: ja/zh/ko/vi/th/id/ms/lo/km/my use other; fr/pt/hi/bn/fa/gu/kn/am use one for 0\u20131; ru/uk/be and pl use one/few/many; cs/sk use one/few/other; ar uses zero/one/two/few/many/other; remaining languages use one at 1, other otherwise. Missing forms use other; million-multiple Romance many is outside v1.\nLimits: 16 tables, 512 keys per table, 2048 characters per nonempty value; default and every plural other form are required. Keys use letters/underscore followed by letters/digits/underscore/dot/hyphen, at most 64 characters. Values cannot contain translation, data or platform placeholders.\nWith an explicit locale, `platformNotes` construct `strings.fallback` (platform all) lists up to 20 referenced keys reaching default. Advisory lint reports missing-translation and unknown-translation-key.\nPseudo-localisation transforms each table value and plural form while preserving keys, tags, category names and bindings; strings is opaque to node walking. Node translation placeholders stay intact; semantics label and hint are display text.\n\nEvery node accepts `semantics {heading?,liveRegion?: "polite"|"assertive",group?,label?,hint?,traits?: ["button"|"image"|"selected"]}`, requiring `prototype_semantics_v1`. At least one field is required; traits are unique (1\u20133). Label/hint are nonempty and accept translations, data and state bindings. Label conflicts with contentDescription.\nHeading enables screen-reader navigation; empty-heading lint flags headings without text/label. Group contains child traversal. Android maps heading/liveRegion/traversal group, label to contentDescription, hint to onClickLabel for tap actions else stateDescription, and traits to Role.Button/Role.Image/selected. iOS maps header/button/image/selected traits, contain grouping, label/hint, and live-region change announcements (not initial render; assertive has high speech priority).\nAndroid observe exposes heading as the string "true" and live-region as polite/assertive; iOS snapshots currently expose no SwiftUI traits. Logical RTL swipe sides are outside this capability.\n',ce='## User-testing sessions\n\nCall `prototype {action:"testSessionBegin", id, goal?, cohort?}` after showing a prototype.\nThe host records accepted events without consuming `awaitEvent`. A goal is a screen label\nsuch as `checkout/done`; reaching it marks completion. Cohort is a free label, with no\ncross-session aggregation. `testSessionEnd {id}` stops recording and returns JSON plus\nMarkdown. Dismissal or session release also ends recording. `testSessionReport {id}`\nreads the latest session for this scope, or pass `testSessionId` for a stored one.\nOnly one session may be active per device and prototype.\n\nReports include screen visits, time, abandonment, validation failures and pager changes.\nLabels use the deepest stack\'s top screen as `stack/screen` (ties use the lowest stack id),\nor `(root)`. Text values, payloads, context and state snapshots are never stored;\nnon-secure changes retain only keys, string lengths or value types and validity.\nSecure fields are excluded. Up to 5,000 sanitized events are retained per session; extra\nevents are counted as dropped and the report is marked truncated. The newest 50 ended\nsessions are kept per device/prototype.\n\nTo record dead taps, show with `window.deadTaps: true` on a device advertising\n`prototype_dead_tap_v1`; an older agent is refused. Dead taps are single-pointer taps\ninside the prototype window that no interactive node handled, excluding drags, scrolling\nand app taps outside the window. Their payload has only screen ids and coordinates,\nrounded to one decimal in window-local dp (points on iOS). Reports include counts,\nup to 200 points per screen and a 48-unit grid. There is no screenshot overlay yet.\n\nStack transition patterns: `sharedAxisX`, `sharedAxisY`, `fadeThrough` and `zoom` (and a push/replace\n`source`) require `prototype_stack_patterns_v1`; zoom needs a source naming exactly one shared key inside the target stack\'s screens.\nWith `prototype_zoom_transition_v1`, zoom morphs source-to-root on Android and uses native\ninteractive zoom on iOS 18+. Older agents keep fadeThrough (Android) or platform push (iOS 17);\nplatformNotes reports the fallback. The new capability reports rendering, not spec acceptance.\nReduced motion crossfades and motion off snaps; composed iOS patterns pop non-interactively.\n',Y='### Data actions\n\nA tap can change `/data` on the device without the agent answering it. Data actions write a\nmember of `data`; each `path` is `/data` plus a non-empty data pointer (the same syntax and\n`MAX_PROTOTYPE_DATA_POINTER_LENGTH` as `{data: pointer}`), so the whole document cannot be\nwritten:\n\n- `{type: "setData", path, value}` creates or replaces an object member, or replaces an existing\n array element. A missing parent is an error.\n- `{type: "appendData", path, value}` appends to the existing array at `path`.\n- `{type: "prependData", path, value}` inserts one row at the start; list maxItems trims from the end.\n- `{type: "removeData", path}` removes an existing member or element; later elements shift down.\n\n`value` uses the emit.context binding forms except `page`: `{value: json}` is the literal,\n`{state: key}` reads state, `{data: pointer}` reads data, and `{item: field}` and\n`{index: true}` read the repeat row (repeat only). A written value must be data: strings, booleans,\nfinite numbers, arrays and objects, without null. Inside a repeat, a whole `{index}` segment of\n`path`, as in `/data/cart/{index}`, is bound to the current row; `{index}` anywhere else, or\n`{alias.field}`, is rejected.\n\nThe host validates pointer syntax and `{index}` use, rejects a literal value data cannot hold, and\nrejects an `appendData` or `prependData` whose target resolves in the spec\'s own `data` to something other than an\narray. A target that only exists after earlier writes is checked by the device. A write that would\nleave `data` over `MAX_PROTOTYPE_DATA_WRITE_BYTES` (or over a depth or array limit), or whose\ntarget is missing or not an array, changes nothing.\nActions run in order, so a later action sees an earlier write.\n\nEach write is a `change` event with `name` the written `/data/...` path and `payload`\n`{op: "set"|"append"|"remove", value?, revision}`; `awaitEvent` with `kind: "change"` returns it.\n`revision` is the device revision after the write, the same counter as a patch result\'s. The host\napplies each write to its own copy of the spec and to `status` data, so both stay current. The\nagent\'s `/data` patches and a device\'s writes never silently lose each other: once the device\nhas written a prototype\'s data, a patch with `/data` ops needs `baseRevision` (the latest\n`revision` the agent has seen) and is refused with `conflict: {revision, state, data}` when a write\nnewer than that revision touched an overlapping path (`data` lists those paths). A `show` makes its\nown `data` authoritative again and clears the history.\n\nData actions require `prototype_data_actions_v1`. A show or patch result that contains one is\nrefused on a device without it, before anything is shown or sent.\n\n\n### Realtime channels\n\nThe validated contract requires `prototype_platform_realtime_v1`; current Android and iOS\nruntimes do not advertise it. A root `channels` map holds at most four named channels and\nrequires `requires.platform:["realtime"]`. Each channel declares `kind:"websocket"|"sse"`\nand `url` (ws/wss or http/https respectively), using request URL placeholders and length rules.\nOptional fields: `protocols` (WebSocket only, up to four), `headers` (up to 16, request rules\nplus refusing Sec-WebSocket-* and Last-Event-ID), `via:"host"|"device"` (host default),\n`connect:"onShow"|"manual"` (onShow default), declared string `statusKey`,\n`decode:"json"|"text"` (json default), `reconnect`, `routes`, `onOpen/onClose/onError`.\nDevice routing additionally requires `prototype_platform_realtime_device_v1`.\nReconnect fields are integer `maxAttempts:0..20`, `initialDelayMs>=100`,\n`maxDelayMs<=60000`, with initial <= max. Up to 16 routes use `when:{pointer,equals}`,\n`{pointer,exists:boolean}`, `{event:SSE-name}`, or omit when. Their actions are state/data\nwrites; request and navigation are refused. In channel callbacks and platform handlers,\n`{event:"/message"}` reads decoded event data and `{event:""}` reads the whole event.\nCalls use `{type:"platform",call:"realtime.connect"|"realtime.disconnect",args:{channel:"chat"}}`\nand `realtime.send` with `args:{channel:"chat",body:{value:json}}` (WebSocket only).\nRead live counters with `{platform:"/realtime/chat/framesIn"}`; state keys are status, attempt,\nframesIn, framesDropped, closeCode, closeReason and error. Events are realtime.status and realtime.frame.\nThe same capability gates `appendData.maxItems` and `upsertData {path,key,value,maxItems?}`.\nUpsert replaces the first row whose key pointer matches the incoming value, otherwise appends;\nmissing incoming identity is an error. maxItems is 1..2000 and drops oldest rows before limits apply.\nFrame/send budgets are 65536/16384 bytes, with eight open channels per device and a 50 ms flush budget.\nTransport, lifecycle and routing execution remain unavailable. Flush-window coalescing is an open\ndecision; no coalescing behavior is implemented.\n',pe="\n### Adaptive panes and navigation\n\n`adaptive` needs `prototype_adaptive_v1` and a closed `kind`:\n- `listDetail`: `list` and `detail` node slots, `selectionKey` naming a declared string state key. Empty string means no selection; selecting pushes detail in compact layout and Back clears the key.\n- `navigationSuite`: 1\u20138 unique `items: [{value,label}]`, `stateKey` naming a declared string whose value matches an item, and a `content` node slot. Item taps write that value. Use conditions on the key to choose content.\n- `supporting`: `main` and `supporting` node slots, `openKey` naming a declared boolean. Close clears the key.\n\nWidths are native per platform. Android Material adaptive scaffolds split panes at expanded and use bar/rail/drawer at compact/medium/expanded. iOS horizontalSizeClass regular uses split/sidebar/inspector, compact uses stack/bottom bar/sheet. There is no shared breakpoint or `minWidth`; `platformNotes` reports the different rules on show. iOS draws Material-styled containers under every design until native platform containers are available.\n",he='## Platform namespaces\n\nChoose styling in this order:\n1. Portable node/window fields express behavior on both platforms: use `button.variant: "outlined"`\n for an outlined button, or `style.cornerRadius` for an authored corner.\n2. Portable theme tokens express a shared visual system: set `theme.colors.primary` once rather\n than repeating a color on each button; use theme typography and corner tokens for shared styles.\n3. `theme.design: "platform"` chooses native platform treatment for covered controls (the default);\n `"material"` keeps the shared Material appearance. Check design fallback notes for custom controls.\n4. Use `ios`/`android` only for semantics with no portable meaning, such as\n `ios: {buttonStyle: "glass"}` or `android: {predictiveBack: {style: "none"}}` on a stack.\n\nNamespaces are closed objects on nodes, window and theme; each field has a contract `why`.\nOwn-platform blocks require `prototype_platform_namespace_v1`, except the four node glass fields,\nwhich require only `prototype_glass_v1` on iOS; mixing another iOS key requires both.\nForeign blocks are accepted and\nignored with paths in `platformNotes`; unknown fields are rejected even in foreign blocks.\niOS glass buttons need 26 and otherwise use bordered buttons; hover needs 17 and is render-only\n(no pointer input). Custom tab bars, app bars and sheets cannot apply `tab.role`,\n`bottomAccessory` (node-only), `navigationTitle` or `sheet.sizing`; these are reported as ignored.\nWindow/theme namespace fields likewise have no applicable node and are reported as ignored.\nAndroid `indication: "ripple"|"none"` controls the node\'s clickable feedback; motion-off still wins.\nStack `predictiveBack.style` uses the existing pop for `default`, horizontal movement with fade\nfor `crossActivity` within the prototype activity, or snaps without predictive scrubbing for `none`.\nUse notes to confirm what the current renderer can apply before relying on a namespace field.',ue='## Platform features\n\nA spec reaches a platform feature through a module. Declare every module it uses in a root `requires: {platform: [<module>], permissions?: [{name, purpose}], background?: [...]}`; a call, handler or read of an undeclared module fails validation with a path, and a call\'s permissions must appear in `requires.permissions`. The host reads `requires` before show and refuses a device that lacks a module\'s capability, before anything is drawn.\n\n- **Call.** `{type:"platform", call:"<module>.<op>", args, into?, statusKey?, timeoutMs?, onSuccess?, onError?}` in any action list. `args` are strict per call; values are `{value}`, `{state}`, `{data}`, `{platform}`, `{item}`/`{index}` in a repeat, or `{event}` in a handler. `into` (a `/data` path) and `statusKey` (a declared string state key that moves loading, then success or error) behave as for `request`. A failure writes one of unsupported, unavailable, permission_denied, invalid_args, cancelled, timeout, too_many_in_flight or platform_error to `<into>__meta.error`. Each finished call is an emit event `prototype_platform` (`{callId, call, outcome, error?, durationMs}`).\n- **Handler.** A root `platform: {on: {"<module>.<event>": [actions]}}`. Inside a handler only, `{event: "/pointer"}` reads the event\'s data. Handlers run atomically like a tap and may use `push`, `pop`, data actions and further calls. A delivered event is an emit event `prototype_platform_event` (`{event, data, truncated?}`).\n- **Read.** `/platform/<module>/<key>` is device-owned, read-only JSON: `{platform:/device/os}` in text, `{platform: "/device/widthClass", equals: "expanded"}` as a condition, `{platform: pointer}` as a value. It is not patchable or stored and is not part of the `/data` revision log.\n- **Names.** Authored emit names cannot start with `prototype_`.\n\nEach module needs `prototype_platform_v1` plus its own capability; a device without either refuses show, patch and `replaceNode` and names the capability. The table below is generated from the contract, so it lists every module, call, event and key a spec may name; a device can run only the modules whose capabilities it advertises:\n',me=`Example: a podcast posts a "new episode" notification, and tapping it deep-links to the episode through a handler \`push\`.
3
3
 
4
4
  \`\`\`json
5
5
  {
@@ -391,7 +391,7 @@ support it yet.
391
391
 
392
392
  \`list {repeat:{items:{path},as,key},children:[row],axis?,header?,footer?,spacing?,contentPadding?,onEndReached?,endThreshold?,refresh?,maxItems?}\`
393
393
  is a lazy scrolling collection (needs \`prototype_lazy_list_v1\` and \`prototype_data_model_v1\`).
394
- The path resolves to at most ${FO} objects; \`key\` is required, projected and unique. Empty arrays
394
+ The path resolves to at most ${rM} objects; \`key\` is required, projected and unique. Empty arrays
395
395
  are valid. Axis defaults to vertical; spacing/padding are nonnegative dp. One row template and
396
396
  optional header/footer each count once toward node/image limits, while per-item binding and text
397
397
  budgets still apply. Header/footer are ordinary nodes outside the row scope. Never nest lists,
@@ -401,7 +401,7 @@ Only composed rows are in \`observe\`; use \`swipeOn\` to reach more. Row keys p
401
401
  per \`(rowCount,lastRowKey)\` signature; empty/no-change pages never refire. Signature state survives
402
402
  recomposition and resets on show. \`endThreshold\` needs \`onEndReached\`. Vertical \`refresh\` uses
403
403
  scroll.refresh semantics and a declared boolean key (also needs \`prototype_refresh_v1\`).
404
- \`maxItems\` (1\u2013${FO}) requires initial data to fit; appending trims oldest rows atomically before
404
+ \`maxItems\` (1\u2013${rM}) requires initial data to fit; appending trims oldest rows atomically before
405
405
  final data limits. The smallest cap among lists sharing the exact data path wins.
406
406
  \`request.append:{from,to}\` (same lazy-list capability) extracts a response array from an object
407
407
  member at \`from\`, extends the existing \`/data/...\` array \`to\`, and writes the remaining response
@@ -421,7 +421,7 @@ until the acceptance run; benchmark qualification remains unverified.
421
421
 
422
422
  \`{type: "chart", variant, series, yRange?, xLabels?, goal?}\` draws a \`line\`, \`area\`, \`bar\` or
423
423
  \`ring\` chart (needs \`prototype_chart_v1\`). \`series\` holds 1-4 \`{path, x?, y, label, color?}\`
424
- entries: \`path\` is a \`/data\` pointer to an array of objects (empty is valid, at most ${lae} items as
424
+ entries: \`path\` is a \`/data\` pointer to an array of objects (empty is valid, at most ${Wae} items as
425
425
  for any data array), \`y\` names each point's numeric member and \`x\` its optional label member;
426
426
  \`color\` is a hex or theme role, or a \`{light, dark}\` pair. Points are drawn in array order, so
427
427
  \`appendData\`, a \`request\` \`into\` or a \`/data\` patch re-renders the chart. \`yRange {min, max}\`
@@ -797,7 +797,7 @@ Data actions). Any state op, including \`test\`, requires \`baseRevision\`. A de
797
797
  must refuse the whole patch with \`conflict\` if the user changed a touched state
798
798
  key since that revision, returning the current revision and those keys' values.
799
799
 
800
- There are at most ${pE} ops, the frame is capped at ${wa/1048576} MiB, and the resulting whole
800
+ There are at most ${IE} ops, the frame is capped at ${ka/1048576} MiB, and the resulting whole
801
801
  spec must satisfy every spec/data limit, including node count and depth. The
802
802
  host tracks a cloned copy of each id's device spec from its last acknowledged show,
803
803
  with components expanded and anchors resolved. It applies ops to a copy and
@@ -817,21 +817,21 @@ patch dispatch is indeterminate and requires a new show. An atomic device
817
817
  refusal leaves the tracked copy unchanged. A successful patch refreshes shown
818
818
  status data without changing \`lastResult\`, timestamps or event snapshots.
819
819
  `,V='## Checks\n\nDevices advertising `prototype_checks_v1` evaluate declarative checks locally. The host refuses\nshow and patch results using this feature on older devices. Inputs `textField`, `slider`,\n`checkbox`, `switch`, `radioGroup`, `datePicker` and `timePicker` accept `checks` (1\u20138 entries)\nand `validateOn: "change" | "blur" | "submit"` (omitted means blur). Each check contains a\nnonempty `message` and exactly one rule. `validateOn` controls error display, never validity.\nThe first failing message is displayed.\n\n- `required: true`: trimmed nonempty text, true for checkbox/switch, nonempty selection for\n radioGroup/datePicker, present number for slider/timePicker.\n- `minLength` / `maxLength`: nonnegative integer UTF-16 lengths, textField only.\n- `min` / `max`: finite numeric inclusive bounds, slider and pickers only. Text fields hold\n strings and have no numeric subtype. Date state remains YYYY-MM-DD; bounds are UTC epoch\n milliseconds. Time pickers retain hourKey/minuteKey; checks apply independently to both keys.\n- `format`: textField only, one of email, phone, digits, decimal, url.\n- `pattern`: textField only, the restricted grammar below, at most 128 UTF-16 units.\n\nEmpty string, null or missing value passes all rules except required. Whitespace is only empty\nfor required. Pattern and format matching use at most 4096 UTF-16 units. Required and length checks inspect the complete value.\nEvery validity reference must name declared state with at least one check. Multiple checked\ncontrols sharing a key must all pass. Numeric/length lower bounds must not exceed upper bounds.\n\nFormats use these exact whole-value expressions (ASCII digits):\n\n- email: `[^\\s@]+@[^\\s@]+\\.[^\\s@]+` (Unicode names allowed).\n- phone: optional leading +, then 7\u201315 characters from ASCII digits, space, hyphen and parentheses;\n at least seven digits. The leading + is outside the character count. Punctuation need not balance.\n- digits: `[0-9]+`.\n- decimal: `-?[0-9]+(\\.[0-9]+)?` (no exponent or leading +).\n- url: `https?://[^\\s/?#]+([/?#]\\S*)?` (lowercase scheme, Unicode hosts allowed).\n\nFormat whitespace follows JavaScript whitespace (including NBSP); no format trims its value.\nPatterns use ASCII `\\d`, `\\w`, `\\s` and their complements `\\D`, `\\W`, `\\S`; `\\w` is\n`[A-Za-z0-9_]` and `\\s` is space, tab, CR, LF, FF, VT. Dot matches one Unicode scalar except\nCR, LF, U+2028 and U+2029. Matching consumes the entire value, even with a final newline.\nAllowed: literals, dot, classes `[abc]` / `[^abc]`, literal ranges, those six shorthand escapes,\nescaped metacharacters, anchors ^ $, groups `(\u2026)` / `(?:\u2026)`, alternation |, and quantifiers\n`? * + {n} {n,} {n,m}`. Every written bound is <=100 and n<=m; open upper bounds are unbounded.\nReject backreferences, lookahead/behind, named groups, lazy/possessive quantifiers, Unicode\nproperties, inline flags, word boundaries `\\b` / `\\B`, and every other escape (including\n`\\n`, `\\x`, `\\u`; use literal characters). Empty classes are rejected. A group repeated more\nthan once cannot contain alternation or any variable quantifier (maximum > minimum), transitively.\nAt most two variable quantifiers are allowed in the entire pattern: ? * + {n,} and {n,m} when\nm>n. Fixed {n} quantifiers inside repeated groups remain allowed.\nThus `(a|b)+`, `(a+)+`, `(a*)*` and `((ab)+)+` are rejected. Optional groups remain allowed.\nThese exact rules and error substrings are pinned in the language-neutral checks fixtures.\n\nConditions `{valid: "email"}` and `{valid: ["email", "password"]}` mean all referenced keys\nare valid, including fields whose errors are not yet shown. They compose with all/any/not in\nvisibleWhen, styleWhen and enabledWhen. enabledWhen is supported on the seven input types plus\nbutton, iconButton, fab, chip and listItem; false disables the control and skips its onTap.\n`emit` may specify `requireValid: ["email", "password"]` (1\u20138 keys). Any failing key aborts the\nwhole action list atomically, shows errors on every listed field, and sends\n`{kind:"validation_failed", keys:[\u2026]}` naming only the failing keys. `change` events carry\n`validity: {key:boolean}` for checked keys. Both event kinds retain the usual event envelope.\n';var wt={device:{tools:["rotate","displayConfig","setPosture","windowMode"],note:"orientation, window size, posture and windowing mode update `/platform/device`; `device.changed` fires only when a size class, posture or idiom changes (on iOS, only width class or idiom), so a rotation that keeps the size class sends no event"},realtime:{tools:[],note:"the channel's server drives frames and status; no device tool"},haptics:{tools:[],note:"assert the call's `prototype_platform` outcome"},sound:{tools:[],note:"assert the call's `prototype_platform` outcome"},clipboard:{tools:["clipboard"],note:"set the clipboard before `paste`/`hasText`; read it after `copy`"},share:{tools:["observe","tapOn"],note:"drive the system share sheet"},battery:{tools:["setDeviceState"],note:"`power.battery` level and charging (Android)"},power:{tools:["setDeviceState"],note:"`power.batterySaver` and `power.thermalStatus` (Android)"},audio:{tools:["mediaSession"],note:"stream volume (Android)"},connectivity:{tools:["setDeviceState"],note:"`connectivity` toggles and `networkCondition`"},biometric:{tools:["biometricAuth","setDeviceState"],note:"match or fail a prompt; `biometrics.enrollment` on an iOS Simulator"},settings:{tools:["observe","pressButton"],note:"confirm the settings screen, then go back"},contacts:{tools:["observe","tapOn"],note:"pick in the system picker"},calendar:{tools:["observe","tapOn"],note:"save or cancel in the system editor"},links:{tools:["observe"],note:"confirm the opened app or browser"},mediaLibrary:{tools:["putAppFile","setAppPermissions"],note:"stage photos in the `media_library` domain; grant or revoke access"},camera:{tools:["setDeviceState","setAppPermissions"],note:"`cameraPoster` sets what the emulated camera sees"},auth:{tools:["observe","tapOn"],note:"complete the provider's sign-in screen"},location:{tools:["setDeviceState","setAppPermissions","systemAlerts"],note:"`location` fix or route; grant access or answer the prompt"},commerce:{tools:["observe","tapOn"],note:"complete the store purchase sheet"},secureStorage:{tools:[],note:"read `/platform/secureStorage/locked`; no device tool"},sensors:{tools:["setSensor","shake"],note:"emulator sensor values and a shake gesture"},map:{tools:["tapOn","swipeOn"],note:"tap markers and pan the map"},webview:{tools:["observe","tapOn"],note:"page content is in the accessibility tree; tap a link to navigate"},audioRecorder:{tools:["injectAudioInput","setAppPermissions"],note:"feed known audio into the microphone"},notifications:{tools:["systemTray","setAppPermissions"],note:"open, act on, reply to or dismiss a posted notification; `notificationsEnabled` blocks the whole app, while a single channel is blocked only in system Settings (`observe`, `tapOn`)"},permissions:{tools:["setAppPermissions","systemAlerts"],note:"grant or revoke before reading `/platform/permissions`; answer the prompt a `request` raises"},media:{tools:["mediaSession"],note:"headset transport keys and session state (Android)"},cast:{tools:[],note:"needs a real receiver on the network; no device tool"}};function Re(e=wt){return["To test a module for real, drive the device with these tools, then assert with `awaitEvent` and `observe`:","","| Module | Tools | What they drive |","| --- | --- | --- |",...Object.entries(e).map(([o,n])=>{let s=n.tools.map((r)=>`\`${r}\``).join(", ")||"-";return`| \`${o}\` | ${s} | ${n.note} |`})].join(`
820
- `)}var y=W.definitions;function bt(e){let o=e.min!==void 0||e.max!==void 0?` ${e.min??0}..${e.max??"n"}`:"";return`${E(e.item??{kind:"value"})}[]${o}`}function E(e){switch(e.kind){case"enum":return(e.values??[]).join("|");case"ref":return e.name??"ref";case"boundKey":return"key";case"strings":return"strings";case"screenMap":return`screen-name \u2192 node (${e.min}..${e.max} entries)`;case"array":return bt(e);case"choice":return(e.options??[]).map(E).join(" or ");default:return e.kind}}function K(e,a){return`${e}${a.optional?"":"*"}: ${E(a.rule)}`}function St(e){let a=Object.values(e),[o,...n]=a,s=new Map;for(let[r,i]of Object.entries(o?.fields??{}))if(n.every((p)=>JSON.stringify(p.fields?.[r])===JSON.stringify(i))&&r!=="type")s.set(r,i);return s}function $e(e,a){return Object.entries(e).map(([o,n])=>{let s=Object.entries(n.fields??{}).filter(([r])=>r!=="type"&&!a.has(r)).map(([r,i])=>K(r,i));return`- \`${o}\`${s.length>0?`: ${s.join(", ")}`:""}`})}var g=(e)=>e.map((a)=>`\`${a}\``).join(", ");function P(e,a){return(y[e]?.fields?.[a]?.rule.values??[]).map(String)}function xt(){let e=Object.entries(W.limits).map(([n,s])=>`| \`${n}\` | ${s} |`),a=["onTap","onLongPress","onDoubleTap","onAppear"],o=y.repeat.fields?.items.rule??{};for(let n of a){let s=y.node.variants?.box.fields?.[n].rule??{};e.push(`| actions per \`${n}\` | ${s.min}..${s.max} |`)}return e.push(`| \`repeat.items\` | ${o.min}..${o.max} |`),e.push(`| \`use\` nesting (host-expanded components) | ${xO} |`),["| Limit | Value |","| --- | --- |",...e].join(`
821
- `)}function Tt(){let e=y.node.variants??{},a=St(e);return["## Nodes","","`*` marks a required field. Fields shared by every node:","",[...a].map(([n,s])=>K(n,s)).join(", "),"",...$e(e,a),"",`Pager options require \`${lE}\` whenever any is explicitly present (including default values): \`axis\` is horizontal (default) or vertical; \`pageSpacing\` is the dp gap (default 0); \`peek: {start?, end?}\` leaves neighbouring pages visible along the main axis (default 0 on each side, \`{}\` is a no-op); \`pageSize\` is fill (default) or \`{dp}\` for fixed-size pages. Distances are finite, spacing and peek are 0..${W.limits.MAX_PROTOTYPE_PAGER_DP}, and fixed page size is 1e-6..${W.limits.MAX_PROTOTYPE_PAGER_DP} (\`MAX_PROTOTYPE_PAGER_DP\`). Objects are strict. On iOS dp means points. A vertical pager cannot have any vertical scroll ancestor, including a scroll with omitted axis; a horizontal scroll ancestor is allowed. Every visible page (including partly visible peek and fixed-size pages) is exposed to accessibility, observe and tapOn; Android marks the current page selected only for pagers with positive peek or a fixed pageSize; iOS marks it selected on the options path (a pager with any non-default option) and not on the legacy paged pager; plain pagers expose only the current page on both. Pages fully off-screen are not exposed.`,"Bar scrolling requires `prototype_bar_scroll_v1` when `onScroll` or `scroll` is explicit. `topAppBar.onScroll` defaults to pinned; enterAlways hides and reveals with scroll direction; exitUntilCollapsed collapses medium/large titles to the small height. `bottomNav` and `tabBar` default to none; hide slides away and minimize draws icons only. Set `scroll` to exactly one scroll id in the same screen for an active behavior. Hidden bars leave hit testing and observation; partial bars expose drawn bounds. Reduced motion snaps between states. iOS uses custom Material bars; native platform bars are deferred.","The current page is the first fully visible page (fixed-size pages snap at Start; fill uses the usual settled page). page_changed, {page}/{pageCount}, setPage and tabBar/bottomNav pager bindings keep their meaning. Fully off-screen retained neighbours stay outside the semantics tree; stable keys and neighbour retention are unchanged. Indicator and carousel remain deferred."].join(`
822
- `)}function _t(){let e=y.action.variants??{};return["## Actions","","Used in `onTap`, `onLongPress`, `onDoubleTap`, `onAppear` and searchBar `onSubmit` lists.","",`Gesture fields are available only on ${g(Object.entries(y.node.variants??{}).filter(([,a])=>a.fields?.onLongPress!==void 0).map(([a])=>a))}; they require \`prototype_gestures_v1\`.`,"",...$e(e,new Map)].join(`
823
- `)}function At(){let e=y.colorRole.values??[];return["## Theme and style","",`Theme: \`{${Object.keys(y.theme.fields??{}).join(", ")}}\`; mode is ${g(P("theme","mode"))}.`,"",`Material colour roles (a colour field takes a hex value or one of these): ${g(e)}.`,"",`Per-mode values, refused unless the device advertises \`${dE}\`: every colour field (style colours, \`border.color\`, gradient stops, \`window.placement.scrim\`, a bottomSheet or drawer \`scrim\`) also takes \`{light, dark}\`, each a hex value or a role, both required; gradient stops and scrims take a role name too. \`theme.colors.light\` and \`theme.colors.dark\` are \`{<role>: hex}\` maps applied after the flat role overrides for the resolved mode. \`image.asset\` and a tabBar or bottomNav item \`image\` take \`{light: assetId, dark: assetId}\`: upload both, and a pair naming two different assets counts as two images. Prefer a role name, which already follows the mode; use a pair for a brand literal that needs a hand-picked dark value.`,"",`Text style roles (\`style.textStyle\`): ${g(P("style","textStyle"))}.`,"",`Corner shapes: ${g(P("themeShapes","corner"))}. Font families: ${g(P("themeTypography","fontFamily"))}.`,"",`Style properties: ${g(Object.keys(y.style.fields??{}))}.`].join(`
824
- `)}function Pt(){return["## Icons","",`Icon names are a closed set of ${Ie().length} Material names; variants: ${g(W.definitions.iconVariant.values)}.`,`Look names up with \`${ZF}?query=<substring>\` (at most ${hT} matches per read).`].join(`
825
- `)}function Et(){return["## Embedding in an app","",`Read \`${dy}?id=<id>\` (optional \`platform=android|ios\`, \`deviceId\`) for a shown or saved prototype as app code: the spec, its assets, a Jetpack Compose or SwiftUI host file that renders it with the published renderer library, the library coordinate to add, and the capabilities the spec needs. Asset bytes for saved prototypes come from \`${Jb}?id=<id>&assetId=<asset>\`.`,"","For one file to paste instead, read `automobile:prototype/<id>/compose` (Kotlin) or `automobile:prototype/<id>/swiftui` (Swift) for a shown prototype: the same host file with the spec inlined as a string constant, a header naming the library coordinate, required capabilities and each asset's URI (assets are not inlined), and a preview sized to `frame` (default `pixel-9` / `iphone-16`). `revision` picks a kept revision as for the web page.","",`To graduate a prototype to screens the app owns, read \`${Xb}?id=<id>\` (same parameters). It returns a one-shot Compose or SwiftUI file with no renderer dependency: layout, text, buttons, text fields, lists and stack navigation become real code, every other node becomes a \`TODO(eject)\` stub that keeps its \`testTag\` and children, and \`remaining\` lists each node, action and binding left to do. It is not round-trip.`].join(`
826
- `)}function Ot(){return["## Web preview and alternatives","",`Read \`automobile:prototype/<id>/web\` for a shown prototype as one self-contained HTML page: the prototype in a device frame with rotate, zoom, a variant switcher and its revision history. Optional \`frame\` (a device frame id; default the showing platform's), \`revision\` (default the latest) and \`variant\`. Revision 1 is the first acknowledged show; each later show of the id and each landed patch adds one; device state edits do not. The last ${TG} revisions per id are kept. Subscribe to the URI without parameters to hear every new revision. The \`exportPrototypeWeb\` tool writes the same page to an HTML file and returns its path and sha256.`,"","To present design alternatives side by side, show each under its own id with `variant: {group, label}` using one `group` (host-side only; the device shows whatever was shown last). The web page of any id in the group offers every alternative by `label`. Describe them in chat and ask the user which they prefer."].join(`
820
+ `)}var y=W.definitions;function bt(e){let o=e.min!==void 0||e.max!==void 0?` ${e.min??0}..${e.max??"n"}`:"";return`${E(e.item??{kind:"value"})}[]${o}`}function E(e){switch(e.kind){case"enum":return(e.values??[]).join("|");case"ref":return e.name??"ref";case"boundKey":return"key";case"strings":return"strings";case"screenMap":return`screen-name \u2192 node (${e.min}..${e.max} entries)`;case"array":return bt(e);case"choice":return(e.options??[]).map(E).join(" or ");default:return e.kind}}function K(e,a){return`${e}${a.optional?"":"*"}: ${E(a.rule)}`}function kt(e){let a=Object.values(e),[o,...n]=a,s=new Map;for(let[r,i]of Object.entries(o?.fields??{}))if(n.every((p)=>JSON.stringify(p.fields?.[r])===JSON.stringify(i))&&r!=="type")s.set(r,i);return s}function $e(e,a){return Object.entries(e).map(([o,n])=>{let s=Object.entries(n.fields??{}).filter(([r])=>r!=="type"&&!a.has(r)).map(([r,i])=>K(r,i));return`- \`${o}\`${s.length>0?`: ${s.join(", ")}`:""}`})}var g=(e)=>e.map((a)=>`\`${a}\``).join(", ");function P(e,a){return(y[e]?.fields?.[a]?.rule.values??[]).map(String)}function St(){let e=Object.entries(W.limits).map(([n,s])=>`| \`${n}\` | ${s} |`),a=["onTap","onLongPress","onDoubleTap","onAppear"],o=y.repeat.fields?.items.rule??{};for(let n of a){let s=y.node.variants?.box.fields?.[n].rule??{};e.push(`| actions per \`${n}\` | ${s.min}..${s.max} |`)}return e.push(`| \`repeat.items\` | ${o.min}..${o.max} |`),e.push(`| \`use\` nesting (host-expanded components) | ${JO} |`),["| Limit | Value |","| --- | --- |",...e].join(`
821
+ `)}function xt(){let e=y.node.variants??{},a=kt(e);return["## Nodes","","`*` marks a required field. Fields shared by every node:","",[...a].map(([n,s])=>K(n,s)).join(", "),"",...$e(e,a),"",`Pager options require \`${kE}\` whenever any is explicitly present (including default values): \`axis\` is horizontal (default) or vertical; \`pageSpacing\` is the dp gap (default 0); \`peek: {start?, end?}\` leaves neighbouring pages visible along the main axis (default 0 on each side, \`{}\` is a no-op); \`pageSize\` is fill (default) or \`{dp}\` for fixed-size pages. Distances are finite, spacing and peek are 0..${W.limits.MAX_PROTOTYPE_PAGER_DP}, and fixed page size is 1e-6..${W.limits.MAX_PROTOTYPE_PAGER_DP} (\`MAX_PROTOTYPE_PAGER_DP\`). Objects are strict. On iOS dp means points. A vertical pager cannot have any vertical scroll ancestor, including a scroll with omitted axis; a horizontal scroll ancestor is allowed. Every visible page (including partly visible peek and fixed-size pages) is exposed to accessibility, observe and tapOn; Android marks the current page selected only for pagers with positive peek or a fixed pageSize; iOS marks it selected on the options path (a pager with any non-default option) and not on the legacy paged pager; plain pagers expose only the current page on both. Pages fully off-screen are not exposed.`,"Bar scrolling requires `prototype_bar_scroll_v1` when `onScroll` or `scroll` is explicit. `topAppBar.onScroll` defaults to pinned; enterAlways hides and reveals with scroll direction; exitUntilCollapsed collapses medium/large titles to the small height. `bottomNav` and `tabBar` default to none; hide slides away and minimize draws icons only. Set `scroll` to exactly one scroll id in the same screen for an active behavior. Hidden bars leave hit testing and observation; partial bars expose drawn bounds. Reduced motion snaps between states. iOS uses custom Material bars; native platform bars are deferred.","The current page is the first fully visible page (fixed-size pages snap at Start; fill uses the usual settled page). page_changed, {page}/{pageCount}, setPage and tabBar/bottomNav pager bindings keep their meaning. Fully off-screen retained neighbours stay outside the semantics tree; stable keys and neighbour retention are unchanged. Indicator and carousel remain deferred."].join(`
822
+ `)}function Tt(){let e=y.action.variants??{};return["## Actions","","Used in `onTap`, `onLongPress`, `onDoubleTap`, `onAppear` and searchBar `onSubmit` lists.","",`Gesture fields are available only on ${g(Object.entries(y.node.variants??{}).filter(([,a])=>a.fields?.onLongPress!==void 0).map(([a])=>a))}; they require \`prototype_gestures_v1\`.`,"",...$e(e,new Map)].join(`
823
+ `)}function _t(){let e=y.colorRole.values??[];return["## Theme and style","",`Theme: \`{${Object.keys(y.theme.fields??{}).join(", ")}}\`; mode is ${g(P("theme","mode"))}.`,"",`Material colour roles (a colour field takes a hex value or one of these): ${g(e)}.`,"",`Per-mode values, refused unless the device advertises \`${TE}\`: every colour field (style colours, \`border.color\`, gradient stops, \`window.placement.scrim\`, a bottomSheet or drawer \`scrim\`) also takes \`{light, dark}\`, each a hex value or a role, both required; gradient stops and scrims take a role name too. \`theme.colors.light\` and \`theme.colors.dark\` are \`{<role>: hex}\` maps applied after the flat role overrides for the resolved mode. \`image.asset\` and a tabBar or bottomNav item \`image\` take \`{light: assetId, dark: assetId}\`: upload both, and a pair naming two different assets counts as two images. Prefer a role name, which already follows the mode; use a pair for a brand literal that needs a hand-picked dark value.`,"",`Text style roles (\`style.textStyle\`): ${g(P("style","textStyle"))}.`,"",`Corner shapes: ${g(P("themeShapes","corner"))}. Font families: ${g(P("themeTypography","fontFamily"))}.`,"",`Style properties: ${g(Object.keys(y.style.fields??{}))}.`].join(`
824
+ `)}function At(){return["## Icons","",`Icon names are a closed set of ${Ie().length} Material names; variants: ${g(W.definitions.iconVariant.values)}.`,`Look names up with \`${uL}?query=<substring>\` (at most ${kT} matches per read).`].join(`
825
+ `)}function Pt(){return["## Embedding in an app","",`Read \`${Ey}?id=<id>\` (optional \`platform=android|ios\`, \`deviceId\`) for a shown or saved prototype as app code: the spec, its assets, a Jetpack Compose or SwiftUI host file that renders it with the published renderer library, the library coordinate to add, and the capabilities the spec needs. Asset bytes for saved prototypes come from \`${uS}?id=<id>&assetId=<asset>\`.`,"","For one file to paste instead, read `automobile:prototype/<id>/compose` (Kotlin) or `automobile:prototype/<id>/swiftui` (Swift) for a shown prototype: the same host file with the spec inlined as a string constant, a header naming the library coordinate, required capabilities and each asset's URI (assets are not inlined), and a preview sized to `frame` (default `pixel-9` / `iphone-16`). `revision` picks a kept revision as for the web page.","",`To graduate a prototype to screens the app owns, read \`${pS}?id=<id>\` (same parameters). It returns a one-shot Compose or SwiftUI file with no renderer dependency: layout, text, buttons, text fields, lists and stack navigation become real code, every other node becomes a \`TODO(eject)\` stub that keeps its \`testTag\` and children, and \`remaining\` lists each node, action and binding left to do. It is not round-trip.`].join(`
826
+ `)}function Ot(){return["## Web preview and alternatives","",`Read \`automobile:prototype/<id>/web\` for a shown prototype as one self-contained HTML page: the prototype in a device frame with rotate, zoom, a variant switcher and its revision history. Optional \`frame\` (a device frame id; default the showing platform's), \`revision\` (default the latest) and \`variant\`. Revision 1 is the first acknowledged show; each later show of the id and each landed patch adds one; device state edits do not. The last ${jG} revisions per id are kept. Subscribe to the URI without parameters to hear every new revision. The \`exportPrototypeWeb\` tool writes the same page to an HTML file and returns its path and sha256.`,"","To present design alternatives side by side, show each under its own id with `variant: {group, label}` using one `group` (host-side only; the device shows whatever was shown last). The web page of any id in the group offers every alternative by `label`. Describe them in chat and ask the user which they prefer."].join(`
827
827
  `)}function Rt(){let e=y.placement.variants??{};return["## Window","",`Placement types: ${g(Object.keys(e))}. Window fields: ${Object.entries(y.window.fields??{}).map(([a,o])=>K(a,o)).join(", ")}.`].join(`
828
- `)}function Ie(){return W.definitions.iconName.values}function Ne(e){let a=e.trim().toLowerCase();if(a==="")return{total:0,names:[]};let o=Ie().filter((n)=>n.includes(a));return{total:o.length,names:o.slice(0,hT)}}function $t(e,a){return`${e}${a.optional?"?":""}: ${E(a.value)}`}function It(e=Lu){return["| Module | Capabilities | Calls | Events | `/platform` state |","| --- | --- | --- | --- | --- |",...Object.entries(e).filter(([,o])=>o.placeholder!==!0).map(([o,n])=>{let s=Object.entries(n.calls).map(([p,u])=>{let w=Object.entries(u.args).map(([v,A])=>$t(v,A)),b=[...u.permissions.map((v)=>`permission ${v}`),...u.background.map((v)=>`background ${v}`),...u.capability?[`capability \`${u.capability}\``]:[]];return`\`${o}.${p}\`(${w.join(", ")})${b.length>0?` needs ${b.join(", ")}`:""}`}),r=Object.keys(n.events).map((p)=>`\`${o}.${p}\``),i=Object.keys(n.state??{}).map((p)=>`\`/${o}/${Nt(o,p)}${p}\``),l=[n.capability,n.deviceCapability,...Object.values(n.backends??{}).map((p)=>p.capability)].filter(Boolean);return`| \`${o}\` | ${g(l)} | ${s.join("<br>")||"-"} | ${r.join(", ")||"-"} | ${i.join(", ")||"-"} |`})].join(`
829
- `)}function Nt(e,a){if(e==="media")return"players/<id>/";return e==="notifications"&&aK.has(a)?"channels/<id>/":""}function jt(){return["## Platform differences","",`iOS draws SF Symbols: ${Object.keys(OV.mapped).length} of ${W.definitions.iconName.values.length} contract names are mapped in schemas/prototype-ios-icon-symbols.json. Unmapped names draw a placeholder symbol. Agents advertise prototype_icon_sf_symbols_v1; older agents should be relaunched with a current agent.`,'With no variant, iOS draws the name\'s default SF Symbol, which is often the outline form; pass `variant: "filled"` on an `icon` node for the closest match to Android where the table has a filled form.',"","- `icon.name`: unmapped names draw a placeholder; a successful show lists affected paths under `platformNotes`. Material parity remains a future option behind `prototype_icon_set_material_v1`.","- `icon.variant`: filled/outlined map where a form exists; rounded/sharp/twoTone and forms absent from the table draw the default symbol. A successful show lists affected paths under `platformNotes`. Material parity remains a future option behind `prototype_icon_variant_v1`.",...N5.filter(({construct:e})=>!e.startsWith("icon.")).map(({construct:e,platform:a,effect:o,capability:n,detail:s})=>`- \`${e}\`: ${a}, ${o}. ${s} A successful show lists affected paths under \`platformNotes\`${n?`; lifted by \`${n}\``:""}.`),...M5.map(({construct:e,detail:a})=>`- \`${e}\`: ${a}`)].join(`
828
+ `)}function Ie(){return W.definitions.iconName.values}function Ne(e){let a=e.trim().toLowerCase();if(a==="")return{total:0,names:[]};let o=Ie().filter((n)=>n.includes(a));return{total:o.length,names:o.slice(0,kT)}}function $t(e,a){return`${e}${a.optional?"?":""}: ${E(a.value)}`}function It(e=Yu){return["| Module | Capabilities | Calls | Events | `/platform` state |","| --- | --- | --- | --- | --- |",...Object.entries(e).filter(([,o])=>o.placeholder!==!0).map(([o,n])=>{let s=Object.entries(n.calls).map(([p,u])=>{let w=Object.entries(u.args).map(([v,A])=>$t(v,A)),b=[...u.permissions.map((v)=>`permission ${v}`),...u.background.map((v)=>`background ${v}`),...u.capability?[`capability \`${u.capability}\``]:[]];return`\`${o}.${p}\`(${w.join(", ")})${b.length>0?` needs ${b.join(", ")}`:""}`}),r=Object.keys(n.events).map((p)=>`\`${o}.${p}\``),i=Object.keys(n.state??{}).map((p)=>`\`/${o}/${Nt(o,p)}${p}\``),l=[n.capability,n.deviceCapability,...Object.values(n.backends??{}).map((p)=>p.capability)].filter(Boolean);return`| \`${o}\` | ${g(l)} | ${s.join("<br>")||"-"} | ${r.join(", ")||"-"} | ${i.join(", ")||"-"} |`})].join(`
829
+ `)}function Nt(e,a){if(e==="media")return"players/<id>/";return e==="notifications"&&IK.has(a)?"channels/<id>/":""}function jt(){return["## Platform differences","",`iOS draws SF Symbols: ${Object.keys(WV.mapped).length} of ${W.definitions.iconName.values.length} contract names are mapped in schemas/prototype-ios-icon-symbols.json. Unmapped names draw a placeholder symbol. Agents advertise prototype_icon_sf_symbols_v1; older agents should be relaunched with a current agent.`,'With no variant, iOS draws the name\'s default SF Symbol, which is often the outline form; pass `variant: "filled"` on an `icon` node for the closest match to Android where the table has a filled form.',"","- `icon.name`: unmapped names draw a placeholder; a successful show lists affected paths under `platformNotes`. Material parity remains a future option behind `prototype_icon_set_material_v1`.","- `icon.variant`: filled/outlined map where a form exists; rounded/sharp/twoTone and forms absent from the table draw the default symbol. A successful show lists affected paths under `platformNotes`. Material parity remains a future option behind `prototype_icon_variant_v1`.",...J5.filter(({construct:e})=>!e.startsWith("icon.")).map(({construct:e,platform:a,effect:o,capability:n,detail:s})=>`- \`${e}\`: ${a}, ${o}. ${s} A successful show lists affected paths under \`platformNotes\`${n?`; lifted by \`${n}\``:""}.`),...Y5.map(({construct:e,detail:a})=>`- \`${e}\`: ${a}`)].join(`
830
830
  `)}function je(){return[we,le,H,`## Limits
831
831
 
832
832
  Generated from \`schemas/prototype-spec-contract.json\`.
833
833
 
834
- `+xt(),Rt(),Tt(),_t(),ve,be,Te,Y,Se,de,pe,ce,xe,ke,V,Oe,_e,At(),Ae,V,ie,Pt(),jt(),Et(),Ot(),`${ue}
834
+ `+St(),Rt(),xt(),Tt(),ve,be,Te,Y,Se,de,pe,ce,xe,ke,V,Oe,_e,_t(),Ae,V,ie,At(),jt(),Pt(),Ot(),`${ue}
835
835
 
836
836
  ${It()}
837
837
 
@@ -962,10 +962,10 @@ ${n?Lt(e.typeName,n):""}`}function Lt(e,a){let{name:o,widthDp:n,heightDp:s}=a.pr
962
962
  ${e}()
963
963
  .frame(width: ${n}, height: ${s})
964
964
  }
965
- `}var Ft="dev.jasonpearson.auto-mobile:auto-mobile-prototype-compose",Gt="https://github.com/kaeawc/auto-mobile",zt="AutoMobilePrototype",Bt={"image/png":"png","image/jpeg":"jpg","image/webp":"webp","font/ttf":"ttf","font/otf":"otf","application/vnd.automobile.agsl":"agsl"};function J(e,a,o=dy){if(e===void 0||e.length===0)throw new t(`${o} needs ${a}.`);return e}function Wt(e){if(e===void 0||e==="android"||e==="ios")return e;throw new t(`Unsupported platform "${e}"; use android or ios.`)}function qe(e){return qt("sha256").update(e,"utf8").digest("hex").slice(0,8)}function Yt(e){return e.replace(/[^A-Za-z0-9_-]/g,"_")}var j=64;function D(e){let a=Yt(e);if(a===e&&a.length<=j)return a;return`${a.slice(0,j-9)}-${qe(e)}`}function T(e){return D(e)}function _(e){let o=e.split(/[^A-Za-z0-9]+/).filter((s)=>s.length>0).map((s)=>s[0].toUpperCase()+s.slice(1)).join(""),n=/^[A-Za-z]/.test(o)?o:`P${o}`;if(/^[A-Za-z][A-Za-z0-9]*$/.test(e)&&n.length<=j)return`${n}Prototype`;return`${n.slice(0,j-8)}Prototype${qe(e)}`}function X(e){let{images:a,fonts:o}=sb(e);return[...[...a].sort().map((n)=>({id:n,kind:"image"})),...[...o].sort().map((n)=>({id:n,kind:"font"}))]}async function Fe(e,a,o){return(await e.savedPrototypes(a)).find((s)=>s.id===o)}function Ge(e,a){let o=e.devicesShowing(a);if(o.length===1)return o[0];if(o.length===0)throw new t(`No device is showing prototype "${a}". Show it first, or pass deviceId for a saved prototype.`);throw new t(`Prototype "${a}" is shown on ${o.length} devices (${o.join(", ")}); pass deviceId.`)}async function Ht(e,a,o){let n=o??Ge(e,a),s=await Fe(e,n,a),r=e.shownSpec(n,a);if(r.kind==="known")return{deviceId:n,spec:r.spec,saved:s};if(r.kind==="busy")throw new t(`A prototype show is in flight on ${n}; read ${dy} again once it finishes.`);if(s)return{deviceId:n,spec:s.spec,saved:s};throw new t(`Unknown prototype "${a}" on ${n}: it is neither shown nor saved there.`)}async function Vt(e,a,o){let n=a??await e.platformOf(o.deviceId)??(o.saved?"android":void 0);if(!n)throw new t(`Cannot tell the platform of ${o.deviceId}; pass platform=android or platform=ios.`);return n}function M(e,a,o){return e==="android"?`app/src/main/assets/prototypes/${a}/${o}`:`Prototypes/${a}/${o}`}function Kt(e,a,o){let n=new URLSearchParams({id:e,assetId:a,deviceId:o});return`${Jb}?${n.toString()}`}function Jt(e,a,o){let n=T(a),s=new Map((o.saved?.assets??[]).map((r)=>[r.id,r]));return X(o.spec).map(({id:r,kind:i})=>{let l=s.get(r),p=l?.mimeType?`.${Bt[l.mimeType]}`:"";return{id:r,kind:i,...l?.mimeType?{mimeType:l.mimeType}:{},path:M(e,n,`${D(r)}${p}`),uri:Kt(a,r,o.deviceId),available:l?.bytes!==void 0}})}function Z(e,a){return e==="android"?{gradle:`${Ft}:${a}`}:{swiftPackage:{url:Gt,from:a,product:zt}}}function Xt(e,a,o){let n=[];if(a.includes("prototype_network_v1"))n.push(e==="android"?'`request` actions fail until the app passes PrototypeNetwork(device = ...); host-routed requests (omitted or via:"host") then use it unless a hostHandler takes them over.':'`request` actions use the renderer\'s URLSession client by default; pass PrototypeNetworkConfiguration(hostHandler:) to take over host-routed requests (omitted or via:"host").');let s=o.filter((r)=>!r.available).map((r)=>r.id);if(s.length>0)n.push(`The host has no bytes for asset(s) ${s.join(", ")} (only saved prototypes keep them); add those files yourself.`);if(e==="ios")n.push("Add the Prototypes folder to the app target as a folder reference so Bundle.main finds files by subdirectory.");return n}function Zt(e,a,o,n){let s=T(a),r=_(a),i={id:a,directory:s,typeName:r,assets:n,storage:o.storage!==void 0},l={path:M(e,s,"spec.json"),content:JSON.stringify(o,null,2)};return e==="android"?[l,{path:`app/src/main/java/prototypes/${r}.kt`,content:I(i)}]:[l,{path:`${r}.swift`,content:N(i)}]}function Q(e){let a=new Map;for(let o of e){let n=`${o.kind}:${o.id.normalize("NFC")}`,s=a.get(n);if(s!==void 0)throw new t(`Asset ids ${JSON.stringify(s)} and ${JSON.stringify(o.id)} are the same string in Swift (canonically equivalent); rename one before exporting for iOS.`);a.set(n,o.id)}}function ee(e,a){if(Gp(e).includes("prototype_custom_shaders_v1"))throw new t(a==="ios"?"Custom AGSL shaders are Android only; remove style.shader before exporting for iOS.":"Native prototype export does not support style.shader yet; the Compose renderer has no custom shader runtime. Remove style.shader before exporting.")}async function te(e,a,o=dy){let n=J(e.id,"id",o),s=Wt(e.platform),r=await Ht(a,n,e.deviceId),i=await Vt(a,s,r);return{id:n,platform:i,resolved:r}}async function ze(e,a){let{id:o,platform:n,resolved:s}=await te(e,a);ee(s.spec,n);let r=a.libraryVersion(),i=Gp(s.spec),l=Jt(n,o,s);if(n==="ios")Q(l);return{id:o,platform:n,library:Z(n,r),requiredCapabilities:i,minimumLibraryVersion:r,files:Zt(n,o,s.spec,l),assets:l,notes:Xt(n,i,l)}}async function Be(e,a){let o=J(e.id,"id"),n=J(e.assetId,"assetId"),s=e.deviceId??Ge(a,o),r=(await Fe(a,s,o))?.assets.find((i)=>i.id===n);if(!r?.bytes||!r.mimeType)throw new t(`No bytes for asset "${n}" of prototype "${o}" on ${s}: the host keeps asset bytes only for saved prototypes. Supply the file yourself.`);return{mimeType:r.mimeType,bytes:r.bytes}}var Ye=W.definitions;function Qt(e,a){let o=c(e);return o?.kind==="ref"&&o.name===a}function ea(e,a,o){let n=c(a);return n?Object.entries(c(e)??{}).flatMap(([s,r])=>(s in n)?k(c(r)?.rule,n[s],`${o}.${s}`):[]):[]}function ta(e,a,o){if(!Array.isArray(a))return[];return Qt(e,"action")?[{path:o,actions:a}]:a.flatMap((n,s)=>k(e,n,`${o}[${s}]`))}var We={ref:(e,a,o)=>e.name==="node"||e.name==="action"?[]:k(Ye[String(e.name)],a,o),array:(e,a,o)=>ta(e.item,a,o),map:(e,a,o)=>Object.entries(c(a)??{}).flatMap(([n,s])=>k(e.item,s,`${o}[${JSON.stringify(n)}]`)),object:(e,a,o)=>ea(e.fields,a,o),tagged:(e,a,o)=>k(c(e.variants)?.[String(c(a)?.type)],a,o),choice:(e,a,o)=>Array.isArray(e.options)?e.options.flatMap((n)=>k(n,a,o)):[]};function k(e,a,o){let n=c(e),s=n?.kind,r=typeof s==="string"&&Object.hasOwn(We,s)?We[s]:void 0;return n&&r?r(n,a,o):[]}function He(e,a){let o=k(Ye.node,e,a).filter((n)=>n.path!==`${a}.onTap`);return[...new Map(o.map((n)=>[n.path,n])).values()]}var Ve={box:{compose:"Box",swiftUi:"ZStack"},row:{compose:"Row",swiftUi:"HStack"},column:{compose:"Column",swiftUi:"VStack"},flow:"stub",grid:"stub",carousel:"stub",text:{compose:"Text",swiftUi:"Text"},image:"stub",icon:"stub",spacer:{compose:"Spacer",swiftUi:"Spacer"},textField:{compose:"TextField / OutlinedTextField",swiftUi:"TextField / SecureField"},searchBar:"stub",switch:"stub",checkbox:"stub",button:{compose:"Button (by variant)",swiftUi:"Button"},pasteButton:"stub",radioGroup:"stub",select:"stub",listItem:{compose:"ListItem",swiftUi:"HStack"},slider:"stub",chip:"stub",card:{compose:"Card",swiftUi:"VStack"},iconButton:"stub",fab:"stub",segmentedButton:"stub",topAppBar:"stub",divider:{compose:"HorizontalDivider / VerticalDivider",swiftUi:"Divider"},badge:"stub",progress:"stub",chart:"stub",dialog:"stub",menu:"stub",snackbar:"stub",timePicker:"stub",datePicker:"stub",list:{compose:"LazyColumn / LazyRow",swiftUi:"ScrollView + LazyVStack / LazyHStack"},scroll:{compose:"Column / Row + scroll modifier",swiftUi:"ScrollView"},pager:"stub",stack:{compose:"when over a back stack",swiftUi:"switch over a back stack"},tabBar:"stub",bottomNav:"stub",bottomSheet:"stub",tooltip:"stub",adaptive:"stub",drawer:"stub",videoPlayer:"stub",map:"stub",castButton:"stub",cameraPreview:"stub",webView:"stub"};function Ke(e){return typeof e==="string"&&Object.hasOwn(Ve,e)&&Ve[e]!=="stub"}var aa=/\{[^{}]+\}/,oa=new Set(["push","replace"]),na=new Set(["pop","popToRoot"]);function sa(e,a){let o=String(e?.type);if(typeof e?.stack!=="string")return;let n=h(e.stack,a);if(oa.has(o)&&typeof e.screen==="string")return`ui.${o}(${n}, ${h(e.screen,a)})`;return na.has(o)?`ui.${o}(${n})`:void 0}class ae{writer;node;path;index;constructor(e,a,o,n){this.writer=e;this.node=a;this.path=o;this.index=n}get language(){return this.writer.dialect.language}string(e){let a=this.node[e];return typeof a==="string"?a:void 0}text(e){let a=this.index===void 0?e:e.replaceAll("{index}","");if(aa.test(a))this.writer.todos.push({path:this.path,kind:"binding",type:e});if(this.index===void 0||!e.includes("{index}"))return h(e,this.language);let o=this.writer.dialect.indexed(this.index),[n,...s]=e.split("{index}");return s.reduce((r,i)=>i.length>0?`${r} + ${o} + ${h(i,this.language)}`:`${r} + ${o}`,h(n,this.language))}testTag(){let e=this.string("testTag");return e===void 0?void 0:this.text(e)}actions(e){let a=this.node[e];if(!Array.isArray(a))return[];return a.map((o,n)=>this.action(c(o),`${this.path}.${e}[${n}]`))}action(e,a){let o=sa(e,this.language);if(o!==void 0)return o;let n=String(e?.type);return this.writer.todos.push({path:a,kind:"action",type:n}),`/* TODO(eject): ${n.replace(/[^A-Za-z0-9_]/g,"")} action */`}children(e="children"){let a=this.node[e];if(Array.isArray(a))return a.flatMap((o,n)=>this.writer.emit(o,`${this.path}.${e}[${n}]`,this.index));return this.writer.emit(a,`${this.path}.${e}`,this.index)}row(e){let a=this.node.children,o=Array.isArray(a)?a[0]:void 0;return this.writer.emit(o,`${this.path}.children[0]`,e)}rowCount(){let e=c(this.node.repeat),a=c(e?.items)?.path,o=typeof a==="string"?kt(this.writer.data,a):[];return this.writer.todos.push({path:`${this.path}.repeat`,kind:"binding",type:String(a)}),Array.isArray(o)?o.length:0}stackScreens(){let e=this.string("id")??"",a=c(this.node.screens)??{},o=this.string("start")??Object.keys(a)[0]??"";return this.writer.stackStarts.set(e,o),Object.entries(a).map(([n,s])=>[n,this.writer.screen(e,n,s,`${this.path}.screens[${JSON.stringify(n)}]`)])}literal(e){return h(e,this.language)}}var ra=" ";function ia(e){return e.map((a)=>a.length>0?`${ra}${a}`:a)}function U(e){let a=(o)=>o.startsWith("/*");return[e.filter((o)=>!a(o)).join("; "),...e.filter(a)].filter((o)=>o.length>0).join(" ")}function d(e,a,o="}"){return[e,...ia(a),o]}function C(e){let o=e.split(/[^A-Za-z0-9]+/).filter((n)=>n.length>0).map((n)=>n[0].toUpperCase()+n.slice(1)).join("");return/^[A-Za-z]/.test(o)?o:`S${o}`}class oe{dialect;data;prefix;todos=[];screens=[];stackStarts=new Map;scaffolded=new Set;screenNames=new Set;constructor(e,a,o){this.dialect=e;this.data=a;this.prefix=o}emit(e,a,o){let n=c(e);if(!n)return[];let s=new ae(this,n,a,o),r=n.type;if(Ke(r)){this.scaffolded.add(r);let l=this.dialect.emitters[r](n,s);return[...this.untranslated(n,a),...l]}this.todos.push({path:a,kind:"node",type:String(r)});let i=ar(n).flatMap(({value:l,suffix:p})=>this.emit(l,`${a}${p}`,o));return[...this.untranslated(n,a),...this.dialect.todoStub(n,s,i)]}untranslated(e,a){return[...this.untranslatedActions(e,a),...this.untranslatedRepeat(e,a)]}untranslatedActions(e,a){let o=He(e,a);this.todos.push(...o.flatMap((s)=>s.actions.map((r,i)=>({path:`${s.path}[${i}]`,kind:"action",type:String(c(r)?.type)}))));let n=o.map((s)=>s.path.slice(a.length+1));return n.length===0?[]:[`// TODO(eject): ${n.join(", ")} actions at ${a} are not translated.`]}untranslatedRepeat(e,a){let o=c(e.repeat);if(!o||e.type==="list")return[];let n=c(o.items)?.path;return this.todos.push({path:`${a}.repeat`,kind:"binding",type:typeof n==="string"?n:"repeat"}),[`// TODO(eject): repeat at ${a} is not translated; its children are written once.`]}screen(e,a,o,n){let s=`${this.prefix}${C(e)}${C(a)}Screen`;for(let i=2;this.screenNames.has(s);i+=1)s=`${this.prefix}${C(e)}${C(a)}Screen${i}`;this.screenNames.add(s);let r=this.screens.length;return this.screens.push({name:s,body:[]}),this.screens[r]={name:s,body:this.emit(o,n)},s}}function f(e,a="",o=!0){let n=["Modifier",a],s=e.testTag();if(s!==void 0)n.push(`.testTag(${s})`);let r=o?e.actions("onTap"):[];if(r.length>0)n.push(`.clickable { ${U(r)} }`);return n.join("")}function L(e,a=""){return(o,n)=>d(`${e}(modifier = ${f(n)}${a}) {`,n.children())}function da(e){return(Array.isArray(e.spans)?e.spans:[]).map((o)=>typeof o==="object"&&o!==null&&("text"in o)?String(o.text):"").join("")}function la(e,a){return a.text(a.string("text")??da(e))}var ca={filled:"Button",tonal:"FilledTonalButton",elevated:"ElevatedButton",outlined:"OutlinedButton",text:"TextButton"},pa=(e,a)=>[`${ca[a.string("variant")??"filled"]??"Button"}(`,...d(" onClick = {",a.actions("onTap").map((o)=>` ${o}`)," },"),` modifier = ${f(a,"",!1)},`,...d(") {",[`Text(text = ${a.text(a.string("label")??"")})`])];function q(e,a,o){let n=e.string(o);return n===void 0?[]:[` ${a} = { Text(text = ${e.text(n)}) },`]}var ha=(e,a)=>{let o=a.text(a.string("stateKey")??""),n=e.secure===!0||e.keyboardType==="password";return[`${e.variant==="filled"?"TextField":"OutlinedTextField"}(`,` value = ui.text(${o}),`,` onValueChange = { ui.setText(${o}, it) },`,` modifier = ${f(a)},`,...q(a,"label","label"),...q(a,"placeholder","placeholder"),...q(a,"supportingText","supportingText"),...e.multiline===!0?[]:[" singleLine = true,"],...n?[" visualTransformation = PasswordVisualTransformation(),"]:[],")"]},ua=(e,a)=>{let o=e.axis==="horizontal"?"LazyRow":"LazyColumn",n=a.children("header"),s=a.children("footer");return d(`${o}(modifier = ${f(a)}) {`,[...n.length>0?d("item {",n):[],...d(`items(${a.rowCount()}) { index ->`,a.row("index")),...s.length>0?d("item {",s):[]])},ma=(e,a)=>["ListItem(",` headlineContent = { Text(text = ${a.text(a.string("headline")??"")}) },`,...q(a,"supportingContent","supporting"),` modifier = ${f(a)},`,")"],ya=(e,a)=>{let o=a.literal(a.string("id")??""),n=a.stackScreens().map(([s,r])=>`${a.literal(s)} -> ${r}(ui)`);return d(`Box(modifier = ${f(a)}) {`,d(`when (ui.top(${o})) {`,n))},fa=(e,a)=>{let o=e.axis==="horizontal";return d(`${o?"Row":"Column"}(modifier = ${f(a,`${o?".horizontalScroll":".verticalScroll"}(rememberScrollState())`)}) {`,a.children("child"))},Je={language:"kotlin",emitters:{box:L("Box"),row:L("Row",", verticalAlignment = Alignment.CenterVertically"),column:L("Column"),card:L("Card"),scroll:fa,spacer:(e,a)=>[`Spacer(modifier = ${f(a,".size(8.dp)")})`],divider:(e,a)=>[`${e.orientation==="vertical"?"VerticalDivider":"HorizontalDivider"}(modifier = ${f(a)})`],text:(e,a)=>[`Text(text = ${la(e,a)}, modifier = ${f(a)})`],button:pa,textField:ha,list:ua,listItem:ma,stack:ya},todoStub:(e,a,o)=>[`// TODO(eject): ${String(e.type)} at ${a.path} has no scaffold mapping; build it by hand.`,...d(`Column(modifier = ${f(a)}) {`,[`Text(text = ${a.literal(`TODO: ${String(e.type)}`)})`,...o])],indexed:(e)=>e},Xe=["androidx.compose.foundation.*","androidx.compose.foundation.layout.*","androidx.compose.foundation.lazy.*","androidx.compose.material3.*","androidx.compose.runtime.*","androidx.compose.ui.Alignment","androidx.compose.ui.Modifier","androidx.compose.ui.graphics.Color","androidx.compose.ui.platform.testTag","androidx.compose.ui.text.input.PasswordVisualTransformation","androidx.compose.ui.unit.dp"];var at=Object.keys(W.definitions.themeModeColors.fields);function Qe(...e){let a=new Map;for(let o of e){let n=c(o)??{};for(let s of at){let r=n[s];if(typeof r==="string"&&Kp(r))a.set(s,r)}}return a}function ot(e){let a=c(e),o=c(a?.colors),n=a?.mode==="light"||a?.mode==="dark"?a.mode:void 0;return{...n?{mode:n}:{},light:Qe(o,o?.light),dark:Qe(o,o?.dark),seeded:o?.seed!==void 0||o?.source!==void 0}}function nt(e){return Kp(e)??{r:0,g:0,b:0,a:1}}function F(e){return Math.round(e).toString(16).padStart(2,"0").toUpperCase()}function ga(e){let{r:a,g:o,b:n,a:s}=nt(e);return`Color(0x${F(s*255)}${F(a)}${F(o)}${F(n)})`}function et(e,a,o){let n=[...o].map(([s,r])=>` ${s} = ${ga(r)},`);return n.length===0?[`private val ${e} = ${a}()`]:[`private val ${e} = ${a}(`,...n,")"]}function va(e){if(e==="dark")return"true";return e==="light"?"false":"isSystemInDarkTheme()"}function st(e,a){return[...["@Composable",`fun ${a}(content: @Composable () -> Unit) {`,` val dark = ${va(e.mode)}`,` MaterialTheme(colorScheme = if (dark) ${a}Dark else ${a}Light, content = content)`,"}",""],...et(`${a}Light`,"lightColorScheme",e.light),...et(`${a}Dark`,"darkColorScheme",e.dark)]}function tt(e){let{r:a,g:o,b:n,a:s}=nt(e),r=(i)=>(i/255).toFixed(3);return`UIColor(red: ${r(a)}, green: ${r(o)}, blue: ${r(n)}, alpha: ${s.toFixed(3)})`}function rt(e,a){let n=at.filter((s)=>e.light.has(s)||e.dark.has(s)).map((s)=>{let r=e.light.get(s)??e.dark.get(s)??"#000000",i=e.dark.get(s)??r;return` static let ${s} = Color(UIColor { $0.userInterfaceStyle == .dark ? ${tt(i)} : ${tt(r)} })`});return[`enum ${a} {`,...n,"}"]}function m(e,a=!0){let o=[],n=e.testTag();if(n!==void 0)o.push(`.accessibilityIdentifier(${n})`);let s=a?e.actions("onTap"):[];if(s.length>0)o.push(`.onTapGesture { ${U(s)} }`);return o}function G(e,a=[]){return(o,n)=>[...d(`${e} {`,n.children()),...a,...m(n)]}function ba(e){return(Array.isArray(e.spans)?e.spans:[]).map((o)=>typeof o==="object"&&o!==null&&("text"in o)?String(o.text):"").join("")}var ka={filled:".borderedProminent",tonal:".bordered",elevated:".bordered",outlined:".bordered",text:".borderless"},Sa=(e,a)=>[...d("Button {",a.actions("onTap"),"} label: {"),` Text(verbatim: ${a.text(a.string("label")??"")})`,"}",`.buttonStyle(${ka[a.string("variant")??"filled"]??".borderedProminent"})`,...m(a,!1)];function xa(e,a){let o=`ui.text(${a.text(a.string("stateKey")??"")})`,n=a.string("placeholder"),s=n===void 0?"":`, prompt: Text(verbatim: ${a.text(n)})`,r=`Text(verbatim: ${a.text(a.string("label")??n??"")})`;if(e.secure===!0||e.keyboardType==="password")return`SecureField(text: ${o}${s}) { ${r} }`;let i=e.multiline===!0?", axis: .vertical":"";return`TextField(text: ${o}${s}${i}) { ${r} }`}var Ta=(e,a)=>{let o=a.string("supportingText"),n=[xa(e,a),".textFieldStyle(.roundedBorder)"];if(o===void 0)return[...n,...m(a)];return[...d("VStack(alignment: .leading) {",[...n,`Text(verbatim: ${a.text(o)})`," .font(.caption)"]),...m(a)]},_a=(e,a)=>{let o=e.axis==="horizontal",n=d(`ForEach(0..<${a.rowCount()}, id: \\.self) { index in`,a.row("index")),s=d(`${o?"LazyHStack":"LazyVStack(alignment: .leading)"} {`,[...a.children("header"),...n,...a.children("footer")]);return[...d(`ScrollView(${o?".horizontal":".vertical"}) {`,s),...m(a)]},Aa=(e,a)=>{let o=a.string("supporting"),n=[`Text(verbatim: ${a.text(a.string("headline")??"")})`,...o===void 0?[]:[`Text(verbatim: ${a.text(o)})`," .font(.subheadline)"," .foregroundStyle(.secondary)"]];return[...d("HStack {",[...d("VStack(alignment: .leading) {",n),"Spacer()"]),".contentShape(Rectangle())",...m(a)]},Pa=(e,a)=>{let o=a.stackScreens().flatMap(([r,i])=>[`case ${a.literal(r)}:`,` ${i}(ui: ui)`]),s=[`switch ui.top(${a.literal(a.string("id")??"")}) {`,...o,"default:"," EmptyView()","}"];return[...d("Group {",s),...m(a)]},Ea=(e,a)=>[...d(`ScrollView(${e.axis==="horizontal"?".horizontal":".vertical"}) {`,a.children("child")),...m(a)],it={language:"swift",emitters:{box:G("ZStack"),row:G("HStack"),column:G("VStack(alignment: .leading)"),card:G("VStack(alignment: .leading)",[".padding()",".background(Color(UIColor.secondarySystemBackground), in: RoundedRectangle(cornerRadius: 12))"]),scroll:Ea,spacer:(e,a)=>["Spacer()",".frame(width: 8, height: 8)",...m(a)],divider:(e,a)=>["Divider()",...m(a)],text:(e,a)=>[`Text(verbatim: ${a.text(a.string("text")??ba(e))})`,...m(a)],button:Sa,textField:Ta,list:_a,listItem:Aa,stack:Pa},todoStub:(e,a,o)=>[`// TODO(eject): ${String(e.type)} at ${a.path} has no scaffold mapping; build it by hand.`,...d("VStack(alignment: .leading) {",[`Text(verbatim: ${a.literal(`TODO: ${String(e.type)}`)})`,...o]),...m(a)],indexed:(e)=>`String(${e})`};function Oa(e){let a=Es(e),o=a.success?c(a.spec):void 0;if(!o)throw new t(`Cannot eject prototype "${String(e.id)}": its components do not expand. Show it again to see the error.`);return o}function Ra(e){return Object.entries(c(e.state)??{}).filter((a)=>typeof a[1]==="string")}function $a(e,a,o){let n=new URLSearchParams({id:e,platform:a}).toString();return[`// Generated once by ${Xb}?${n}.`,"// This file is yours: edit it freely. It is never regenerated or synced back to the spec.",`// ${o} TODO(eject) item(s) remain; search for TODO(eject).`]}function Ia(e,a,o){let s=new oe(a==="android"?Je:it,o.data,""),r=s.emit(o.root,"root");return{writer:s,root:r,typeName:_(e),theme:ot(o.theme),texts:Ra(o),header:$a(e,a,s.todos.length)}}function dt(e){return`mapOf<String, String>(${e.map(([o,n])=>`${h(o,"kotlin")} to ${h(n,"kotlin")}`).join(", ")})`}function Na(e){return d(`class ${e.typeName}State {`,[`private val starts: Map<String, String> = ${dt([...e.writer.stackStarts])}`,"private val stacks = mutableStateMapOf<String, List<String>>()",`private val texts = mutableStateMapOf<String, String>().apply { putAll(${dt(e.texts)}) }`,"","fun top(stack: String): String = path(stack).last()","fun push(stack: String, screen: String) { stacks[stack] = path(stack) + screen }","fun pop(stack: String) { val p = path(stack); if (p.size > 1) stacks[stack] = p.dropLast(1) }",'fun popToRoot(stack: String) { stacks[stack] = listOf(starts[stack] ?: "") }',"fun replace(stack: String, screen: String) { stacks[stack] = path(stack).dropLast(1) + screen }",'fun text(key: String): String = texts[key] ?: ""',"fun setText(key: String, value: String) { texts[key] = value }","",'private fun path(stack: String): List<String> = stacks[stack] ?: listOf(starts[stack] ?: "")'])}function ja(e){let a=e.typeName,o=e.writer.screens.flatMap((s)=>["","@Composable",...d(`private fun ${s.name}(ui: ${a}State) {`,s.body)]);return`${[...e.header,"package prototypes.eject","",...Xe.map((s)=>`import ${s}`),"","@Composable",...d(`fun ${a}() {`,[`val ui = remember { ${a}State() }`,...d(`${a}Theme {`,e.root)]),...o,"",...Na(e),"",...st(e.theme,`${a}Theme`)].join(`
965
+ `}var Ft="dev.jasonpearson.auto-mobile:auto-mobile-prototype-compose",Gt="https://github.com/kaeawc/auto-mobile",zt="AutoMobilePrototype",Bt={"image/png":"png","image/jpeg":"jpg","image/webp":"webp","font/ttf":"ttf","font/otf":"otf","application/vnd.automobile.agsl":"agsl"};function J(e,a,o=Ey){if(e===void 0||e.length===0)throw new t(`${o} needs ${a}.`);return e}function Wt(e){if(e===void 0||e==="android"||e==="ios")return e;throw new t(`Unsupported platform "${e}"; use android or ios.`)}function qe(e){return qt("sha256").update(e,"utf8").digest("hex").slice(0,8)}function Yt(e){return e.replace(/[^A-Za-z0-9_-]/g,"_")}var j=64;function D(e){let a=Yt(e);if(a===e&&a.length<=j)return a;return`${a.slice(0,j-9)}-${qe(e)}`}function T(e){return D(e)}function _(e){let o=e.split(/[^A-Za-z0-9]+/).filter((s)=>s.length>0).map((s)=>s[0].toUpperCase()+s.slice(1)).join(""),n=/^[A-Za-z]/.test(o)?o:`P${o}`;if(/^[A-Za-z][A-Za-z0-9]*$/.test(e)&&n.length<=j)return`${n}Prototype`;return`${n.slice(0,j-8)}Prototype${qe(e)}`}function X(e){let{images:a,fonts:o}=Ab(e);return[...[...a].sort().map((n)=>({id:n,kind:"image"})),...[...o].sort().map((n)=>({id:n,kind:"font"}))]}async function Fe(e,a,o){return(await e.savedPrototypes(a)).find((s)=>s.id===o)}function Ge(e,a){let o=e.devicesShowing(a);if(o.length===1)return o[0];if(o.length===0)throw new t(`No device is showing prototype "${a}". Show it first, or pass deviceId for a saved prototype.`);throw new t(`Prototype "${a}" is shown on ${o.length} devices (${o.join(", ")}); pass deviceId.`)}async function Ht(e,a,o){let n=o??Ge(e,a),s=await Fe(e,n,a),r=e.shownSpec(n,a);if(r.kind==="known")return{deviceId:n,spec:r.spec,saved:s};if(r.kind==="busy")throw new t(`A prototype show is in flight on ${n}; read ${Ey} again once it finishes.`);if(s)return{deviceId:n,spec:s.spec,saved:s};throw new t(`Unknown prototype "${a}" on ${n}: it is neither shown nor saved there.`)}async function Vt(e,a,o){let n=a??await e.platformOf(o.deviceId)??(o.saved?"android":void 0);if(!n)throw new t(`Cannot tell the platform of ${o.deviceId}; pass platform=android or platform=ios.`);return n}function M(e,a,o){return e==="android"?`app/src/main/assets/prototypes/${a}/${o}`:`Prototypes/${a}/${o}`}function Kt(e,a,o){let n=new URLSearchParams({id:e,assetId:a,deviceId:o});return`${uS}?${n.toString()}`}function Jt(e,a,o){let n=T(a),s=new Map((o.saved?.assets??[]).map((r)=>[r.id,r]));return X(o.spec).map(({id:r,kind:i})=>{let l=s.get(r),p=l?.mimeType?`.${Bt[l.mimeType]}`:"";return{id:r,kind:i,...l?.mimeType?{mimeType:l.mimeType}:{},path:M(e,n,`${D(r)}${p}`),uri:Kt(a,r,o.deviceId),available:l?.bytes!==void 0}})}function Z(e,a){return e==="android"?{gradle:`${Ft}:${a}`}:{swiftPackage:{url:Gt,from:a,product:zt}}}function Xt(e,a,o){let n=[];if(a.includes("prototype_network_v1"))n.push(e==="android"?'`request` actions fail until the app passes PrototypeNetwork(device = ...); host-routed requests (omitted or via:"host") then use it unless a hostHandler takes them over.':'`request` actions use the renderer\'s URLSession client by default; pass PrototypeNetworkConfiguration(hostHandler:) to take over host-routed requests (omitted or via:"host").');let s=o.filter((r)=>!r.available).map((r)=>r.id);if(s.length>0)n.push(`The host has no bytes for asset(s) ${s.join(", ")} (only saved prototypes keep them); add those files yourself.`);if(e==="ios")n.push("Add the Prototypes folder to the app target as a folder reference so Bundle.main finds files by subdirectory.");return n}function Zt(e,a,o,n){let s=T(a),r=_(a),i={id:a,directory:s,typeName:r,assets:n,storage:o.storage!==void 0},l={path:M(e,s,"spec.json"),content:JSON.stringify(o,null,2)};return e==="android"?[l,{path:`app/src/main/java/prototypes/${r}.kt`,content:I(i)}]:[l,{path:`${r}.swift`,content:N(i)}]}function Q(e){let a=new Map;for(let o of e){let n=`${o.kind}:${o.id.normalize("NFC")}`,s=a.get(n);if(s!==void 0)throw new t(`Asset ids ${JSON.stringify(s)} and ${JSON.stringify(o.id)} are the same string in Swift (canonically equivalent); rename one before exporting for iOS.`);a.set(n,o.id)}}function ee(e,a){if(dm(e).includes("prototype_custom_shaders_v1"))throw new t(a==="ios"?"Custom AGSL shaders are Android only; remove style.shader before exporting for iOS.":"Native prototype export does not support style.shader yet; the Compose renderer has no custom shader runtime. Remove style.shader before exporting.")}async function te(e,a,o=Ey){let n=J(e.id,"id",o),s=Wt(e.platform),r=await Ht(a,n,e.deviceId),i=await Vt(a,s,r);return{id:n,platform:i,resolved:r}}async function ze(e,a){let{id:o,platform:n,resolved:s}=await te(e,a);ee(s.spec,n);let r=a.libraryVersion(),i=dm(s.spec),l=Jt(n,o,s);if(n==="ios")Q(l);return{id:o,platform:n,library:Z(n,r),requiredCapabilities:i,minimumLibraryVersion:r,files:Zt(n,o,s.spec,l),assets:l,notes:Xt(n,i,l)}}async function Be(e,a){let o=J(e.id,"id"),n=J(e.assetId,"assetId"),s=e.deviceId??Ge(a,o),r=(await Fe(a,s,o))?.assets.find((i)=>i.id===n);if(!r?.bytes||!r.mimeType)throw new t(`No bytes for asset "${n}" of prototype "${o}" on ${s}: the host keeps asset bytes only for saved prototypes. Supply the file yourself.`);return{mimeType:r.mimeType,bytes:r.bytes}}var Ye=W.definitions;function Qt(e,a){let o=c(e);return o?.kind==="ref"&&o.name===a}function ea(e,a,o){let n=c(a);return n?Object.entries(c(e)??{}).flatMap(([s,r])=>(s in n)?k(c(r)?.rule,n[s],`${o}.${s}`):[]):[]}function ta(e,a,o){if(!Array.isArray(a))return[];return Qt(e,"action")?[{path:o,actions:a}]:a.flatMap((n,s)=>k(e,n,`${o}[${s}]`))}var We={ref:(e,a,o)=>e.name==="node"||e.name==="action"?[]:k(Ye[String(e.name)],a,o),array:(e,a,o)=>ta(e.item,a,o),map:(e,a,o)=>Object.entries(c(a)??{}).flatMap(([n,s])=>k(e.item,s,`${o}[${JSON.stringify(n)}]`)),object:(e,a,o)=>ea(e.fields,a,o),tagged:(e,a,o)=>k(c(e.variants)?.[String(c(a)?.type)],a,o),choice:(e,a,o)=>Array.isArray(e.options)?e.options.flatMap((n)=>k(n,a,o)):[]};function k(e,a,o){let n=c(e),s=n?.kind,r=typeof s==="string"&&Object.hasOwn(We,s)?We[s]:void 0;return n&&r?r(n,a,o):[]}function He(e,a){let o=k(Ye.node,e,a).filter((n)=>n.path!==`${a}.onTap`);return[...new Map(o.map((n)=>[n.path,n])).values()]}var Ve={box:{compose:"Box",swiftUi:"ZStack"},row:{compose:"Row",swiftUi:"HStack"},column:{compose:"Column",swiftUi:"VStack"},flow:"stub",grid:"stub",carousel:"stub",text:{compose:"Text",swiftUi:"Text"},image:"stub",icon:"stub",spacer:{compose:"Spacer",swiftUi:"Spacer"},textField:{compose:"TextField / OutlinedTextField",swiftUi:"TextField / SecureField"},searchBar:"stub",switch:"stub",checkbox:"stub",button:{compose:"Button (by variant)",swiftUi:"Button"},pasteButton:"stub",radioGroup:"stub",select:"stub",listItem:{compose:"ListItem",swiftUi:"HStack"},slider:"stub",chip:"stub",card:{compose:"Card",swiftUi:"VStack"},iconButton:"stub",fab:"stub",segmentedButton:"stub",topAppBar:"stub",divider:{compose:"HorizontalDivider / VerticalDivider",swiftUi:"Divider"},badge:"stub",progress:"stub",chart:"stub",dialog:"stub",menu:"stub",snackbar:"stub",timePicker:"stub",datePicker:"stub",list:{compose:"LazyColumn / LazyRow",swiftUi:"ScrollView + LazyVStack / LazyHStack"},scroll:{compose:"Column / Row + scroll modifier",swiftUi:"ScrollView"},pager:"stub",stack:{compose:"when over a back stack",swiftUi:"switch over a back stack"},tabBar:"stub",bottomNav:"stub",bottomSheet:"stub",tooltip:"stub",adaptive:"stub",drawer:"stub",videoPlayer:"stub",map:"stub",castButton:"stub",cameraPreview:"stub",webView:"stub"};function Ke(e){return typeof e==="string"&&Object.hasOwn(Ve,e)&&Ve[e]!=="stub"}var aa=/\{[^{}]+\}/,oa=new Set(["push","replace"]),na=new Set(["pop","popToRoot"]);function sa(e,a){let o=String(e?.type);if(typeof e?.stack!=="string")return;let n=h(e.stack,a);if(oa.has(o)&&typeof e.screen==="string")return`ui.${o}(${n}, ${h(e.screen,a)})`;return na.has(o)?`ui.${o}(${n})`:void 0}class ae{writer;node;path;index;constructor(e,a,o,n){this.writer=e;this.node=a;this.path=o;this.index=n}get language(){return this.writer.dialect.language}string(e){let a=this.node[e];return typeof a==="string"?a:void 0}text(e){let a=this.index===void 0?e:e.replaceAll("{index}","");if(aa.test(a))this.writer.todos.push({path:this.path,kind:"binding",type:e});if(this.index===void 0||!e.includes("{index}"))return h(e,this.language);let o=this.writer.dialect.indexed(this.index),[n,...s]=e.split("{index}");return s.reduce((r,i)=>i.length>0?`${r} + ${o} + ${h(i,this.language)}`:`${r} + ${o}`,h(n,this.language))}testTag(){let e=this.string("testTag");return e===void 0?void 0:this.text(e)}actions(e){let a=this.node[e];if(!Array.isArray(a))return[];return a.map((o,n)=>this.action(c(o),`${this.path}.${e}[${n}]`))}action(e,a){let o=sa(e,this.language);if(o!==void 0)return o;let n=String(e?.type);return this.writer.todos.push({path:a,kind:"action",type:n}),`/* TODO(eject): ${n.replace(/[^A-Za-z0-9_]/g,"")} action */`}children(e="children"){let a=this.node[e];if(Array.isArray(a))return a.flatMap((o,n)=>this.writer.emit(o,`${this.path}.${e}[${n}]`,this.index));return this.writer.emit(a,`${this.path}.${e}`,this.index)}row(e){let a=this.node.children,o=Array.isArray(a)?a[0]:void 0;return this.writer.emit(o,`${this.path}.children[0]`,e)}rowCount(){let e=c(this.node.repeat),a=c(e?.items)?.path,o=typeof a==="string"?Et(this.writer.data,a):[];return this.writer.todos.push({path:`${this.path}.repeat`,kind:"binding",type:String(a)}),Array.isArray(o)?o.length:0}stackScreens(){let e=this.string("id")??"",a=c(this.node.screens)??{},o=this.string("start")??Object.keys(a)[0]??"";return this.writer.stackStarts.set(e,o),Object.entries(a).map(([n,s])=>[n,this.writer.screen(e,n,s,`${this.path}.screens[${JSON.stringify(n)}]`)])}literal(e){return h(e,this.language)}}var ra=" ";function ia(e){return e.map((a)=>a.length>0?`${ra}${a}`:a)}function U(e){let a=(o)=>o.startsWith("/*");return[e.filter((o)=>!a(o)).join("; "),...e.filter(a)].filter((o)=>o.length>0).join(" ")}function d(e,a,o="}"){return[e,...ia(a),o]}function C(e){let o=e.split(/[^A-Za-z0-9]+/).filter((n)=>n.length>0).map((n)=>n[0].toUpperCase()+n.slice(1)).join("");return/^[A-Za-z]/.test(o)?o:`S${o}`}class oe{dialect;data;prefix;todos=[];screens=[];stackStarts=new Map;scaffolded=new Set;screenNames=new Set;constructor(e,a,o){this.dialect=e;this.data=a;this.prefix=o}emit(e,a,o){let n=c(e);if(!n)return[];let s=new ae(this,n,a,o),r=n.type;if(Ke(r)){this.scaffolded.add(r);let l=this.dialect.emitters[r](n,s);return[...this.untranslated(n,a),...l]}this.todos.push({path:a,kind:"node",type:String(r)});let i=cr(n).flatMap(({value:l,suffix:p})=>this.emit(l,`${a}${p}`,o));return[...this.untranslated(n,a),...this.dialect.todoStub(n,s,i)]}untranslated(e,a){return[...this.untranslatedActions(e,a),...this.untranslatedRepeat(e,a)]}untranslatedActions(e,a){let o=He(e,a);this.todos.push(...o.flatMap((s)=>s.actions.map((r,i)=>({path:`${s.path}[${i}]`,kind:"action",type:String(c(r)?.type)}))));let n=o.map((s)=>s.path.slice(a.length+1));return n.length===0?[]:[`// TODO(eject): ${n.join(", ")} actions at ${a} are not translated.`]}untranslatedRepeat(e,a){let o=c(e.repeat);if(!o||e.type==="list")return[];let n=c(o.items)?.path;return this.todos.push({path:`${a}.repeat`,kind:"binding",type:typeof n==="string"?n:"repeat"}),[`// TODO(eject): repeat at ${a} is not translated; its children are written once.`]}screen(e,a,o,n){let s=`${this.prefix}${C(e)}${C(a)}Screen`;for(let i=2;this.screenNames.has(s);i+=1)s=`${this.prefix}${C(e)}${C(a)}Screen${i}`;this.screenNames.add(s);let r=this.screens.length;return this.screens.push({name:s,body:[]}),this.screens[r]={name:s,body:this.emit(o,n)},s}}function f(e,a="",o=!0){let n=["Modifier",a],s=e.testTag();if(s!==void 0)n.push(`.testTag(${s})`);let r=o?e.actions("onTap"):[];if(r.length>0)n.push(`.clickable { ${U(r)} }`);return n.join("")}function L(e,a=""){return(o,n)=>d(`${e}(modifier = ${f(n)}${a}) {`,n.children())}function da(e){return(Array.isArray(e.spans)?e.spans:[]).map((o)=>typeof o==="object"&&o!==null&&("text"in o)?String(o.text):"").join("")}function la(e,a){return a.text(a.string("text")??da(e))}var ca={filled:"Button",tonal:"FilledTonalButton",elevated:"ElevatedButton",outlined:"OutlinedButton",text:"TextButton"},pa=(e,a)=>[`${ca[a.string("variant")??"filled"]??"Button"}(`,...d(" onClick = {",a.actions("onTap").map((o)=>` ${o}`)," },"),` modifier = ${f(a,"",!1)},`,...d(") {",[`Text(text = ${a.text(a.string("label")??"")})`])];function q(e,a,o){let n=e.string(o);return n===void 0?[]:[` ${a} = { Text(text = ${e.text(n)}) },`]}var ha=(e,a)=>{let o=a.text(a.string("stateKey")??""),n=e.secure===!0||e.keyboardType==="password";return[`${e.variant==="filled"?"TextField":"OutlinedTextField"}(`,` value = ui.text(${o}),`,` onValueChange = { ui.setText(${o}, it) },`,` modifier = ${f(a)},`,...q(a,"label","label"),...q(a,"placeholder","placeholder"),...q(a,"supportingText","supportingText"),...e.multiline===!0?[]:[" singleLine = true,"],...n?[" visualTransformation = PasswordVisualTransformation(),"]:[],")"]},ua=(e,a)=>{let o=e.axis==="horizontal"?"LazyRow":"LazyColumn",n=a.children("header"),s=a.children("footer");return d(`${o}(modifier = ${f(a)}) {`,[...n.length>0?d("item {",n):[],...d(`items(${a.rowCount()}) { index ->`,a.row("index")),...s.length>0?d("item {",s):[]])},ma=(e,a)=>["ListItem(",` headlineContent = { Text(text = ${a.text(a.string("headline")??"")}) },`,...q(a,"supportingContent","supporting"),` modifier = ${f(a)},`,")"],ya=(e,a)=>{let o=a.literal(a.string("id")??""),n=a.stackScreens().map(([s,r])=>`${a.literal(s)} -> ${r}(ui)`);return d(`Box(modifier = ${f(a)}) {`,d(`when (ui.top(${o})) {`,n))},fa=(e,a)=>{let o=e.axis==="horizontal";return d(`${o?"Row":"Column"}(modifier = ${f(a,`${o?".horizontalScroll":".verticalScroll"}(rememberScrollState())`)}) {`,a.children("child"))},Je={language:"kotlin",emitters:{box:L("Box"),row:L("Row",", verticalAlignment = Alignment.CenterVertically"),column:L("Column"),card:L("Card"),scroll:fa,spacer:(e,a)=>[`Spacer(modifier = ${f(a,".size(8.dp)")})`],divider:(e,a)=>[`${e.orientation==="vertical"?"VerticalDivider":"HorizontalDivider"}(modifier = ${f(a)})`],text:(e,a)=>[`Text(text = ${la(e,a)}, modifier = ${f(a)})`],button:pa,textField:ha,list:ua,listItem:ma,stack:ya},todoStub:(e,a,o)=>[`// TODO(eject): ${String(e.type)} at ${a.path} has no scaffold mapping; build it by hand.`,...d(`Column(modifier = ${f(a)}) {`,[`Text(text = ${a.literal(`TODO: ${String(e.type)}`)})`,...o])],indexed:(e)=>e},Xe=["androidx.compose.foundation.*","androidx.compose.foundation.layout.*","androidx.compose.foundation.lazy.*","androidx.compose.material3.*","androidx.compose.runtime.*","androidx.compose.ui.Alignment","androidx.compose.ui.Modifier","androidx.compose.ui.graphics.Color","androidx.compose.ui.platform.testTag","androidx.compose.ui.text.input.PasswordVisualTransformation","androidx.compose.ui.unit.dp"];var at=Object.keys(W.definitions.themeModeColors.fields);function Ze(...e){let a=new Map;for(let o of e){let n=c(o)??{};for(let s of at){let r=n[s];if(typeof r==="string"&&cm(r))a.set(s,r)}}return a}function ot(e){let a=c(e),o=c(a?.colors),n=a?.mode==="light"||a?.mode==="dark"?a.mode:void 0;return{...n?{mode:n}:{},light:Ze(o,o?.light),dark:Ze(o,o?.dark),seeded:o?.seed!==void 0||o?.source!==void 0}}function nt(e){return cm(e)??{r:0,g:0,b:0,a:1}}function F(e){return Math.round(e).toString(16).padStart(2,"0").toUpperCase()}function ga(e){let{r:a,g:o,b:n,a:s}=nt(e);return`Color(0x${F(s*255)}${F(a)}${F(o)}${F(n)})`}function et(e,a,o){let n=[...o].map(([s,r])=>` ${s} = ${ga(r)},`);return n.length===0?[`private val ${e} = ${a}()`]:[`private val ${e} = ${a}(`,...n,")"]}function va(e){if(e==="dark")return"true";return e==="light"?"false":"isSystemInDarkTheme()"}function st(e,a){return[...["@Composable",`fun ${a}(content: @Composable () -> Unit) {`,` val dark = ${va(e.mode)}`,` MaterialTheme(colorScheme = if (dark) ${a}Dark else ${a}Light, content = content)`,"}",""],...et(`${a}Light`,"lightColorScheme",e.light),...et(`${a}Dark`,"darkColorScheme",e.dark)]}function tt(e){let{r:a,g:o,b:n,a:s}=nt(e),r=(i)=>(i/255).toFixed(3);return`UIColor(red: ${r(a)}, green: ${r(o)}, blue: ${r(n)}, alpha: ${s.toFixed(3)})`}function rt(e,a){let n=at.filter((s)=>e.light.has(s)||e.dark.has(s)).map((s)=>{let r=e.light.get(s)??e.dark.get(s)??"#000000",i=e.dark.get(s)??r;return` static let ${s} = Color(UIColor { $0.userInterfaceStyle == .dark ? ${tt(i)} : ${tt(r)} })`});return[`enum ${a} {`,...n,"}"]}function m(e,a=!0){let o=[],n=e.testTag();if(n!==void 0)o.push(`.accessibilityIdentifier(${n})`);let s=a?e.actions("onTap"):[];if(s.length>0)o.push(`.onTapGesture { ${U(s)} }`);return o}function G(e,a=[]){return(o,n)=>[...d(`${e} {`,n.children()),...a,...m(n)]}function wa(e){return(Array.isArray(e.spans)?e.spans:[]).map((o)=>typeof o==="object"&&o!==null&&("text"in o)?String(o.text):"").join("")}var ba={filled:".borderedProminent",tonal:".bordered",elevated:".bordered",outlined:".bordered",text:".borderless"},Sa=(e,a)=>[...d("Button {",a.actions("onTap"),"} label: {"),` Text(verbatim: ${a.text(a.string("label")??"")})`,"}",`.buttonStyle(${ba[a.string("variant")??"filled"]??".borderedProminent"})`,...m(a,!1)];function xa(e,a){let o=`ui.text(${a.text(a.string("stateKey")??"")})`,n=a.string("placeholder"),s=n===void 0?"":`, prompt: Text(verbatim: ${a.text(n)})`,r=`Text(verbatim: ${a.text(a.string("label")??n??"")})`;if(e.secure===!0||e.keyboardType==="password")return`SecureField(text: ${o}${s}) { ${r} }`;let i=e.multiline===!0?", axis: .vertical":"";return`TextField(text: ${o}${s}${i}) { ${r} }`}var Ta=(e,a)=>{let o=a.string("supportingText"),n=[xa(e,a),".textFieldStyle(.roundedBorder)"];if(o===void 0)return[...n,...m(a)];return[...d("VStack(alignment: .leading) {",[...n,`Text(verbatim: ${a.text(o)})`," .font(.caption)"]),...m(a)]},_a=(e,a)=>{let o=e.axis==="horizontal",n=d(`ForEach(0..<${a.rowCount()}, id: \\.self) { index in`,a.row("index")),s=d(`${o?"LazyHStack":"LazyVStack(alignment: .leading)"} {`,[...a.children("header"),...n,...a.children("footer")]);return[...d(`ScrollView(${o?".horizontal":".vertical"}) {`,s),...m(a)]},Aa=(e,a)=>{let o=a.string("supporting"),n=[`Text(verbatim: ${a.text(a.string("headline")??"")})`,...o===void 0?[]:[`Text(verbatim: ${a.text(o)})`," .font(.subheadline)"," .foregroundStyle(.secondary)"]];return[...d("HStack {",[...d("VStack(alignment: .leading) {",n),"Spacer()"]),".contentShape(Rectangle())",...m(a)]},Pa=(e,a)=>{let o=a.stackScreens().flatMap(([r,i])=>[`case ${a.literal(r)}:`,` ${i}(ui: ui)`]),s=[`switch ui.top(${a.literal(a.string("id")??"")}) {`,...o,"default:"," EmptyView()","}"];return[...d("Group {",s),...m(a)]},Ea=(e,a)=>[...d(`ScrollView(${e.axis==="horizontal"?".horizontal":".vertical"}) {`,a.children("child")),...m(a)],it={language:"swift",emitters:{box:G("ZStack"),row:G("HStack"),column:G("VStack(alignment: .leading)"),card:G("VStack(alignment: .leading)",[".padding()",".background(Color(UIColor.secondarySystemBackground), in: RoundedRectangle(cornerRadius: 12))"]),scroll:Ea,spacer:(e,a)=>["Spacer()",".frame(width: 8, height: 8)",...m(a)],divider:(e,a)=>["Divider()",...m(a)],text:(e,a)=>[`Text(verbatim: ${a.text(a.string("text")??wa(e))})`,...m(a)],button:Sa,textField:Ta,list:_a,listItem:Aa,stack:Pa},todoStub:(e,a,o)=>[`// TODO(eject): ${String(e.type)} at ${a.path} has no scaffold mapping; build it by hand.`,...d("VStack(alignment: .leading) {",[`Text(verbatim: ${a.literal(`TODO: ${String(e.type)}`)})`,...o]),...m(a)],indexed:(e)=>`String(${e})`};function Oa(e){let a=Is(e),o=a.success?c(a.spec):void 0;if(!o)throw new t(`Cannot eject prototype "${String(e.id)}": its components do not expand. Show it again to see the error.`);return o}function Ra(e){return Object.entries(c(e.state)??{}).filter((a)=>typeof a[1]==="string")}function $a(e,a,o){let n=new URLSearchParams({id:e,platform:a}).toString();return[`// Generated once by ${pS}?${n}.`,"// This file is yours: edit it freely. It is never regenerated or synced back to the spec.",`// ${o} TODO(eject) item(s) remain; search for TODO(eject).`]}function Ia(e,a,o){let s=new oe(a==="android"?Je:it,o.data,""),r=s.emit(o.root,"root");return{writer:s,root:r,typeName:_(e),theme:ot(o.theme),texts:Ra(o),header:$a(e,a,s.todos.length)}}function dt(e){return`mapOf<String, String>(${e.map(([o,n])=>`${h(o,"kotlin")} to ${h(n,"kotlin")}`).join(", ")})`}function Na(e){return d(`class ${e.typeName}State {`,[`private val starts: Map<String, String> = ${dt([...e.writer.stackStarts])}`,"private val stacks = mutableStateMapOf<String, List<String>>()",`private val texts = mutableStateMapOf<String, String>().apply { putAll(${dt(e.texts)}) }`,"","fun top(stack: String): String = path(stack).last()","fun push(stack: String, screen: String) { stacks[stack] = path(stack) + screen }","fun pop(stack: String) { val p = path(stack); if (p.size > 1) stacks[stack] = p.dropLast(1) }",'fun popToRoot(stack: String) { stacks[stack] = listOf(starts[stack] ?: "") }',"fun replace(stack: String, screen: String) { stacks[stack] = path(stack).dropLast(1) + screen }",'fun text(key: String): String = texts[key] ?: ""',"fun setText(key: String, value: String) { texts[key] = value }","",'private fun path(stack: String): List<String> = stacks[stack] ?: listOf(starts[stack] ?: "")'])}function ja(e){let a=e.typeName,o=e.writer.screens.flatMap((s)=>["","@Composable",...d(`private fun ${s.name}(ui: ${a}State) {`,s.body)]);return`${[...e.header,"package prototypes.eject","",...Xe.map((s)=>`import ${s}`),"","@Composable",...d(`fun ${a}() {`,[`val ui = remember { ${a}State() }`,...d(`${a}Theme {`,e.root)]),...o,"",...Na(e),"",...st(e.theme,`${a}Theme`)].join(`
966
966
  `)}
967
967
  `}function lt(e){if(e.length===0)return"[:]";return`Dictionary([${e.map(([o,n])=>`(${h(o,"swift")}, ${h(n,"swift")})`).join(", ")}], uniquingKeysWith: { first, _ in first })`}function Da(e){return d(`final class ${e.typeName}State: ObservableObject {`,["@Published private var stacks: [String: [String]] = [:]","@Published private var texts: [String: String]","private let starts: [String: String]","",...d("init() {",[`starts = ${lt([...e.writer.stackStarts])}`,`texts = ${lt(e.texts)}`]),"",'func top(_ stack: String) -> String { path(stack).last ?? "" }',"func push(_ stack: String, _ screen: String) { stacks[stack] = path(stack) + [screen] }","func pop(_ stack: String) { let p = path(stack); if p.count > 1 { stacks[stack] = Array(p.dropLast()) } }",'func popToRoot(_ stack: String) { stacks[stack] = [starts[stack] ?? ""] }',"func replace(_ stack: String, _ screen: String) { stacks[stack] = Array(path(stack).dropLast()) + [screen] }",...d("func text(_ key: String) -> Binding<String> {",['Binding(get: { self.texts[key] ?? "" }, set: { self.texts[key] = $0 })']),"",'private func path(_ stack: String) -> [String] { stacks[stack] ?? [starts[stack] ?? ""] }'])}function Ma(e){return[...e.theme.light.has("primary")||e.theme.dark.has("primary")?[`.tint(${e.typeName}Theme.primary)`]:[],...e.theme.mode?[`.preferredColorScheme(.${e.theme.mode})`]:[]]}function Ca(e){let a=e.typeName,o=e.writer.screens.flatMap((s)=>["",...d(`private struct ${s.name}: View {`,[`@ObservedObject var ui: ${a}State`,"",...d("var body: some View {",d("Group {",s.body))])]);return`${[...e.header,"import SwiftUI","import UIKit","",...d(`struct ${a}: View {`,[`@StateObject private var ui = ${a}State()`,"",...d("var body: some View {",[...d("Group {",e.root),...Ma(e)])]),...o,"",...Da(e),"",...rt(e.theme,`${a}Theme`)].join(`
968
968
  `)}
969
- `}function Ua(e){let a=["One-shot scaffold, not round-trip: re-reading this resource never merges with code you changed.","Only layout, text, buttons, text fields, lists and stack navigation are translated; styles, conditions, state other than text, and data bindings are TODOs.","Every testTag is kept (Modifier.testTag / .accessibilityIdentifier), so flows recorded on the prototype can target the ejected screens."];if(e.theme.seeded)a.push("The spec seeds its color scheme (theme.colors.seed or source); only explicit color roles are exported.");return a}async function ct(e,a){let{id:o,platform:n,resolved:s}=await te(e,a,Xb),r=Ia(o,n,Oa(s.spec)),i=n==="android"?{path:`app/src/main/java/prototypes/eject/${r.typeName}.kt`,content:ja(r)}:{path:`${r.typeName}.swift`,content:Ca(r)};return{id:o,platform:n,files:[i],scaffolded:[...r.writer.scaffolded].sort(),remaining:r.writer.todos,notes:Ua(r)}}import{createHash as La}from"crypto";var qa={compose:{platform:"android",mimeType:UE.compose,render:I},swiftui:{platform:"ios",mimeType:UE.swiftui,render:N}};function Fa(e,a,o){let n=T(a);return X(o).map(({id:s,kind:r})=>({id:s,kind:r,path:M(e,n,D(s)),uri:`${Jb}?${new URLSearchParams({id:a,assetId:s}).toString()}`}))}function Ga(e){if("gradle"in e)return`Library (Gradle): implementation("${e.gradle}")`;let{url:a,from:o,product:n}=e.swiftPackage;return`Library (SwiftPM): .package(url: "${a}", from: "${o}"), product "${n}"`}function za(e,a,o,n){let{spec:s}=n,r=Gp(s),i=o.length===0?["Assets: none"]:["Assets (not inlined; add each file at its path, a saved prototype serves its bytes):",...o.map((l)=>` ${l.kind} ${l.path} <- ${l.uri}`)];return[`Generated by ${e} (revision ${n.revision}, frame ${n.frame.id}).`,Ga(Z(a,n.version)),`Required capabilities: ${r.length===0?"none":r.join(", ")}`,...i]}function Ba(e){let{width:a,height:o}=fv(e,yC(e)[0]);return{name:e.label,widthDp:a,heightDp:o}}function pt(e,a,o,n){return CV(()=>Wa(e,a,o,n))}function Wa(e,a,o,n){let{platform:s,mimeType:r,render:i}=qa[a],l=vC(o.mcpSessionId),p=e.revision(o.id,D5(o.revision),l),u=uu(o.frame??Uh(s));ee(p.spec,s);let w=Fa(s,o.id,p.spec);if(s==="ios")Q(w);let b=ly(o.id,a),v=i({id:o.id,directory:T(o.id),typeName:_(o.id),assets:w,storage:p.spec.storage!==void 0,inline:{spec:JSON.stringify(p.spec),generatedBy:b,header:za(b,s,w,{spec:p.spec,revision:p.revision,frame:u,version:n}),preview:Ba(u)}}),{deviceRevision:A,mcpSessionId:re}=p;return{text:v,mimeType:r,meta:{revision:p.revision,...A===void 0?{}:{deviceRevision:A},...re===void 0?{}:{mcpSessionId:re},revisions:e.history(o.id,l).map((gt)=>gt.revision),frame:u.id,sha256:La("sha256").update(v,"utf8").digest("hex"),rendererVersion:n}}}import{createHash as Xa}from"crypto";var ut=W.definitions;function B(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function mt(e,a,o){if(a)e.forEach((n)=>S(n,a,o))}var Ya={chip:(e)=>typeof e.stateKey==="string"?["filled/check"]:[],segmentedButton:()=>["filled/check"],select:()=>["filled/expand_more"],datePicker:()=>["filled/arrow_drop_down","filled/chevron_left","filled/chevron_right"]},ne=(e,a,o)=>{if(B(e))mt(Object.values(e),a.item,o)},Ha={ref:(e,a,o)=>Va(e,a.name,o),array:(e,a,o)=>{if(Array.isArray(e))mt(e,a.item,o)},map:ne,record:ne,screenMap:ne,object:(e,a,o)=>Ka(e,a,o),tagged:(e,a,o)=>{let n=B(e)?String(e.type):"",s=a.variants?.[n];if(s){for(let r of Ya[n]?.(e)??[])o.keys.add(r);S(e,s,o)}},choice:(e,a,o)=>a.options?.forEach((n)=>S(e,n,o))};function S(e,a,o){if(e!==void 0&&e!==null)Ha[a.kind]?.(e,a,o)}function Va(e,a,o){if(a==="iconName"){if(typeof e==="string"&&ty.has(e))o.keys.add(`filled/${e}`);return}let n=ut[a];if(n)S(e,n,o)}var ht=(e,a)=>e?.kind==="ref"&&e.name===a;function Ka(e,a,o){if(!B(e))return;let n=Object.entries(a.fields??{}),s=n.find(([,i])=>ht(i?.rule,"iconVariant"))?.[0],r=s?YF(e[s]):"filled";if(s)o.variants.add(r);for(let[i,l]of n){let p=e[i];if(ht(l?.rule,"iconName")&&typeof p==="string"){if(ty.has(p))o.keys.add(`${r}/${p}`)}else if(l)S(p,l.rule,o)}}function z(e,a){if(typeof e==="string"){if(ty.has(e))a.add(e)}else if(Array.isArray(e))e.forEach((o)=>z(o,a));else if(B(e))Object.values(e).forEach((o)=>z(o,a))}function Ja(e){let a={keys:new Set,variants:new Set(["filled"])};S(Rf(e),ut.spec,a);let o=new Set;z(e.state,o),z(e.data,o);for(let n of o)for(let s of a.variants)a.keys.add(`${s}/${n}`);return[...a.keys].sort()}function yt(e,a){let o={},n=[];for(let s of Ja(e)){let[r,i]=s.split("/"),l=a.glyph(i,YF(r));if(l)o[s]=l.map((p)=>lu(p)).join("");else n.push(s)}return{glyphs:o,iconMissing:n}}var Za="automobile-prototype-embed",Qa=1;function eo(e,a=DV()){let{glyphs:o,iconMissing:n}=yt(e,a);return{format:Za,version:Qa,requires:Gp(e),spec:e,...Object.keys(o).length>0?{glyphs:o}:{},...n.length>0?{iconMissing:n}:{}}}function ft(e,a,o,n){let s=Nf(e,a.id),r=O5(e,a,s),i=eo(r.revision.spec,n),l=JSON.stringify(i),{deviceRevision:p,mcpSessionId:u}=r.revision,w=vC(a.mcpSessionId);return{text:l,envelope:i,meta:{revision:r.revision.revision,...p===void 0?{}:{deviceRevision:p},...u===void 0?{}:{mcpSessionId:u},revisions:e.history(r.id,w).map((b)=>b.revision),variants:s,sha256:Xa("sha256").update(l).digest("hex"),rendererVersion:o}}}function es(){return new C5}function se(e,a,o){let n=new URLSearchParams(Object.entries(o).filter((r)=>r[1]!==void 0).sort(([r],[i])=>r.localeCompare(i))).toString(),s=ly(e,a);return n?`${s}?${n}`:s}async function ts(e,a){let o=await JF(e.revisions,a,e.rendererVersion(),e.images),{frame:n,revision:s,variant:r,mcpSessionId:i}=a;return{uri:se(a.id,"web",{frame:n,revision:s,variant:r,mcpSessionId:i}),mimeType:"text/html",text:o.html,_meta:{...o.meta}}}function as(e,a){let o=ft(e.revisions,a,e.rendererVersion()),{revision:n,variant:s,mcpSessionId:r}=a;return{uri:se(a.id,"embed",{revision:n,variant:s,mcpSessionId:r}),mimeType:"application/json",text:o.text,_meta:{...o.meta}}}function os(e,a,o){let n=pt(e.revisions,a,o,e.rendererVersion()),{frame:s,revision:r,mcpSessionId:i}=o;return{uri:se(o.id,a,{frame:s,revision:r,mcpSessionId:i}),mimeType:n.mimeType,text:n.text,_meta:{...n.meta}}}function ns(){return{uri:SC,mimeType:"text/markdown",text:je()}}function ss(e){let{total:a,names:o}=Ne(e);return{uri:`${ZF}?query=${Ze(e)}`,mimeType:"application/json",text:JSON.stringify({query:e,total:a,truncated:a>o.length,names:o})}}async function rs(e,a){let o=await ze(e,a),n=new URLSearchParams(e).toString();return{uri:n?`${dy}?${n}`:dy,mimeType:"application/json",text:JSON.stringify(o,null,2)}}async function is(e,a){let o=await ct(e,a),n=new URLSearchParams(e).toString();return{uri:n?`${Xb}?${n}`:Xb,mimeType:"application/json",text:JSON.stringify(o,null,2)}}async function ds(e,a){let o=await Be(e,a);return{uri:`${Jb}?${new URLSearchParams(e).toString()}`,mimeType:o.mimeType,blob:Buffer.from(o.bytes).toString("base64")}}export{es as createPrototypeWebImageEncoder,is as readEject,as as readEmbed,ns as readGuide,ss as readIcons,rs as readNative,ds as readNativeAsset,os as readSource,ts as readWeb};
969
+ `}function Ua(e){let a=["One-shot scaffold, not round-trip: re-reading this resource never merges with code you changed.","Only layout, text, buttons, text fields, lists and stack navigation are translated; styles, conditions, state other than text, and data bindings are TODOs.","Every testTag is kept (Modifier.testTag / .accessibilityIdentifier), so flows recorded on the prototype can target the ejected screens."];if(e.theme.seeded)a.push("The spec seeds its color scheme (theme.colors.seed or source); only explicit color roles are exported.");return a}async function ct(e,a){let{id:o,platform:n,resolved:s}=await te(e,a,pS),r=Ia(o,n,Oa(s.spec)),i=n==="android"?{path:`app/src/main/java/prototypes/eject/${r.typeName}.kt`,content:ja(r)}:{path:`${r.typeName}.swift`,content:Ca(r)};return{id:o,platform:n,files:[i],scaffolded:[...r.writer.scaffolded].sort(),remaining:r.writer.todos,notes:Ua(r)}}import{createHash as La}from"crypto";var qa={compose:{platform:"android",mimeType:rI.compose,render:I},swiftui:{platform:"ios",mimeType:rI.swiftui,render:N}};function Fa(e,a,o){let n=T(a);return X(o).map(({id:s,kind:r})=>({id:s,kind:r,path:M(e,n,D(s)),uri:`${uS}?${new URLSearchParams({id:a,assetId:s}).toString()}`}))}function Ga(e){if("gradle"in e)return`Library (Gradle): implementation("${e.gradle}")`;let{url:a,from:o,product:n}=e.swiftPackage;return`Library (SwiftPM): .package(url: "${a}", from: "${o}"), product "${n}"`}function za(e,a,o,n){let{spec:s}=n,r=dm(s),i=o.length===0?["Assets: none"]:["Assets (not inlined; add each file at its path, a saved prototype serves its bytes):",...o.map((l)=>` ${l.kind} ${l.path} <- ${l.uri}`)];return[`Generated by ${e} (revision ${n.revision}, frame ${n.frame.id}).`,Ga(Z(a,n.version)),`Required capabilities: ${r.length===0?"none":r.join(", ")}`,...i]}function Ba(e){let{width:a,height:o}=xv(e,EC(e)[0]);return{name:e.label,widthDp:a,heightDp:o}}function pt(e,a,o,n){return qV(()=>Wa(e,a,o,n))}function Wa(e,a,o,n){let{platform:s,mimeType:r,render:i}=qa[a],l=IC(o.mcpSessionId),p=e.revision(o.id,K5(o.revision),l),u=Su(o.frame??Zh(s));ee(p.spec,s);let w=Fa(s,o.id,p.spec);if(s==="ios")Q(w);let b=Iy(o.id,a),v=i({id:o.id,directory:T(o.id),typeName:_(o.id),assets:w,storage:p.spec.storage!==void 0,inline:{spec:JSON.stringify(p.spec),generatedBy:b,header:za(b,s,w,{spec:p.spec,revision:p.revision,frame:u,version:n}),preview:Ba(u)}}),{deviceRevision:A,mcpSessionId:re}=p;return{text:v,mimeType:r,meta:{revision:p.revision,...A===void 0?{}:{deviceRevision:A},...re===void 0?{}:{mcpSessionId:re},revisions:e.history(o.id,l).map((gt)=>gt.revision),frame:u.id,sha256:La("sha256").update(v,"utf8").digest("hex"),rendererVersion:n}}}import{createHash as Xa}from"crypto";var ut=W.definitions;function B(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function mt(e,a,o){if(a)e.forEach((n)=>S(n,a,o))}var Ya={chip:(e)=>typeof e.stateKey==="string"?["filled/check"]:[],segmentedButton:()=>["filled/check"],select:()=>["filled/expand_more"],datePicker:()=>["filled/arrow_drop_down","filled/chevron_left","filled/chevron_right"]},ne=(e,a,o)=>{if(B(e))mt(Object.values(e),a.item,o)},Ha={ref:(e,a,o)=>Va(e,a.name,o),array:(e,a,o)=>{if(Array.isArray(e))mt(e,a.item,o)},map:ne,record:ne,screenMap:ne,object:(e,a,o)=>Ka(e,a,o),tagged:(e,a,o)=>{let n=B(e)?String(e.type):"",s=a.variants?.[n];if(s){for(let r of Ya[n]?.(e)??[])o.keys.add(r);S(e,s,o)}},choice:(e,a,o)=>a.options?.forEach((n)=>S(e,n,o))};function S(e,a,o){if(e!==void 0&&e!==null)Ha[a.kind]?.(e,a,o)}function Va(e,a,o){if(a==="iconName"){if(typeof e==="string"&&by.has(e))o.keys.add(`filled/${e}`);return}let n=ut[a];if(n)S(e,n,o)}var ht=(e,a)=>e?.kind==="ref"&&e.name===a;function Ka(e,a,o){if(!B(e))return;let n=Object.entries(a.fields??{}),s=n.find(([,i])=>ht(i?.rule,"iconVariant"))?.[0],r=s?cL(e[s]):"filled";if(s)o.variants.add(r);for(let[i,l]of n){let p=e[i];if(ht(l?.rule,"iconName")&&typeof p==="string"){if(by.has(p))o.keys.add(`${r}/${p}`)}else if(l)S(p,l.rule,o)}}function z(e,a){if(typeof e==="string"){if(by.has(e))a.add(e)}else if(Array.isArray(e))e.forEach((o)=>z(o,a));else if(B(e))Object.values(e).forEach((o)=>z(o,a))}function Ja(e){let a={keys:new Set,variants:new Set(["filled"])};S(Lf(e),ut.spec,a);let o=new Set;z(e.state,o),z(e.data,o);for(let n of o)for(let s of a.variants)a.keys.add(`${s}/${n}`);return[...a.keys].sort()}function yt(e,a){let o={},n=[];for(let s of Ja(e)){let[r,i]=s.split("/"),l=a.glyph(i,cL(r));if(l)o[s]=l.map((p)=>bu(p)).join("");else n.push(s)}return{glyphs:o,iconMissing:n}}var Za="automobile-prototype-embed",Qa=1;function eo(e,a=VV()){let{glyphs:o,iconMissing:n}=yt(e,a);return{format:Za,version:Qa,requires:dm(e),spec:e,...Object.keys(o).length>0?{glyphs:o}:{},...n.length>0?{iconMissing:n}:{}}}function ft(e,a,o,n){let s=Jf(e,a.id),r=G5(e,a,s),i=eo(r.revision.spec,n),l=JSON.stringify(i),{deviceRevision:p,mcpSessionId:u}=r.revision,w=IC(a.mcpSessionId);return{text:l,envelope:i,meta:{revision:r.revision.revision,...p===void 0?{}:{deviceRevision:p},...u===void 0?{}:{mcpSessionId:u},revisions:e.history(r.id,w).map((b)=>b.revision),variants:s,sha256:Xa("sha256").update(l).digest("hex"),rendererVersion:o}}}function es(){return new j5}function se(e,a,o){let n=new URLSearchParams(Object.entries(o).filter((r)=>r[1]!==void 0).sort(([r],[i])=>r.localeCompare(i))).toString(),s=Iy(e,a);return n?`${s}?${n}`:s}async function ts(e,a){let o=await dL(e.revisions,a,e.rendererVersion(),e.images),{frame:n,revision:s,variant:r,mcpSessionId:i}=a;return{uri:se(a.id,"web",{frame:n,revision:s,variant:r,mcpSessionId:i}),mimeType:"text/html",text:o.html,_meta:{...o.meta}}}function as(e,a){let o=ft(e.revisions,a,e.rendererVersion()),{revision:n,variant:s,mcpSessionId:r}=a;return{uri:se(a.id,"embed",{revision:n,variant:s,mcpSessionId:r}),mimeType:"application/json",text:o.text,_meta:{...o.meta}}}function os(e,a,o){let n=pt(e.revisions,a,o,e.rendererVersion()),{frame:s,revision:r,mcpSessionId:i}=o;return{uri:se(o.id,a,{frame:s,revision:r,mcpSessionId:i}),mimeType:n.mimeType,text:n.text,_meta:{...n.meta}}}function ns(){return{uri:xC,mimeType:"text/markdown",text:je()}}function ss(e){let{total:a,names:o}=Ne(e);return{uri:`${uL}?query=${Qe(e)}`,mimeType:"application/json",text:JSON.stringify({query:e,total:a,truncated:a>o.length,names:o})}}async function rs(e,a){let o=await ze(e,a),n=new URLSearchParams(e).toString();return{uri:n?`${Ey}?${n}`:Ey,mimeType:"application/json",text:JSON.stringify(o,null,2)}}async function is(e,a){let o=await ct(e,a),n=new URLSearchParams(e).toString();return{uri:n?`${pS}?${n}`:pS,mimeType:"application/json",text:JSON.stringify(o,null,2)}}async function ds(e,a){let o=await Be(e,a);return{uri:`${uS}?${new URLSearchParams(e).toString()}`,mimeType:o.mimeType,blob:Buffer.from(o.bytes).toString("base64")}}export{es as createPrototypeWebImageEncoder,is as readEject,as as readEmbed,ns as readGuide,ss as readIcons,rs as readNative,ds as readNativeAsset,os as readSource,ts as readWeb};
970
970
 
971
- //# debugId=EE6F81868B53B4B764756E2164756E21
971
+ //# debugId=E282F6B1AA18F11864756E2164756E21
@@ -0,0 +1,5 @@
1
+ // @bun
2
+ import{h,I}from"./chunk-5wk1t46v.js";import{t}from"./chunk-2b39zyxc.js";import{existsSync as E}from"fs";var EA=h.IOS_SCREEN_CAPTURE_HELPER.name,d=h.IOS_SCREEN_CAPTURE_HELPER.legacyName;function DS(r=process.env){return I(h.IOS_SCREEN_CAPTURE_HELPER,r)}function OS(r,n={}){let o=n.env??process.env,s=n.exists??E,a=[r,DS(o)].filter((e)=>Boolean(e));for(let e of a)if(s(e))return e;throw new t(`No executable screen-capture-helper was found at the configured path. Set ${EA} to the absolute path of a local development build.`)}
3
+ export{EA,DS,OS};
4
+
5
+ //# debugId=51F5C7D14978438B64756E2164756E21