@jxsuite/studio 4.1.0 → 5.1.0

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 (612) hide show
  1. package/README.md +1 -1
  2. package/data/elements-meta.json +5 -15
  3. package/data/html-meta.json +51 -6
  4. package/dist/chunks/{2020-n6tfwnda.js → 2020-g79n01v9.js} +14 -14
  5. package/dist/chunks/action-group-sp6tna8v.js +118 -0
  6. package/dist/chunks/adapter-nj7tnjfn.js +13 -0
  7. package/dist/chunks/cf-account-picker-qwkm9zrf.js +495 -0
  8. package/dist/chunks/cf-settings-71bv5zn9.js +20 -0
  9. package/dist/chunks/color-area-v17df7b8.js +154 -0
  10. package/dist/chunks/color-field-anwybnkn.js +208 -0
  11. package/dist/chunks/color-slider-44ez96fc.js +79 -0
  12. package/dist/chunks/color-xgg3kfw7.js +35 -0
  13. package/dist/chunks/combobox-5e7jev23.js +236 -0
  14. package/dist/chunks/components-vj72gqtq.js +20 -0
  15. package/dist/chunks/content-source-5mex4x1h.js +46 -0
  16. package/dist/chunks/convert-to-repeater-n64h8znc.js +639 -0
  17. package/dist/chunks/dialog-s890b918.js +85 -0
  18. package/dist/chunks/editor-3qhbg51x.js +35 -0
  19. package/dist/chunks/entry-commands-234hjnqw.js +42 -0
  20. package/dist/chunks/extension-sections-3hpcambp.js +651 -0
  21. package/dist/chunks/file-ops-8yhd83xr.js +47 -0
  22. package/dist/chunks/files-hnxqxtsg.js +72 -0
  23. package/dist/chunks/format-host-779sfp2n.js +40 -0
  24. package/dist/chunks/fs-events-b88z7g4h.js +18 -0
  25. package/dist/chunks/git-panel-xwj8fpab.js +49 -0
  26. package/dist/chunks/icons-yy10wk9r.js +402 -0
  27. package/dist/chunks/iframe-entry-15g3n6xv.js +114 -0
  28. package/dist/chunks/iframe-entry-b27vq81n.js +203 -0
  29. package/dist/chunks/iframe-entry-nbyxee1y.js +154 -0
  30. package/dist/chunks/iframe-host-syr0f9sb.js +115 -0
  31. package/dist/chunks/{index-wcnt3r9g.js → index-mdmyx6s2.js} +11 -11
  32. package/dist/chunks/index-n4j7qxyr.js +109 -0
  33. package/dist/chunks/{javascript-q8kpmfah.js → javascript-xvrfrpkb.js} +8 -9
  34. package/dist/chunks/{jsonMode-dkr040ga.js → jsonMode-tyx2kj6p.js} +526 -527
  35. package/dist/chunks/jx-validate-j0wxk72w.js +15 -0
  36. package/dist/chunks/layers-ff81wthn.js +30 -0
  37. package/dist/chunks/layers-panel-pnnghw12.js +40 -0
  38. package/dist/chunks/listbox-6v2ac08j.js +44 -0
  39. package/dist/chunks/lit-html-m244w9nh.js +17 -0
  40. package/dist/chunks/media-picker-cqhhxhhe.js +33 -0
  41. package/dist/chunks/menu-9p2w46gj.js +15 -0
  42. package/dist/chunks/menu-q99sy9rf.js +285 -0
  43. package/dist/chunks/{monaco-binding-rj6xpx9y.js → monaco-binding-sw6en4dw.js} +12 -15
  44. package/dist/chunks/monaco-buffer-bhy3hb6m.js +24 -0
  45. package/dist/chunks/{monaco-setup-vx9bhp1d.js → monaco-setup-8r62fj6y.js} +27 -37
  46. package/dist/chunks/notify-628t0anh.js +21 -0
  47. package/dist/chunks/number-field-5cndyft8.js +72 -0
  48. package/dist/chunks/parse-0zx14y15.js +10 -0
  49. package/dist/chunks/platform-ts9cg1a4.js +15 -0
  50. package/dist/chunks/popover-47z3ytva.js +25 -0
  51. package/dist/chunks/publish-panel-5zt840sy.js +27 -0
  52. package/dist/chunks/quick-search-3748564r.js +45 -0
  53. package/dist/chunks/references-qvvxej7h.js +26 -0
  54. package/dist/chunks/right-panel-eccz92qt.js +45 -0
  55. package/dist/chunks/section-registry-5pq32t0q.js +25 -0
  56. package/dist/chunks/select-2r16ez41.js +49 -0
  57. package/dist/chunks/split-g7nm1rgb.js +191 -0
  58. package/dist/chunks/studio-0hqbpbd0.js +62 -0
  59. package/dist/chunks/{studio-kryvsw6j.js → studio-1k325pwf.js} +48 -48
  60. package/dist/chunks/{studio-b55y6ewx.js → studio-1knfqgy7.js} +58 -58
  61. package/dist/chunks/{studio-2b946z26.js → studio-1z1kcjv4.js} +3628 -6095
  62. package/dist/chunks/studio-3773086z.js +111 -0
  63. package/dist/chunks/{studio-4r1pv6zf.js → studio-38emantv.js} +22 -5
  64. package/dist/chunks/{studio-x813zp48.js → studio-3rz5md06.js} +30 -30
  65. package/dist/chunks/{studio-ynxwkrzd.js → studio-4wbb9ct0.js} +1953 -1785
  66. package/dist/chunks/studio-5te5tqng.js +81 -0
  67. package/dist/chunks/studio-5wt9nvhd.js +4763 -0
  68. package/dist/chunks/{studio-p4ngc046.js → studio-6ewqv390.js} +9 -9
  69. package/dist/chunks/{studio-9ab8gqsm.js → studio-6nzaktvy.js} +7 -7
  70. package/dist/chunks/studio-6xa21ej9.js +28 -0
  71. package/dist/chunks/{studio-5ehhackm.js → studio-727xh0e3.js} +19 -19
  72. package/dist/chunks/{studio-gcye7qka.js → studio-7e11jpa9.js} +21 -21
  73. package/dist/chunks/{studio-q7v10cg2.js → studio-7r42c6ca.js} +17 -7
  74. package/dist/chunks/studio-7xnmgv66.js +13615 -0
  75. package/dist/chunks/studio-8scdew0e.js +258 -0
  76. package/dist/chunks/{studio-fbf92ckf.js → studio-93zafjcm.js} +275 -163
  77. package/dist/chunks/{studio-dg7zyt5x.js → studio-98b3z07n.js} +1 -1
  78. package/dist/chunks/studio-a79k90n9.js +1162 -0
  79. package/dist/chunks/studio-aj37eer3.js +422 -0
  80. package/dist/chunks/studio-b0k5tqgp.js +2432 -0
  81. package/dist/chunks/studio-b4s9eykd.js +2811 -0
  82. package/dist/chunks/{studio-f3wqjtk5.js → studio-d0300636.js} +5076 -18
  83. package/dist/chunks/{studio-b9b6t1dg.js → studio-e1f24eff.js} +5664 -5666
  84. package/dist/chunks/studio-e1pgga3k.js +839 -0
  85. package/dist/chunks/{studio-g1wdche0.js → studio-esrxgwh6.js} +80 -80
  86. package/dist/chunks/{studio-q43a5y9h.js → studio-g3jevyjt.js} +16 -16
  87. package/dist/chunks/studio-gb13095a.js +2806 -0
  88. package/dist/chunks/{studio-3n4ndrzh.js → studio-h4nvnjvz.js} +303 -303
  89. package/dist/chunks/{studio-dm19q43r.js → studio-hhvs4sdg.js} +10 -10
  90. package/dist/chunks/{studio-x4qkdat5.js → studio-js8nv53n.js} +1749 -2481
  91. package/dist/chunks/{studio-qjy2z9bv.js → studio-jxp12rnj.js} +32 -137
  92. package/dist/chunks/{studio-trxrabqf.js → studio-k75n7vhp.js} +527 -283
  93. package/dist/chunks/{studio-q9jd1a60.js → studio-kcgff4r0.js} +15 -18
  94. package/dist/chunks/studio-kkqkcp3d.js +17668 -0
  95. package/dist/chunks/studio-mhhyamxf.js +763 -0
  96. package/dist/chunks/{studio-cft9r4r7.js → studio-nq5yf5mp.js} +22 -22
  97. package/dist/chunks/{studio-685s0ytq.js → studio-nqgawfks.js} +572 -572
  98. package/dist/chunks/studio-nzzpvzx9.js +185 -0
  99. package/dist/chunks/{studio-xrapxwwc.js → studio-p1dw8wfw.js} +55 -29
  100. package/dist/chunks/{studio-3bqpr0zh.js → studio-pxbgypmg.js} +299 -110
  101. package/dist/chunks/studio-q0a3yk4n.js +33 -0
  102. package/dist/chunks/{studio-wcy8trfx.js → studio-q3x06vmj.js} +23 -22
  103. package/dist/chunks/{studio-6yw4y3my.js → studio-qhhm6hbt.js} +8 -8
  104. package/dist/chunks/studio-qk4bfsq2.js +362 -0
  105. package/dist/chunks/{studio-msd847zn.js → studio-qk90fgqm.js} +361 -90
  106. package/dist/chunks/{studio-jkftxyvz.js → studio-r6nvacqs.js} +5861 -2154
  107. package/dist/chunks/{studio-53n8bp04.js → studio-r6xmmtfh.js} +1 -1
  108. package/dist/chunks/{studio-tvgyw9xw.js → studio-rnyf3f8e.js} +83 -250
  109. package/dist/chunks/studio-rs76k67r.js +12 -0
  110. package/dist/chunks/studio-sbjyc8bt.js +954 -0
  111. package/dist/chunks/{studio-1wagb550.js → studio-syg2tadj.js} +11 -11
  112. package/dist/chunks/{studio-t5wqwzdh.js → studio-tjjk7jvk.js} +1 -1
  113. package/dist/chunks/studio-tqgryxwg.js +1171 -0
  114. package/dist/chunks/studio-try13h8c.js +8448 -0
  115. package/dist/chunks/studio-vfq8qpjt.js +1219 -0
  116. package/dist/chunks/studio-vhv5tgxm.js +51 -0
  117. package/dist/chunks/{studio-ahn3pdgx.js → studio-vw7mzp6z.js} +52 -38
  118. package/dist/chunks/studio-wqpngpsk.js +174 -0
  119. package/dist/chunks/studio-wqz6dggm.js +1356 -0
  120. package/dist/chunks/{studio-92ab2jqe.js → studio-y9zedyjn.js} +14 -14
  121. package/dist/chunks/studio-yxa81f45.js +12 -0
  122. package/dist/chunks/studio-zdn2g6dt.js +148 -0
  123. package/dist/chunks/swatch-group-t0n6h2rn.js +139 -0
  124. package/dist/chunks/tabs-wbp1cdws.js +156 -0
  125. package/dist/chunks/textfield-abk6qyee.js +73 -0
  126. package/dist/chunks/toast-780pvfbv.js +27 -0
  127. package/dist/chunks/toast-host-3sqv46sn.js +116 -0
  128. package/dist/chunks/toolbar-xbh5bhtr.js +194 -0
  129. package/dist/chunks/tooltip-eptb614w.js +369 -0
  130. package/dist/chunks/tree-a73p0pqm.js +367 -0
  131. package/dist/chunks/{tsMode-76kr53q0.js → tsMode-jy4m86rv.js} +83 -85
  132. package/dist/chunks/workspace-t55jdjbx.js +69 -0
  133. package/dist/chunks/{ws-client-6shc5rze.js → ws-client-ny4zc51r.js} +20 -22
  134. package/dist/iframe-entry.js +1873 -673
  135. package/dist/studio.js +52757 -36535
  136. package/dist/workers/editor.worker.js +1040 -1085
  137. package/dist/workers/json.worker.js +1946 -1991
  138. package/dist/workers/ts.worker.js +4960 -5005
  139. package/index.html +1 -0
  140. package/package.json +18 -52
  141. package/src/about/about-modal.ts +72 -123
  142. package/src/browse/library-commands.ts +20 -49
  143. package/src/browse/library-layouts.ts +87 -218
  144. package/src/browse/library-pane.ts +406 -438
  145. package/src/canvas/canvas-helpers.ts +0 -49
  146. package/src/canvas/canvas-live-render.ts +25 -2
  147. package/src/canvas/canvas-render.ts +666 -231
  148. package/src/canvas/canvas-surface.ts +0 -1
  149. package/src/canvas/canvas-utils.ts +333 -113
  150. package/src/canvas/dialog-path.ts +76 -0
  151. package/src/canvas/dialog-state.ts +67 -0
  152. package/src/canvas/diff-marks.ts +252 -0
  153. package/src/canvas/diff-toolbar.ts +349 -0
  154. package/src/canvas/diff-view.ts +123 -0
  155. package/src/canvas/edit-width-drag.ts +187 -0
  156. package/src/canvas/edit-width.ts +150 -0
  157. package/src/canvas/iframe-entry.ts +125 -8
  158. package/src/canvas/iframe-host.ts +271 -15
  159. package/src/canvas/iframe-interaction.ts +133 -13
  160. package/src/canvas/iframe-patch.ts +7 -1
  161. package/src/canvas/iframe-position.ts +5 -6
  162. package/src/canvas/iframe-protocol.ts +106 -5
  163. package/src/canvas/iframe-render.ts +373 -5
  164. package/src/canvas/iframe-slash.ts +2 -2
  165. package/src/canvas/iframe-subtree.ts +5 -8
  166. package/src/canvas/path-mapping.ts +25 -3
  167. package/src/canvas/popover-path.ts +106 -0
  168. package/src/canvas/popover-state.ts +155 -0
  169. package/src/canvas/surface-registry.ts +21 -10
  170. package/src/collab/presence-chips.ts +51 -73
  171. package/src/commands/app-commands.ts +10 -0
  172. package/src/commands/budget.ts +12 -0
  173. package/src/commands/command-args.ts +404 -6
  174. package/src/commands/defaults.ts +44 -14
  175. package/src/commands/live-context.ts +11 -5
  176. package/src/commands/reference.ts +12 -2
  177. package/src/commands/registry.ts +177 -10
  178. package/src/commands/run-reported.ts +120 -0
  179. package/src/content/entry-commands.ts +16 -23
  180. package/src/content/entry-editor.ts +144 -101
  181. package/src/editor/canvas-slash-bridge.ts +2 -2
  182. package/src/editor/context-menu.ts +49 -200
  183. package/src/editor/convert-to-component.ts +42 -82
  184. package/src/editor/convert-to-repeater.ts +132 -173
  185. package/src/editor/inline-edit.ts +11 -0
  186. package/src/editor/shortcuts.ts +76 -49
  187. package/src/editor/slash-menu.ts +150 -157
  188. package/src/files/components.ts +39 -0
  189. package/src/files/file-ops.ts +117 -11
  190. package/src/files/files.ts +944 -660
  191. package/src/files/json-layout.ts +474 -0
  192. package/src/files/serialize-document.ts +8 -1
  193. package/src/format/convert-file.ts +16 -10
  194. package/src/format/format-host.ts +50 -1
  195. package/src/github/github-auth.ts +71 -29
  196. package/src/github/github-publish.ts +57 -65
  197. package/src/grid/cell-editors.ts +46 -4
  198. package/src/grid/cell-popovers.ts +65 -62
  199. package/src/grid/grid-open.ts +86 -64
  200. package/src/grid/grid-panel.ts +390 -469
  201. package/src/grid/redirects-grid.ts +40 -44
  202. package/src/hosting/layout.ts +2 -0
  203. package/src/i18n/i18n-commands.ts +137 -62
  204. package/src/media/media-pane.ts +83 -133
  205. package/src/new-project/add-repo-modal.ts +125 -176
  206. package/src/new-project/import-tab.ts +181 -252
  207. package/src/new-project/location-fields.ts +93 -160
  208. package/src/new-project/new-project-modal.ts +376 -367
  209. package/src/packages/jxsuite-update.ts +76 -18
  210. package/src/packages/pull-package-sync.ts +5 -1
  211. package/src/panels/activity-panel.ts +137 -104
  212. package/src/panels/ai-chat/chat-markdown.ts +15 -13
  213. package/src/panels/ai-chat/chat-view.ts +307 -472
  214. package/src/panels/ai-chat/composer.ts +175 -163
  215. package/src/panels/ai-chat/sessions-view.ts +31 -62
  216. package/src/panels/ai-panel.ts +213 -185
  217. package/src/panels/block-action-bar.ts +444 -645
  218. package/src/panels/bottom-dock.ts +112 -47
  219. package/src/panels/chat-panel.ts +19 -14
  220. package/src/panels/data-explorer.ts +141 -102
  221. package/src/panels/data-grid.ts +183 -150
  222. package/src/panels/dnd.ts +75 -20
  223. package/src/panels/editors.ts +26 -19
  224. package/src/panels/elements-panel.ts +243 -186
  225. package/src/panels/empty-state.ts +26 -42
  226. package/src/panels/events-panel.ts +950 -729
  227. package/src/panels/formula-workspace.ts +434 -207
  228. package/src/panels/frontmatter-fields.ts +185 -153
  229. package/src/panels/frontmatter-panel.ts +411 -187
  230. package/src/panels/git-diff-open.ts +148 -0
  231. package/src/panels/git-panel.ts +679 -605
  232. package/src/panels/head-panel.ts +617 -435
  233. package/src/panels/i18n-panel.ts +148 -135
  234. package/src/panels/imports-panel.ts +388 -414
  235. package/src/panels/jump-bar.ts +175 -147
  236. package/src/panels/layers-panel.ts +785 -671
  237. package/src/panels/left-panel.ts +166 -94
  238. package/src/panels/navigator-panels.ts +1 -1
  239. package/src/panels/pane-context.ts +529 -677
  240. package/src/panels/pane-grid.ts +265 -241
  241. package/src/panels/panel-registry.ts +15 -4
  242. package/src/panels/problems-panel.ts +181 -99
  243. package/src/panels/properties-panel.ts +1525 -966
  244. package/src/panels/provenance.ts +31 -113
  245. package/src/panels/quick-search.ts +223 -184
  246. package/src/panels/right-panel.ts +135 -169
  247. package/src/panels/seo-modal.ts +346 -167
  248. package/src/panels/settings-menu.ts +114 -661
  249. package/src/panels/settings-pane.ts +102 -86
  250. package/src/panels/signals-panel.ts +1318 -1172
  251. package/src/panels/statement-editor.ts +556 -428
  252. package/src/panels/style-panel.ts +1367 -630
  253. package/src/panels/style-utils.ts +36 -32
  254. package/src/panels/stylebook-doc.ts +30 -73
  255. package/src/panels/stylebook-layers-panel.ts +202 -109
  256. package/src/panels/stylebook-panel.ts +124 -88
  257. package/src/panels/tab-strip.ts +386 -288
  258. package/src/platforms/cloud.ts +106 -16
  259. package/src/platforms/devserver.ts +13 -0
  260. package/src/publish/deploy-checklist.ts +124 -78
  261. package/src/publish/publish-commands.ts +4 -12
  262. package/src/publish/publish-panel.ts +134 -328
  263. package/src/services/a11y-report.ts +100 -43
  264. package/src/services/ai-command-tools.ts +538 -0
  265. package/src/services/ai-project-tools.ts +8 -2
  266. package/src/services/ai-session-store.ts +6 -0
  267. package/src/services/ai-system-prompt.ts +112 -24
  268. package/src/services/ai-tools.ts +36 -212
  269. package/src/services/ai-write-report.ts +174 -0
  270. package/src/services/document-assistant.ts +59 -8
  271. package/src/services/idle.ts +16 -14
  272. package/src/services/live-surfaces.ts +237 -0
  273. package/src/services/model-uri.ts +75 -0
  274. package/src/services/notify.ts +63 -6
  275. package/src/services/popover-report.ts +325 -0
  276. package/src/services/surface-registry.ts +56 -0
  277. package/src/services/tool-executor.ts +6 -0
  278. package/src/settings/contexts-section.ts +301 -299
  279. package/src/settings/contributed-section.ts +361 -222
  280. package/src/settings/css-vars-editor.ts +320 -325
  281. package/src/settings/defs-editor.ts +572 -535
  282. package/src/settings/dependencies-editor.ts +79 -107
  283. package/src/settings/extension-commands.ts +379 -0
  284. package/src/settings/extension-rows.ts +113 -0
  285. package/src/settings/extensions-section.ts +287 -0
  286. package/src/settings/general-settings.ts +130 -181
  287. package/src/settings/head-editor.ts +10 -305
  288. package/src/settings/locales-section.ts +146 -196
  289. package/src/settings/preferences-accounts.ts +1 -1
  290. package/src/settings/preferences-dialog.ts +350 -330
  291. package/src/settings/project-sections.ts +108 -181
  292. package/src/settings/schema-field-ui.ts +11 -399
  293. package/src/settings/section-registry.ts +8 -1
  294. package/src/settings/settings-document.ts +31 -29
  295. package/src/shell/tree.ts +42 -87
  296. package/src/shell.ts +48 -36
  297. package/src/studio.ts +104 -69
  298. package/src/style/token-ref.ts +0 -39
  299. package/src/surfaces/about.json +263 -0
  300. package/src/surfaces/about.ts +163 -0
  301. package/src/surfaces/add-repo.json +486 -0
  302. package/src/surfaces/add-repo.ts +236 -0
  303. package/src/surfaces/ai-chat.json +2185 -0
  304. package/src/surfaces/ai-chat.ts +549 -0
  305. package/src/surfaces/ai-credentials-form.json +332 -0
  306. package/src/surfaces/ai-credentials-form.ts +151 -0
  307. package/src/surfaces/ai-managed-connect.json +108 -0
  308. package/src/surfaces/ai-managed-connect.ts +123 -0
  309. package/src/surfaces/ai-model-picker.json +114 -0
  310. package/src/surfaces/ai-model-picker.ts +139 -0
  311. package/src/surfaces/block-action-bar.json +640 -0
  312. package/src/surfaces/block-action-bar.ts +368 -0
  313. package/src/surfaces/bottom-dock.json +154 -0
  314. package/src/surfaces/bottom-dock.ts +125 -0
  315. package/src/surfaces/canvas-stage.json +444 -0
  316. package/src/surfaces/canvas-stage.ts +253 -0
  317. package/src/surfaces/cf-account-picker.json +300 -0
  318. package/src/surfaces/cf-account-picker.ts +214 -0
  319. package/src/surfaces/commandbar.json +985 -0
  320. package/src/{panels/toolbar.ts → surfaces/commandbar.ts} +417 -391
  321. package/src/surfaces/convert-repeater.json +309 -0
  322. package/src/surfaces/convert-repeater.ts +276 -0
  323. package/src/surfaces/data-actions.json +157 -0
  324. package/src/surfaces/data-actions.ts +115 -0
  325. package/src/surfaces/dialog.json +289 -0
  326. package/src/surfaces/dialog.ts +343 -0
  327. package/src/surfaces/diff-toolbar.json +266 -0
  328. package/src/surfaces/diff-toolbar.ts +139 -0
  329. package/src/surfaces/doc-header.json +739 -0
  330. package/src/surfaces/doc-header.ts +223 -0
  331. package/src/surfaces/empty-state.json +152 -0
  332. package/src/surfaces/empty-state.ts +173 -0
  333. package/src/surfaces/entry-editor.json +281 -0
  334. package/src/surfaces/entry-editor.ts +191 -0
  335. package/src/surfaces/expression-editor.json +977 -0
  336. package/src/surfaces/expression-editor.ts +266 -0
  337. package/src/surfaces/files-panel.json +592 -0
  338. package/src/surfaces/files-panel.ts +301 -0
  339. package/src/surfaces/formula-palette.json +364 -0
  340. package/src/surfaces/formula-palette.ts +340 -0
  341. package/src/surfaces/git-panel.json +1822 -0
  342. package/src/surfaces/git-panel.ts +272 -0
  343. package/src/surfaces/github-auth.json +112 -0
  344. package/src/surfaces/github-auth.ts +139 -0
  345. package/src/surfaces/github-publish.json +206 -0
  346. package/src/surfaces/github-publish.ts +169 -0
  347. package/src/surfaces/grid-cell.json +218 -0
  348. package/src/surfaces/grid-cell.ts +120 -0
  349. package/src/surfaces/grid-open.json +147 -0
  350. package/src/surfaces/grid-open.ts +87 -0
  351. package/src/surfaces/grid-panel.json +706 -0
  352. package/src/surfaces/grid-panel.ts +206 -0
  353. package/src/surfaces/grid-replace.json +179 -0
  354. package/src/surfaces/grid-replace.ts +89 -0
  355. package/src/surfaces/grid-views.json +446 -0
  356. package/src/surfaces/grid-views.ts +172 -0
  357. package/src/surfaces/inspector-dock.json +187 -0
  358. package/src/surfaces/inspector-dock.ts +161 -0
  359. package/src/surfaces/jump-bar.json +243 -0
  360. package/src/surfaces/jump-bar.ts +129 -0
  361. package/src/surfaces/jxsuite-update.json +146 -0
  362. package/src/surfaces/jxsuite-update.ts +149 -0
  363. package/src/surfaces/library-pane.json +1509 -0
  364. package/src/surfaces/library-pane.ts +363 -0
  365. package/src/surfaces/logic-panel.json +1949 -0
  366. package/src/surfaces/logic-panel.ts +369 -0
  367. package/src/surfaces/logic-workspace.json +685 -0
  368. package/src/surfaces/logic-workspace.ts +238 -0
  369. package/src/surfaces/media-browser.json +303 -0
  370. package/src/surfaces/media-browser.ts +145 -0
  371. package/src/surfaces/media-field.json +163 -0
  372. package/src/surfaces/media-field.ts +116 -0
  373. package/src/surfaces/media-pane.json +513 -0
  374. package/src/surfaces/media-pane.ts +197 -0
  375. package/src/surfaces/menu.json +232 -0
  376. package/src/surfaces/menu.ts +375 -0
  377. package/src/surfaces/navigator-dock.json +113 -0
  378. package/src/surfaces/navigator-dock.ts +130 -0
  379. package/src/surfaces/new-project.json +1654 -0
  380. package/src/surfaces/new-project.ts +411 -0
  381. package/src/surfaces/palette.json +535 -0
  382. package/src/surfaces/pane-context.json +1497 -0
  383. package/src/surfaces/pane-context.ts +513 -0
  384. package/src/surfaces/pane-grid.json +188 -0
  385. package/src/surfaces/pane-grid.ts +250 -0
  386. package/src/surfaces/panel-activity.json +563 -0
  387. package/src/surfaces/panel-activity.ts +187 -0
  388. package/src/surfaces/panel-data.json +141 -0
  389. package/src/surfaces/panel-data.ts +156 -0
  390. package/src/surfaces/panel-deploy-checklist.json +283 -0
  391. package/src/surfaces/panel-deploy-checklist.ts +175 -0
  392. package/src/surfaces/panel-elements.json +367 -0
  393. package/src/surfaces/panel-elements.ts +175 -0
  394. package/src/surfaces/panel-i18n.json +569 -0
  395. package/src/surfaces/panel-i18n.ts +212 -0
  396. package/src/surfaces/panel-imports.json +780 -0
  397. package/src/surfaces/panel-imports.ts +209 -0
  398. package/src/surfaces/panel-outline.json +734 -0
  399. package/src/surfaces/panel-outline.ts +326 -0
  400. package/src/surfaces/panel-page.json +1017 -0
  401. package/src/surfaces/panel-page.ts +240 -0
  402. package/src/surfaces/panel-problems.json +497 -0
  403. package/src/surfaces/panel-problems.ts +157 -0
  404. package/src/surfaces/panel-signals.json +1973 -0
  405. package/src/surfaces/panel-signals.ts +436 -0
  406. package/src/surfaces/panel-stylebook-layers.json +317 -0
  407. package/src/surfaces/panel-stylebook-layers.ts +155 -0
  408. package/src/surfaces/preferences.json +850 -0
  409. package/src/surfaces/preferences.ts +332 -0
  410. package/src/surfaces/progress-modal.json +110 -0
  411. package/src/surfaces/progress-modal.ts +150 -0
  412. package/src/surfaces/properties-panel.json +1578 -0
  413. package/src/surfaces/properties-panel.ts +337 -0
  414. package/src/surfaces/publish.json +945 -0
  415. package/src/surfaces/publish.ts +392 -0
  416. package/src/surfaces/push-plan.json +110 -0
  417. package/src/surfaces/push-plan.ts +87 -0
  418. package/src/surfaces/rail.json +255 -0
  419. package/src/surfaces/rail.ts +206 -0
  420. package/src/surfaces/reference-field.json +191 -0
  421. package/src/surfaces/reference-field.ts +168 -0
  422. package/src/surfaces/schema-builder.json +1011 -0
  423. package/src/surfaces/schema-builder.ts +181 -0
  424. package/src/surfaces/schema-form.json +1013 -0
  425. package/src/surfaces/schema-form.ts +287 -0
  426. package/src/surfaces/secret-field.json +60 -0
  427. package/src/surfaces/secret-field.ts +146 -0
  428. package/src/surfaces/seo.json +932 -0
  429. package/src/surfaces/seo.ts +334 -0
  430. package/src/surfaces/settings-contexts.json +538 -0
  431. package/src/surfaces/settings-contexts.ts +196 -0
  432. package/src/surfaces/settings-contributed.json +489 -0
  433. package/src/surfaces/settings-contributed.ts +199 -0
  434. package/src/surfaces/settings-css-vars.json +820 -0
  435. package/src/surfaces/settings-css-vars.ts +199 -0
  436. package/src/surfaces/settings-defs.json +1363 -0
  437. package/src/surfaces/settings-defs.ts +232 -0
  438. package/src/surfaces/settings-deploy.json +108 -0
  439. package/src/surfaces/settings-deploy.ts +135 -0
  440. package/src/surfaces/settings-extensions.json +486 -0
  441. package/src/surfaces/settings-extensions.ts +201 -0
  442. package/src/surfaces/settings-head.json +779 -0
  443. package/src/surfaces/settings-head.ts +397 -0
  444. package/src/surfaces/settings-locales.json +420 -0
  445. package/src/surfaces/settings-locales.ts +137 -0
  446. package/src/surfaces/settings-overview.json +423 -0
  447. package/src/surfaces/settings-overview.ts +218 -0
  448. package/src/surfaces/settings-packages.json +549 -0
  449. package/src/surfaces/settings-packages.ts +158 -0
  450. package/src/surfaces/settings-pane.json +147 -0
  451. package/src/surfaces/settings-pane.ts +150 -0
  452. package/src/surfaces/settings-rawjson.json +104 -0
  453. package/src/surfaces/settings-rawjson.ts +107 -0
  454. package/src/surfaces/shell.json +99 -0
  455. package/src/surfaces/shell.ts +30 -0
  456. package/src/surfaces/slash-menu.json +180 -0
  457. package/src/surfaces/slash-menu.ts +272 -0
  458. package/src/surfaces/statements.json +607 -0
  459. package/src/surfaces/statements.ts +238 -0
  460. package/src/surfaces/statusbar.json +94 -0
  461. package/src/{panels → surfaces}/statusbar.ts +164 -90
  462. package/src/surfaces/style-panel.json +1643 -0
  463. package/src/surfaces/style-panel.ts +338 -0
  464. package/src/surfaces/stylebook-chrome.json +122 -0
  465. package/src/surfaces/stylebook-chrome.ts +122 -0
  466. package/src/surfaces/tab-strip.json +448 -0
  467. package/src/surfaces/tab-strip.ts +200 -0
  468. package/src/surfaces/target-line.json +419 -0
  469. package/src/surfaces/target-line.ts +349 -0
  470. package/src/surfaces/toasts.json +189 -0
  471. package/src/surfaces/welcome.json +571 -0
  472. package/src/surfaces/welcome.ts +338 -0
  473. package/src/tabs/project-config.ts +111 -21
  474. package/src/tabs/tab.ts +52 -1
  475. package/src/types.ts +22 -0
  476. package/src/ui/ai-credentials-form.ts +104 -100
  477. package/src/ui/ai-managed-connect.ts +54 -36
  478. package/src/ui/ai-model-picker.ts +75 -47
  479. package/src/ui/cf-account-picker.ts +76 -141
  480. package/src/ui/color-selector.ts +80 -301
  481. package/src/ui/dynamic-slot.ts +43 -283
  482. package/src/ui/expression-editor.ts +797 -576
  483. package/src/ui/field-input.ts +8 -181
  484. package/src/ui/form-controls.ts +838 -366
  485. package/src/ui/formula-chips.ts +80 -85
  486. package/src/ui/kit.ts +33 -0
  487. package/src/ui/layers.ts +296 -593
  488. package/src/ui/media-picker.ts +203 -284
  489. package/src/ui/panel-resize.ts +251 -64
  490. package/src/ui/popover-surface.ts +153 -0
  491. package/src/ui/progress-modal.ts +22 -60
  492. package/src/ui/regions.ts +13 -7
  493. package/src/ui/schema-form.ts +906 -440
  494. package/src/ui/surface.ts +122 -0
  495. package/src/ui/unit-selector.ts +15 -131
  496. package/src/ui/value-source.ts +14 -0
  497. package/src/ui/virtual-window.ts +88 -9
  498. package/src/utils/canvas-media.ts +72 -0
  499. package/src/utils/edit-display.ts +7 -2
  500. package/src/utils/element-selectors.ts +69 -0
  501. package/src/utils/link-target.ts +2 -2
  502. package/src/utils/studio-utils.ts +0 -10
  503. package/src/view.ts +15 -0
  504. package/src/workspace/pane-derive.ts +36 -14
  505. package/src/workspace/workspace.ts +75 -6
  506. package/styles/canvas.css +72 -467
  507. package/styles/forced-colors.css +48 -82
  508. package/styles/forced-colors.json +41 -0
  509. package/styles/inspector.css +2 -1301
  510. package/styles/overlays.css +96 -2823
  511. package/styles/panels.css +6 -1423
  512. package/styles/shell-frame.css +272 -0
  513. package/styles/shell-frame.json +227 -0
  514. package/styles/shell.css +0 -1568
  515. package/styles/tokens.css +65 -136
  516. package/styles/tokens.json +84 -0
  517. package/dist/chunks/Overlay.dev-etqjwyf9.js +0 -23
  518. package/dist/chunks/adapter-jkcyhgtr.js +0 -14
  519. package/dist/chunks/cf-account-picker-en86v7x7.js +0 -163
  520. package/dist/chunks/cf-settings-45fgyx8y.js +0 -21
  521. package/dist/chunks/components-zmx10t62.js +0 -21
  522. package/dist/chunks/content-source-48xg86wj.js +0 -49
  523. package/dist/chunks/convert-to-repeater-43tfn7cs.js +0 -268
  524. package/dist/chunks/editor-cptq5jp1.js +0 -37
  525. package/dist/chunks/entry-commands-e63f1xc0.js +0 -46
  526. package/dist/chunks/extension-sections-txv08xxg.js +0 -311
  527. package/dist/chunks/file-ops-wbh4hhsx.js +0 -48
  528. package/dist/chunks/files-3kfkwhk4.js +0 -93
  529. package/dist/chunks/flow-qbfgrpeh.js +0 -634
  530. package/dist/chunks/focus-trap.esm-9t2drp0g.js +0 -1025
  531. package/dist/chunks/focus-visible-6eg70z18.js +0 -167
  532. package/dist/chunks/format-host-w16q81tf.js +0 -43
  533. package/dist/chunks/fs-events-hrrg328g.js +0 -21
  534. package/dist/chunks/git-panel-532whr1y.js +0 -59
  535. package/dist/chunks/index-zqxmqxmh.js +0 -109
  536. package/dist/chunks/jx-validate-8er6gbxy.js +0 -19
  537. package/dist/chunks/layers-panel-07hm7g5j.js +0 -67
  538. package/dist/chunks/layers-q75tj2mf.js +0 -45
  539. package/dist/chunks/lit-html-j055mkh8.js +0 -22
  540. package/dist/chunks/monaco-buffer-dqw0xxpx.js +0 -25
  541. package/dist/chunks/notify-yjm79n4s.js +0 -30
  542. package/dist/chunks/parse-24d0gay3.js +0 -19
  543. package/dist/chunks/platform-ba1j1gbh.js +0 -16
  544. package/dist/chunks/publish-panel-v9st96ja.js +0 -29
  545. package/dist/chunks/quick-search-zccjp6gv.js +0 -66
  546. package/dist/chunks/references-3qx8fkyp.js +0 -29
  547. package/dist/chunks/right-panel-vm3b6kwy.js +0 -46
  548. package/dist/chunks/section-registry-sx1ykvkw.js +0 -30
  549. package/dist/chunks/sp-overlay.dev-qgvhnxf4.js +0 -18
  550. package/dist/chunks/sp-popover.dev-n2etmqdp.js +0 -9
  551. package/dist/chunks/sp-progress-circle.dev-hryhj0es.js +0 -15
  552. package/dist/chunks/sp-tooltip.dev-4sastdnw.js +0 -15
  553. package/dist/chunks/sp-tray.dev-tkbscm06.js +0 -199
  554. package/dist/chunks/studio-0a5qrh01.js +0 -3
  555. package/dist/chunks/studio-0bq6eh7v.js +0 -12
  556. package/dist/chunks/studio-18j3bmcz.js +0 -14
  557. package/dist/chunks/studio-1ngbejft.js +0 -181
  558. package/dist/chunks/studio-4b5z1x43.js +0 -424
  559. package/dist/chunks/studio-4n2b5gzb.js +0 -357
  560. package/dist/chunks/studio-4rzyrtjz.js +0 -426
  561. package/dist/chunks/studio-5fx3g9jg.js +0 -4325
  562. package/dist/chunks/studio-5myknm0a.js +0 -1903
  563. package/dist/chunks/studio-7afd21nw.js +0 -67
  564. package/dist/chunks/studio-7nte2hj4.js +0 -613
  565. package/dist/chunks/studio-8jpzqk5s.js +0 -128
  566. package/dist/chunks/studio-8q517dfr.js +0 -24
  567. package/dist/chunks/studio-950k0dx6.js +0 -6939
  568. package/dist/chunks/studio-9n4mrmnb.js +0 -184
  569. package/dist/chunks/studio-a0tswy0w.js +0 -58
  570. package/dist/chunks/studio-ahnzd9rg.js +0 -23
  571. package/dist/chunks/studio-bgxt93ga.js +0 -62
  572. package/dist/chunks/studio-cmb03bg5.js +0 -13890
  573. package/dist/chunks/studio-d1ks410h.js +0 -175
  574. package/dist/chunks/studio-ef49pnzx.js +0 -387
  575. package/dist/chunks/studio-eqq1w638.js +0 -113
  576. package/dist/chunks/studio-ew0djmhg.js +0 -341
  577. package/dist/chunks/studio-gfgb8pe6.js +0 -1652
  578. package/dist/chunks/studio-h4rxscfc.js +0 -10
  579. package/dist/chunks/studio-jc994dnr.js +0 -1066
  580. package/dist/chunks/studio-kyh9avvv.js +0 -28
  581. package/dist/chunks/studio-m3hwn7y1.js +0 -120
  582. package/dist/chunks/studio-mtfnaqs9.js +0 -659
  583. package/dist/chunks/studio-n6j9t10q.js +0 -318
  584. package/dist/chunks/studio-n8b9wqwy.js +0 -291
  585. package/dist/chunks/studio-qcy05dp8.js +0 -1743
  586. package/dist/chunks/studio-qq6jym04.js +0 -53
  587. package/dist/chunks/studio-rdx8jcxy.js +0 -83
  588. package/dist/chunks/studio-rn9ta2mz.js +0 -111
  589. package/dist/chunks/studio-t7qb9db8.js +0 -665
  590. package/dist/chunks/studio-tmc5emrw.js +0 -8
  591. package/dist/chunks/studio-z6nw2ek2.js +0 -18
  592. package/dist/chunks/studio-zczps70q.js +0 -378
  593. package/dist/chunks/studio-zqazsct2.js +0 -1823
  594. package/dist/chunks/workspace-fjx52ryv.js +0 -89
  595. package/src/panels/activity-bar.ts +0 -251
  596. package/src/panels/panel-scheduler.ts +0 -219
  597. package/src/panels/style-inputs.ts +0 -237
  598. package/src/panels/target-line.ts +0 -274
  599. package/src/panels/welcome-screen.ts +0 -346
  600. package/src/ui/button-group.ts +0 -102
  601. package/src/ui/choice-field.ts +0 -72
  602. package/src/ui/field-row.ts +0 -249
  603. package/src/ui/formula-palette.ts +0 -213
  604. package/src/ui/icons.ts +0 -52
  605. package/src/ui/jx-theme.ts +0 -130
  606. package/src/ui/spectrum.ts +0 -340
  607. package/src/ui/value-selector.ts +0 -174
  608. package/src/ui/widgets.ts +0 -172
  609. /package/dist/chunks/{javascript-s56a8dpk.css → javascript-4abzjcy9.css} +0 -0
  610. /package/dist/chunks/{jsonMode-zr9sw0c7.css → jsonMode-vvmpmk34.css} +0 -0
  611. /package/dist/chunks/{monaco-setup-pav68602.css → monaco-setup-8vbyqnhr.css} +0 -0
  612. /package/dist/chunks/{tsMode-ef187txx.css → tsMode-hnxxb73h.css} +0 -0
@@ -1,3 +1,35 @@
1
+ var __chunkGraphs;
2
+ var __chunkSeen;
3
+ var __chunkNonce;
4
+ var __preload = (id, seenOnly) => {
5
+ for (var [base, graph, ids] of __chunkGraphs || [])
6
+ for (var stack = [id], g = globalThis, d = g.document, head, j, node, k, link;j = stack.pop(); )
7
+ if (!__chunkSeen[j] && (node = graph[j])) {
8
+ __chunkSeen[j] = 1;
9
+ for (k = 1;k < node.length; k++)
10
+ stack.push(ids[node[k]]);
11
+ if (!seenOnly && j !== id && (head = d && d.head)) {
12
+ if (__chunkNonce === undefined)
13
+ __chunkNonce = (k = d.querySelector("meta[property=csp-nonce]")) && (k.nonce || k.getAttribute("nonce")) || "";
14
+ link = d.createElement("link");
15
+ link.rel = "modulepreload";
16
+ link.crossOrigin = "";
17
+ if (__chunkNonce)
18
+ link.nonce = __chunkNonce;
19
+ link.href = new g.URL(node[0], base);
20
+ head.appendChild(link);
21
+ }
22
+ }
23
+ };
24
+ var __chunks = (base, ids, nodes, entry) => {
25
+ for (var graph = {}, i = 0;i < ids.length; i++)
26
+ graph[ids[i]] = nodes[i];
27
+ (__chunkGraphs ||= []).push([base, graph, ids]);
28
+ __chunkSeen ||= {};
29
+ __preload(ids[entry], 1);
30
+ };
31
+ __chunks(import.meta.url,["1zk4r753","a73p0pqm","eptb614w","abk6qyee","780pvfbv","3sqv46sn","xgg3kfw7","yy10wk9r","5cndyft8","sp6tna8v","47z3ytva","2r16ez41","g7nm1rgb","t0n6h2rn","wbp1cdws","xbh5bhtr","v17df7b8","anwybnkn","44ez96fc","5e7jev23","s890b918","6v2ac08j","q99sy9rf","nbyxee1y","15g3n6xv","b27vq81n"],[["./iframe-entry.js"],["./chunks/tree-a73p0pqm.js"],["./chunks/tooltip-eptb614w.js",23],["./chunks/textfield-abk6qyee.js"],["./chunks/toast-780pvfbv.js",24],["./chunks/toast-host-3sqv46sn.js",24],["./chunks/color-xgg3kfw7.js",25],["./chunks/icons-yy10wk9r.js"],["./chunks/number-field-5cndyft8.js"],["./chunks/action-group-sp6tna8v.js"],["./chunks/popover-47z3ytva.js",23],["./chunks/select-2r16ez41.js"],["./chunks/split-g7nm1rgb.js"],["./chunks/swatch-group-t0n6h2rn.js"],["./chunks/tabs-wbp1cdws.js"],["./chunks/toolbar-xbh5bhtr.js"],["./chunks/color-area-v17df7b8.js",25],["./chunks/color-field-anwybnkn.js",25,23],["./chunks/color-slider-44ez96fc.js",25],["./chunks/combobox-5e7jev23.js",23],["./chunks/dialog-s890b918.js"],["./chunks/listbox-6v2ac08j.js"],["./chunks/menu-q99sy9rf.js",23],["./chunks/iframe-entry-nbyxee1y.js"],["./chunks/iframe-entry-15g3n6xv.js"],["./chunks/iframe-entry-b27vq81n.js"]],0);
32
+
1
33
  // src/canvas/iframe-channel.ts
2
34
  var ENVELOPE = "jx:canvas";
3
35
  function postMessageChannel(opts) {
@@ -31,6 +63,17 @@ function postMessageChannel(opts) {
31
63
  };
32
64
  }
33
65
 
66
+ // src/utils/geometry.ts
67
+ function rectOf(el) {
68
+ return el.getBoundingClientRect();
69
+ }
70
+ function rectOfRange(r) {
71
+ return r.getBoundingClientRect();
72
+ }
73
+ function elementAtPoint(x, y, root = document) {
74
+ return root.elementFromPoint(x, y);
75
+ }
76
+
34
77
  // ../schema/src/guards.ts
35
78
  function isJsonObject(value) {
36
79
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -91,7 +134,17 @@ function paramNames(parameters) {
91
134
  return parameters.map((p) => typeof p === "string" ? p : p.name);
92
135
  }
93
136
  function isTagExpression(tagName) {
94
- return isJsonObject(tagName) && isJsonObject(tagName.$expression);
137
+ if (!isJsonObject(tagName) || !isJsonObject(tagName.$expression)) {
138
+ return false;
139
+ }
140
+ const expression = tagName.$expression;
141
+ if (expression.operator === "?:") {
142
+ return typeof expression.value === "string" && typeof expression.initial === "string";
143
+ }
144
+ if (expression.operator === "switch") {
145
+ return isJsonObject(expression.cases) && Object.values(expression.cases).every((tag) => typeof tag === "string") && typeof expression.default === "string";
146
+ }
147
+ return false;
95
148
  }
96
149
  function tagNameCandidates(tagName) {
97
150
  if (typeof tagName === "string") {
@@ -292,6 +345,13 @@ class EffectScope {
292
345
  function effectScope(detached) {
293
346
  return new EffectScope(detached);
294
347
  }
348
+ function onScopeDispose(fn, failSilently = false) {
349
+ if (activeEffectScope) {
350
+ activeEffectScope.cleanups.push(fn);
351
+ } else if (!failSilently) {
352
+ warn(`onScopeDispose() is called when there is no active effect scope to be associated with.`);
353
+ }
354
+ }
295
355
  var activeSub;
296
356
  var pausedQueueEffects = /* @__PURE__ */ new WeakSet;
297
357
 
@@ -1127,6 +1187,7 @@ class ReadonlyReactiveHandler extends BaseReactiveHandler {
1127
1187
  }
1128
1188
  var mutableHandlers = /* @__PURE__ */ new MutableReactiveHandler;
1129
1189
  var readonlyHandlers = /* @__PURE__ */ new ReadonlyReactiveHandler;
1190
+ var shallowReactiveHandlers = /* @__PURE__ */ new MutableReactiveHandler(true);
1130
1191
  var toShallow = (value) => value;
1131
1192
  var getProto = (v) => Reflect.getPrototypeOf(v);
1132
1193
  function createIterableMethod(method, isReadonly2, isShallow2) {
@@ -1304,6 +1365,9 @@ function createInstrumentationGetter(isReadonly2, shallow) {
1304
1365
  var mutableCollectionHandlers = {
1305
1366
  get: /* @__PURE__ */ createInstrumentationGetter(false, false)
1306
1367
  };
1368
+ var shallowCollectionHandlers = {
1369
+ get: /* @__PURE__ */ createInstrumentationGetter(false, true)
1370
+ };
1307
1371
  var readonlyCollectionHandlers = {
1308
1372
  get: /* @__PURE__ */ createInstrumentationGetter(true, false)
1309
1373
  };
@@ -1338,6 +1402,9 @@ function reactive(target) {
1338
1402
  }
1339
1403
  return createReactiveObject(target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap);
1340
1404
  }
1405
+ function shallowReactive(target) {
1406
+ return createReactiveObject(target, false, shallowReactiveHandlers, shallowCollectionHandlers, shallowReactiveMap);
1407
+ }
1341
1408
  function readonly(target) {
1342
1409
  return createReactiveObject(target, true, readonlyHandlers, readonlyCollectionHandlers, readonlyMap);
1343
1410
  }
@@ -1514,6 +1581,87 @@ function resolveAtQuery(atKey, mediaQueries) {
1514
1581
  }
1515
1582
  return null;
1516
1583
  }
1584
+ function isDeclarationAtRule(atKey) {
1585
+ return atKey.startsWith("@position-try") || atKey.startsWith("@property") || atKey.startsWith("@font-face") || atKey.startsWith("@counter-style");
1586
+ }
1587
+ function isKeyframesAtRule(atKey) {
1588
+ return atKey.startsWith("@keyframes");
1589
+ }
1590
+ function resolveNestedSelector(scope, key) {
1591
+ const resolved = [];
1592
+ for (const scopePart of splitSelectorList(scope)) {
1593
+ for (const keyPart of splitSelectorList(key)) {
1594
+ resolved.push(resolveOneNestedSelector(scopePart, keyPart));
1595
+ }
1596
+ }
1597
+ return resolved.join(", ");
1598
+ }
1599
+ function resolveOneNestedSelector(scope, key) {
1600
+ if (key.startsWith("&")) {
1601
+ return key.replaceAll("&", scope);
1602
+ }
1603
+ if (key.startsWith("[") || key.startsWith(":") || key.startsWith(".")) {
1604
+ return `${scope}${key}`;
1605
+ }
1606
+ return `${scope} ${key}`;
1607
+ }
1608
+ function splitSelectorList(selector) {
1609
+ const members = [];
1610
+ let depth = 0;
1611
+ let quote = null;
1612
+ let start = 0;
1613
+ for (let i = 0;i < selector.length; i += 1) {
1614
+ const ch = selector[i];
1615
+ if (quote) {
1616
+ if (ch === "\\") {
1617
+ i += 1;
1618
+ } else if (ch === quote) {
1619
+ quote = null;
1620
+ }
1621
+ continue;
1622
+ }
1623
+ if (ch === '"' || ch === "'") {
1624
+ quote = ch;
1625
+ } else if (ch === "(" || ch === "[") {
1626
+ depth += 1;
1627
+ } else if (ch === ")" || ch === "]") {
1628
+ depth = Math.max(0, depth - 1);
1629
+ } else if (ch === "," && depth === 0) {
1630
+ members.push(selector.slice(start, i));
1631
+ start = i + 1;
1632
+ }
1633
+ }
1634
+ members.push(selector.slice(start));
1635
+ const trimmed = members.map((member) => member.trim()).filter((member) => member !== "");
1636
+ return trimmed.length > 0 ? trimmed : [selector];
1637
+ }
1638
+ var COMPOUND_BREAK = /[\s>+~,()]/;
1639
+ var LEADING_TYPE = /^[A-Za-z_\u{00A0}-\u{FFFF}][\w\u{00A0}-\u{FFFF}-]*/u;
1640
+ function transposeOpenAttribute(selector, options) {
1641
+ let out = "";
1642
+ let from = 0;
1643
+ for (;; ) {
1644
+ const at = selector.indexOf("[open]", from);
1645
+ if (at === -1) {
1646
+ return out + selector.slice(from);
1647
+ }
1648
+ let start = at;
1649
+ while (start > 0 && !COMPOUND_BREAK.test(selector[start - 1])) {
1650
+ start -= 1;
1651
+ }
1652
+ const type = LEADING_TYPE.exec(selector.slice(start, at))?.[0]?.toLowerCase();
1653
+ const dialog = type === undefined ? options.dialog === true : type === "dialog";
1654
+ out += selector.slice(from, at) + (dialog ? "[data-jx-dialog-open]" : "[open]");
1655
+ from = at + "[open]".length;
1656
+ }
1657
+ }
1658
+ function transposeCanvasOverlaySelector(selector, options = {}) {
1659
+ if (selector.includes("::backdrop")) {
1660
+ return null;
1661
+ }
1662
+ const transposed = selector.replaceAll(":popover-open", "[data-jx-popover-open]").replaceAll(":modal", "[data-jx-dialog-open]").replaceAll("[inert]", "[data-jx-inert]");
1663
+ return transposeOpenAttribute(transposed, options);
1664
+ }
1517
1665
  var COLOR_SCHEME_ATTR = "data-color-scheme";
1518
1666
  function pureSchemeOf(query) {
1519
1667
  const m = /^\(\s*prefers-color-scheme\s*:\s*(light|dark)\s*\)$/.exec(query.trim());
@@ -1531,6 +1679,195 @@ function schemeSelectors(selector, scheme) {
1531
1679
  forced: `:where(:root[${COLOR_SCHEME_ATTR}="${scheme}"]) ${selector}`
1532
1680
  };
1533
1681
  }
1682
+ function isNestedSelectorKey(key) {
1683
+ return key.startsWith(":") || key.startsWith(".") || key.startsWith("&") || key.startsWith("[");
1684
+ }
1685
+ function cssPropertyName(property) {
1686
+ return property.startsWith("--") ? property : camelToKebab(property);
1687
+ }
1688
+ function hashCss(text) {
1689
+ let h = 2166136261;
1690
+ for (let i = 0;i < text.length; i += 1) {
1691
+ h ^= text.charCodeAt(i);
1692
+ h = Math.imul(h, 16777619);
1693
+ }
1694
+ return (h >>> 0).toString(36);
1695
+ }
1696
+ function cssRuleText(conditions, selector, declarations) {
1697
+ const body = declarations.map(([property, value]) => `${property}: ${value}`).join("; ");
1698
+ let text = selector === null ? body : `${selector} { ${body} }`;
1699
+ for (let i = conditions.length - 1;i >= 0; i -= 1) {
1700
+ text = `${conditions[i]} { ${text} }`;
1701
+ }
1702
+ return text;
1703
+ }
1704
+ function cssKeyframesText(conditions, name, blocks) {
1705
+ const body = blocks.map(({ selector, declarations }) => `${selector} { ${declarations.map(([property, value]) => `${property}: ${value}`).join("; ")} }`).join(" ");
1706
+ let text = `@keyframes ${name} { ${body} }`;
1707
+ for (let i = conditions.length - 1;i >= 0; i -= 1) {
1708
+ text = `${conditions[i]} { ${text} }`;
1709
+ }
1710
+ return text;
1711
+ }
1712
+ function withoutKeyframes(block) {
1713
+ const keyframeKeys = Object.keys(block).filter((key) => isKeyframesAtRule(key));
1714
+ if (keyframeKeys.length === 0) {
1715
+ return block;
1716
+ }
1717
+ const rest = { ...block };
1718
+ for (const key of keyframeKeys) {
1719
+ delete rest[key];
1720
+ }
1721
+ return rest;
1722
+ }
1723
+ function compoundsOntoScope(key) {
1724
+ const rest = key.startsWith("&") ? key.slice(1) : key;
1725
+ return rest === "" || rest.startsWith(":") || rest.startsWith(".") || rest.startsWith("[");
1726
+ }
1727
+ function buildStyleRules(style, options = {}) {
1728
+ const {
1729
+ scope = null,
1730
+ mediaQueries = {},
1731
+ transposeValue = (value) => value,
1732
+ transposeSelector = (selector) => selector,
1733
+ resolveValue
1734
+ } = options;
1735
+ const rules = [];
1736
+ const isBlock = (value) => value !== null && typeof value === "object" && !Array.isArray(value) && !isRef(value);
1737
+ const blocksOf = (key, value) => {
1738
+ if (isBlock(value)) {
1739
+ return [value];
1740
+ }
1741
+ if (!isDeclarationAtRule(key) || !Array.isArray(value) || value.length === 0) {
1742
+ return [];
1743
+ }
1744
+ return value.every((entry) => isBlock(entry)) ? value : [];
1745
+ };
1746
+ const declarationValue = (property, value, reactive, target) => {
1747
+ if (value === undefined || value === null) {
1748
+ return null;
1749
+ }
1750
+ if (isRef(value)) {
1751
+ return reactive ? resolveValue?.(property, value, target) ?? null : null;
1752
+ }
1753
+ if (typeof value === "object") {
1754
+ return null;
1755
+ }
1756
+ const raw = String(value);
1757
+ if (isTemplateString(raw)) {
1758
+ return reactive ? resolveValue?.(property, raw, target) ?? null : null;
1759
+ }
1760
+ return transposeValue(raw);
1761
+ };
1762
+ const declarationsOf = (node, skipSelectorKeys, reactive = true, target = "self") => {
1763
+ const declarations = [];
1764
+ for (const [key, value] of Object.entries(node)) {
1765
+ if (blocksOf(key, value).length > 0) {
1766
+ continue;
1767
+ }
1768
+ if (skipSelectorKeys && (key.startsWith("@") || isNestedSelectorKey(key))) {
1769
+ continue;
1770
+ }
1771
+ if (key.startsWith("$")) {
1772
+ continue;
1773
+ }
1774
+ const resolved = declarationValue(key, value, reactive, target);
1775
+ if (resolved !== null) {
1776
+ declarations.push([cssPropertyName(key), resolved]);
1777
+ }
1778
+ }
1779
+ return declarations;
1780
+ };
1781
+ const emit = (conditions, selector, declarations, target, description) => {
1782
+ if (declarations.length === 0) {
1783
+ return;
1784
+ }
1785
+ let resolved = selector;
1786
+ if (resolved !== null) {
1787
+ const transposed = transposeSelector(resolved);
1788
+ if (transposed === null) {
1789
+ return;
1790
+ }
1791
+ resolved = transposed;
1792
+ }
1793
+ const text = cssRuleText(conditions, resolved, declarations);
1794
+ rules.push({
1795
+ text,
1796
+ conditions: [...conditions],
1797
+ selector: resolved,
1798
+ declarations,
1799
+ target,
1800
+ key: hashCss(text),
1801
+ ...typeof description === "string" && description !== "" ? { description } : {}
1802
+ });
1803
+ };
1804
+ const emitKeyframes = (atKey, block, conditions) => {
1805
+ const name = atKey.slice("@keyframes".length).trim();
1806
+ if (name === "") {
1807
+ return;
1808
+ }
1809
+ const blocks = [];
1810
+ for (const [key, value] of Object.entries(block)) {
1811
+ if (!isBlock(value)) {
1812
+ continue;
1813
+ }
1814
+ const declarations = declarationsOf(value, true, false, "unscoped");
1815
+ if (declarations.length > 0) {
1816
+ blocks.push({ declarations, selector: key.trim() });
1817
+ }
1818
+ }
1819
+ if (blocks.length === 0) {
1820
+ return;
1821
+ }
1822
+ const text = cssKeyframesText(conditions, name, blocks);
1823
+ rules.push({
1824
+ blocks,
1825
+ conditions: [...conditions],
1826
+ declarations: [],
1827
+ key: hashCss(text),
1828
+ selector: null,
1829
+ target: "unscoped",
1830
+ text
1831
+ });
1832
+ };
1833
+ const walkAt = (atKey, block, selector, conditions, target) => {
1834
+ if (atKey === "@--") {
1835
+ return;
1836
+ }
1837
+ if (isDeclarationAtRule(atKey)) {
1838
+ emit([...conditions, atKey], null, declarationsOf(block, false, true, "unscoped"), "unscoped", block["$description"]);
1839
+ return;
1840
+ }
1841
+ if (isKeyframesAtRule(atKey)) {
1842
+ emitKeyframes(atKey, block, conditions);
1843
+ return;
1844
+ }
1845
+ const query = resolveAtQuery(atKey, mediaQueries);
1846
+ const atRule = query === null ? atKey : `@media ${query}`;
1847
+ const scheme = query === null ? null : pureSchemeOf(query);
1848
+ if (scheme !== null && selector !== null) {
1849
+ const { auto, forced } = schemeSelectors(selector, scheme);
1850
+ walk(block, auto, [...conditions, atRule], target);
1851
+ walk(withoutKeyframes(block), forced, conditions, target);
1852
+ return;
1853
+ }
1854
+ walk(block, selector, [...conditions, atRule], target);
1855
+ };
1856
+ function walk(node, selector, conditions, target) {
1857
+ emit(conditions, selector, declarationsOf(node, true, true, target), target, node["$description"]);
1858
+ for (const [key, value] of Object.entries(node)) {
1859
+ for (const block of blocksOf(key, value)) {
1860
+ if (key.startsWith("@")) {
1861
+ walkAt(key, block, selector, conditions, target);
1862
+ } else if (selector !== null) {
1863
+ walk(block, resolveNestedSelector(selector, key), conditions, compoundsOntoScope(key) ? target : "descendant");
1864
+ }
1865
+ }
1866
+ }
1867
+ }
1868
+ walk(style, scope, [], "self");
1869
+ return rules;
1870
+ }
1534
1871
 
1535
1872
  // ../schema/src/intl.ts
1536
1873
  var DEFAULT_FORMAT_LOCALE = "en-US";
@@ -1762,8 +2099,8 @@ function withPageLocale(path, args, state) {
1762
2099
  filled[index] = locale;
1763
2100
  return filled;
1764
2101
  }
1765
- function isBlessedGlobal(ref2) {
1766
- return ref2.startsWith("window#/") && BLESSED_GLOBALS.has(ref2.slice("window#/".length));
2102
+ function isBlessedGlobal(ref) {
2103
+ return ref.startsWith("window#/") && BLESSED_GLOBALS.has(ref.slice("window#/".length));
1767
2104
  }
1768
2105
  function isMutating(op) {
1769
2106
  return MUTATING_OPS.has(op);
@@ -1802,24 +2139,43 @@ function resolveOperand(operand, state, event, iterCtx, trace) {
1802
2139
  trace?.report(trace.path ?? [], operand);
1803
2140
  return operand;
1804
2141
  }
1805
- function resolveExprRef(ref2, state, event, iterCtx) {
1806
- if (ref2 === "$reduce/acc") {
2142
+ function calleeOwner(ref, state, event, iterCtx) {
2143
+ const parentRef = (prefix) => {
2144
+ const rest = ref.slice(prefix.length);
2145
+ if (!rest.includes("/")) {
2146
+ return state;
2147
+ }
2148
+ return resolveExprRef(ref.slice(0, ref.lastIndexOf("/")), state, event, iterCtx);
2149
+ };
2150
+ if (ref.startsWith("#/state/")) {
2151
+ return parentRef("#/state/");
2152
+ }
2153
+ if (ref.startsWith("parent#/")) {
2154
+ return parentRef("parent#/");
2155
+ }
2156
+ if (ref.startsWith("$map/") || ref.startsWith("$args/")) {
2157
+ return ref.split("/").length > 2 ? resolveExprRef(ref.slice(0, ref.lastIndexOf("/")), state, event, iterCtx) : undefined;
2158
+ }
2159
+ return;
2160
+ }
2161
+ function resolveExprRef(ref, state, event, iterCtx) {
2162
+ if (ref === "$reduce/acc") {
1807
2163
  return iterCtx?.acc;
1808
2164
  }
1809
- if (ref2.startsWith("$reduce/")) {
2165
+ if (ref.startsWith("$reduce/")) {
1810
2166
  return iterCtx?.acc;
1811
2167
  }
1812
- if (ref2.startsWith("$args/")) {
1813
- const parts = ref2.slice("$args/".length).split("/");
2168
+ if (ref.startsWith("$args/")) {
2169
+ const parts = ref.slice("$args/".length).split("/");
1814
2170
  const base = iterCtx?.args?.[parts[0]];
1815
2171
  return parts.length > 1 ? getPath(base, parts.slice(1).join("/")) : base;
1816
2172
  }
1817
- if (ref2.startsWith("event#/")) {
1818
- const path = ref2.slice("event#/".length);
2173
+ if (ref.startsWith("event#/")) {
2174
+ const path = ref.slice("event#/".length);
1819
2175
  return getPath(event, path);
1820
2176
  }
1821
- if (ref2.startsWith("$map/")) {
1822
- const parts = ref2.split("/");
2177
+ if (ref.startsWith("$map/")) {
2178
+ const parts = ref.split("/");
1823
2179
  const [, key] = parts;
1824
2180
  let base;
1825
2181
  const map = scopeMap(state);
@@ -1832,23 +2188,23 @@ function resolveExprRef(ref2, state, event, iterCtx) {
1832
2188
  }
1833
2189
  return parts.length > 2 ? getPath(base, parts.slice(2).join("/")) : base;
1834
2190
  }
1835
- if (ref2.startsWith("#/state/")) {
1836
- return readPath(state, ref2.slice("#/state/".length));
2191
+ if (ref.startsWith("#/state/")) {
2192
+ return readPath(state, ref.slice("#/state/".length));
1837
2193
  }
1838
- if (ref2.startsWith("parent#/")) {
1839
- return readPath(state, ref2.slice("parent#/".length));
2194
+ if (ref.startsWith("parent#/")) {
2195
+ return readPath(state, ref.slice("parent#/".length));
1840
2196
  }
1841
- if (ref2.startsWith("window#/")) {
1842
- return getPath(globalThis.window, ref2.slice("window#/".length));
2197
+ if (ref.startsWith("window#/")) {
2198
+ return getPath(globalThis.window, ref.slice("window#/".length));
1843
2199
  }
1844
- if (ref2.startsWith("document#/")) {
1845
- return getPath(globalThis.document, ref2.slice("document#/".length));
2200
+ if (ref.startsWith("document#/")) {
2201
+ return getPath(globalThis.document, ref.slice("document#/".length));
1846
2202
  }
1847
- return readPath(state, ref2) ?? null;
2203
+ return readPath(state, ref) ?? null;
1848
2204
  }
1849
- function resolveWritableRef(ref2, state, _event, iterCtx) {
1850
- if (ref2.startsWith("$map/")) {
1851
- const parts = ref2.split("/");
2205
+ function resolveWritableRef(ref, state, _event, iterCtx) {
2206
+ if (ref.startsWith("$map/")) {
2207
+ const parts = ref.split("/");
1852
2208
  const [, key] = parts;
1853
2209
  let base;
1854
2210
  const map = scopeMap(state);
@@ -1870,8 +2226,8 @@ function resolveWritableRef(ref2, state, _event, iterCtx) {
1870
2226
  obj: scopeMap(state) ?? state
1871
2227
  };
1872
2228
  }
1873
- if (ref2.startsWith("#/state/")) {
1874
- const sub = ref2.slice("#/state/".length);
2229
+ if (ref.startsWith("#/state/")) {
2230
+ const sub = ref.slice("#/state/".length);
1875
2231
  const slash = sub.indexOf("/");
1876
2232
  if (slash === -1) {
1877
2233
  return { key: sub, obj: state };
@@ -1884,7 +2240,7 @@ function resolveWritableRef(ref2, state, _event, iterCtx) {
1884
2240
  }
1885
2241
  return { key: lastKey, obj };
1886
2242
  }
1887
- return { key: ref2, obj: state };
2243
+ return { key: ref, obj: state };
1888
2244
  }
1889
2245
  function evaluateExpression(node, state, event, iterCtx, trace) {
1890
2246
  const result = evaluateNode(node, state, event, iterCtx, trace);
@@ -1931,19 +2287,19 @@ function evaluateNode(node, state, event, iterCtx, trace) {
1931
2287
  }
1932
2288
  const callee = resolveExprRef(calleeRef, state, event, iterCtx);
1933
2289
  if (typeof callee === "function") {
1934
- return callee(...argValues);
2290
+ return callee.apply(calleeOwner(calleeRef, state, event, iterCtx), argValues);
1935
2291
  }
1936
2292
  if (callee && typeof callee === "object" && "$expression" in callee) {
1937
- const def2 = callee;
2293
+ const def = callee;
1938
2294
  const args = {};
1939
- for (const [i, p] of (def2.parameters ?? []).entries()) {
2295
+ for (const [i, p] of (def.parameters ?? []).entries()) {
1940
2296
  const name = typeof p === "string" ? p : p?.name ?? "";
1941
2297
  if (!name) {
1942
2298
  continue;
1943
2299
  }
1944
2300
  args[name] = argValues[i] === undefined && typeof p === "object" && p !== null && "default" in p ? p.default : argValues[i];
1945
2301
  }
1946
- return evaluateExpression(def2.$expression, state, event, { args, callDepth });
2302
+ return evaluateExpression(def.$expression, state, event, { args, callDepth });
1947
2303
  }
1948
2304
  throw new TypeError(`$expression: call target "${calleeRef}" is not callable`);
1949
2305
  }
@@ -2185,10 +2541,10 @@ function isNonRelativeRef(value) {
2185
2541
  function normalizeProjectPath(path) {
2186
2542
  return path.replaceAll("\\", "/").replace(/^\.\//, "").replace(/^\/+/, "").replace(/\/+$/, "");
2187
2543
  }
2188
- function joinProjectPath(dir, ref2) {
2544
+ function joinProjectPath(dir, ref) {
2189
2545
  const base = dir === "" || dir === "." ? [] : normalizeProjectPath(dir).split("/");
2190
2546
  const out = base.filter((segment) => segment !== "" && segment !== ".");
2191
- for (const segment of ref2.replaceAll("\\", "/").split("/")) {
2547
+ for (const segment of ref.replaceAll("\\", "/").split("/")) {
2192
2548
  if (segment === "" || segment === ".") {
2193
2549
  continue;
2194
2550
  }
@@ -2310,6 +2666,12 @@ function statementKind(statement) {
2310
2666
  if ("$switch" in statement) {
2311
2667
  return "switch";
2312
2668
  }
2669
+ if ("stopPropagation" in statement) {
2670
+ return "stopPropagation";
2671
+ }
2672
+ if ("preventDefault" in statement) {
2673
+ return "preventDefault";
2674
+ }
2313
2675
  return "dispatch";
2314
2676
  }
2315
2677
  async function runStatements(statements, state, event, opts = {}) {
@@ -2343,7 +2705,8 @@ async function runStatements(statements, state, event, opts = {}) {
2343
2705
  }
2344
2706
  case "dispatch": {
2345
2707
  const dispatch = statement;
2346
- const target = opts.target ?? event?.currentTarget;
2708
+ const fallback = typeof opts.target === "function" ? opts.target() : opts.target;
2709
+ const target = event?.currentTarget ?? fallback;
2347
2710
  if (!target) {
2348
2711
  break;
2349
2712
  }
@@ -2360,6 +2723,14 @@ async function runStatements(statements, state, event, opts = {}) {
2360
2723
  target.dispatchEvent(new CustomEvent(dispatch.dispatchEvent, init));
2361
2724
  break;
2362
2725
  }
2726
+ case "stopPropagation": {
2727
+ event?.stopPropagation();
2728
+ break;
2729
+ }
2730
+ case "preventDefault": {
2731
+ event?.preventDefault();
2732
+ break;
2733
+ }
2363
2734
  default: {
2364
2735
  break;
2365
2736
  }
@@ -2407,10 +2778,14 @@ function serializeCookie(name, value, options) {
2407
2778
  }
2408
2779
  // ../runtime/src/runtime.ts
2409
2780
  var _resolveCache = new Map;
2410
- async function resolve(source) {
2781
+ async function resolve(source, ctx) {
2411
2782
  if (typeof source !== "string") {
2412
2783
  return source;
2413
2784
  }
2785
+ const served = ctx?.resolver?.(source);
2786
+ if (served) {
2787
+ return served;
2788
+ }
2414
2789
  if (_resolveCache.has(source)) {
2415
2790
  return _resolveCache.get(source);
2416
2791
  }
@@ -2495,10 +2870,36 @@ var _canvasDelinkAnchors = false;
2495
2870
  function setCanvasDelinkAnchors(on) {
2496
2871
  _canvasDelinkAnchors = on;
2497
2872
  }
2873
+ var _canvasDelinkPopovers = false;
2874
+ function setCanvasDelinkPopovers(on) {
2875
+ _canvasDelinkPopovers = on;
2876
+ }
2877
+ var _canvasDelinkCommands = false;
2878
+ function setCanvasDelinkCommands(on) {
2879
+ _canvasDelinkCommands = on;
2880
+ }
2881
+ var _canvasInsideStampedHost = false;
2882
+ function canvasStamped(el) {
2883
+ return _canvasInsideStampedHost || el.dataset.jxPath !== undefined;
2884
+ }
2498
2885
  function canvasAttrName(el, key) {
2499
2886
  if (_canvasDelinkAnchors && key === "href" && (el.tagName === "A" || el.tagName === "AREA")) {
2500
2887
  return "data-jx-href";
2501
2888
  }
2889
+ if (_canvasDelinkPopovers && key === "popover" && canvasStamped(el)) {
2890
+ return "data-jx-popover";
2891
+ }
2892
+ if (_canvasDelinkCommands && canvasStamped(el)) {
2893
+ if (key === "commandfor") {
2894
+ return "data-jx-commandfor";
2895
+ }
2896
+ if (key === "inert") {
2897
+ return "data-jx-inert";
2898
+ }
2899
+ if (key === "open" && el.tagName === "DIALOG") {
2900
+ return "data-jx-open";
2901
+ }
2902
+ }
2502
2903
  return key;
2503
2904
  }
2504
2905
  var _canvasAssetResolver = null;
@@ -2578,71 +2979,84 @@ function boundPropKey(key, val, state) {
2578
2979
  }
2579
2980
  return sub;
2580
2981
  }
2581
- async function buildScope(doc, parentScope = {}, base = location.href) {
2982
+ async function buildScope(doc, parentScope = {}, base = location.href, ctx) {
2983
+ const dispatchRoot = () => ctx?.root ?? null;
2582
2984
  const raw = {};
2583
- for (const [key, val] of Object.entries(parentScope)) {
2584
- raw[key] = val;
2585
- }
2586
2985
  const defs = doc.state ?? {};
2986
+ const linked = isReactive(parentScope);
2987
+ for (const key of Object.keys(parentScope)) {
2988
+ if (linked && !(key in defs)) {
2989
+ Object.defineProperty(raw, key, {
2990
+ configurable: true,
2991
+ enumerable: true,
2992
+ get: () => parentScope[key],
2993
+ set: (value) => {
2994
+ parentScope[key] = value;
2995
+ }
2996
+ });
2997
+ } else {
2998
+ raw[key] = parentScope[key];
2999
+ }
3000
+ }
2587
3001
  const imports = doc.imports ?? {};
2588
- for (const [, def2] of Object.entries(defs)) {
2589
- if (isPrototypeDef(def2) && !def2.$src) {
2590
- const mapped = imports[def2.$prototype];
3002
+ for (const [, def] of Object.entries(defs)) {
3003
+ if (isPrototypeDef(def) && !def.$src) {
3004
+ const mapped = imports[def.$prototype];
2591
3005
  if (mapped) {
2592
3006
  if (!mapped.endsWith(".class.json")) {
2593
- console.warn(`Jx: import "${def2.$prototype}" must map to a .class.json path, got "${mapped}"`);
3007
+ console.warn(`Jx: import "${def.$prototype}" must map to a .class.json path, got "${mapped}"`);
2594
3008
  continue;
2595
3009
  }
2596
- def2.$src = mapped;
3010
+ def.$src = mapped;
2597
3011
  }
2598
3012
  }
2599
3013
  }
2600
- for (const [key, def2] of Object.entries(defs)) {
2601
- if (typeof def2 === "string") {
2602
- if (!def2.includes("${")) {
2603
- raw[key] = def2;
3014
+ for (const [key, def] of Object.entries(defs)) {
3015
+ if (typeof def === "string") {
3016
+ if (!def.includes("${")) {
3017
+ raw[key] = def;
2604
3018
  }
2605
3019
  continue;
2606
3020
  }
2607
- if (typeof def2 === "number" || typeof def2 === "boolean" || def2 === null) {
2608
- raw[key] = def2;
3021
+ if (typeof def === "number" || typeof def === "boolean" || def === null) {
3022
+ raw[key] = def;
2609
3023
  continue;
2610
3024
  }
2611
- if (Array.isArray(def2)) {
2612
- raw[key] = def2;
3025
+ if (Array.isArray(def)) {
3026
+ raw[key] = def;
2613
3027
  continue;
2614
3028
  }
2615
- if (typeof def2 === "object") {
2616
- if (def2.$prototype) {
3029
+ if (typeof def === "object") {
3030
+ if (def.$prototype) {
2617
3031
  continue;
2618
3032
  }
2619
- if (isExpressionDef(def2)) {
3033
+ if (isExpressionDef(def)) {
2620
3034
  continue;
2621
3035
  }
2622
- if (isServerFnDef(def2)) {
3036
+ if (isServerFnDef(def)) {
2623
3037
  continue;
2624
3038
  }
2625
- if ("default" in def2) {
2626
- raw[key] = def2.default;
3039
+ if ("default" in def) {
3040
+ raw[key] = def.default;
2627
3041
  continue;
2628
3042
  }
2629
- if (hasSchemaKeywords(def2)) {
3043
+ if (hasSchemaKeywords(def)) {
2630
3044
  continue;
2631
3045
  }
2632
- raw[key] = def2;
3046
+ raw[key] = def;
2633
3047
  }
2634
3048
  }
2635
3049
  const state = reactive(raw);
2636
- for (const [key, def2] of Object.entries(defs)) {
2637
- if (typeof def2 === "string" && def2.includes("${")) {
2638
- state[key] = computed(() => evaluateTemplate(def2, state));
3050
+ for (const [key, def] of Object.entries(defs)) {
3051
+ if (typeof def === "string" && def.includes("${")) {
3052
+ state[key] = computed(() => evaluateTemplate(def, state));
2639
3053
  }
2640
3054
  }
2641
- for (const [key, def2] of Object.entries(defs)) {
2642
- if (isExpressionDef(def2)) {
2643
- const node = def2.$expression;
2644
- if (isNamedFormulaDef(def2)) {
2645
- const params = def2.parameters;
3055
+ for (const [key, def] of Object.entries(defs)) {
3056
+ if (isExpressionDef(def)) {
3057
+ const node = def.$expression;
3058
+ if (isNamedFormulaDef(def)) {
3059
+ const params = def.parameters;
2646
3060
  state[key] = (...argValues) => {
2647
3061
  const args = {};
2648
3062
  for (const [i, p] of params.entries()) {
@@ -2662,11 +3076,11 @@ async function buildScope(doc, parentScope = {}, base = location.href) {
2662
3076
  }
2663
3077
  }
2664
3078
  }
2665
- for (const [key, def2] of Object.entries(defs)) {
2666
- if (hasStructuredBody(def2)) {
2667
- const { body } = def2;
2668
- if (Array.isArray(def2.parameters) && def2.parameters.length > 0) {
2669
- const params = def2.parameters;
3079
+ for (const [key, def] of Object.entries(defs)) {
3080
+ if (hasStructuredBody(def)) {
3081
+ const { body } = def;
3082
+ if (Array.isArray(def.parameters) && def.parameters.length > 0) {
3083
+ const params = def.parameters;
2670
3084
  state[key] = (...argValues) => {
2671
3085
  const args = {};
2672
3086
  for (const [i, p] of params.entries()) {
@@ -2676,34 +3090,35 @@ async function buildScope(doc, parentScope = {}, base = location.href) {
2676
3090
  }
2677
3091
  args[name] = argValues[i] === undefined && typeof p === "object" && p !== null && "default" in p ? p.default : argValues[i];
2678
3092
  }
2679
- return runStatements(body, state, null, { args });
3093
+ return runStatements(body, state, null, { args, target: dispatchRoot });
2680
3094
  };
2681
3095
  } else {
2682
3096
  const handler = (s, event) => {
2683
- runStatements(body, s, event ?? null);
3097
+ runStatements(body, s, event ?? null, { target: dispatchRoot });
2684
3098
  };
2685
3099
  state[key] = handler;
2686
3100
  }
2687
- } else if (isFunctionDef(def2)) {
2688
- state[key] = await resolveFunction(def2, state, key, base);
3101
+ } else if (isFunctionDef(def)) {
3102
+ state[key] = await resolveFunction(def, state, key, base);
2689
3103
  }
2690
3104
  }
2691
- for (const [key, def2] of Object.entries(defs)) {
2692
- if (isPrototypeDef(def2)) {
2693
- state[key] = await resolvePrototype(def2, state, key, base);
3105
+ for (const [key, def] of Object.entries(defs)) {
3106
+ if (isPrototypeDef(def)) {
3107
+ state[key] = await resolvePrototype(def, state, key, base, ctx);
2694
3108
  }
2695
3109
  }
2696
- if (!_serverFnConfig.skip) {
2697
- for (const [key, def2] of Object.entries(defs)) {
2698
- if (isServerFnDef(def2)) {
2699
- state[key] = await resolveServerFunction(def2, state, key, base);
3110
+ if (!(ctx?.skipServerFunctions ?? _serverFnConfig.skip)) {
3111
+ for (const [key, def] of Object.entries(defs)) {
3112
+ if (isServerFnDef(def)) {
3113
+ state[key] = await resolveServerFunction(def, state, key, base);
2700
3114
  }
2701
3115
  }
2702
3116
  }
3117
+ const rootMedia = ctx?.media ?? _rootMedia;
2703
3118
  if (doc.$media) {
2704
3119
  state["$media"] = doc.$media;
2705
- } else if (!state["$media"] && Object.keys(_rootMedia).length > 0) {
2706
- state["$media"] = _rootMedia;
3120
+ } else if (!state["$media"] && Object.keys(rootMedia).length > 0) {
3121
+ state["$media"] = rootMedia;
2707
3122
  }
2708
3123
  return state;
2709
3124
  }
@@ -2747,36 +3162,72 @@ function evaluateAttrTemplate(str, state) {
2747
3162
  return fn(state, $map, $map?.item, $map?.index);
2748
3163
  }
2749
3164
  var _moduleCache = new Map;
2750
- async function resolveFunction(def2, state, key, base) {
2751
- if (def2.body && def2.$src) {
3165
+ var _moduleLoaders = new Map;
3166
+ function preloadModule(specifier, mod) {
3167
+ if (typeof mod === "function") {
3168
+ _moduleCache.delete(specifier);
3169
+ _moduleLoaders.set(specifier, mod);
3170
+ return;
3171
+ }
3172
+ _moduleLoaders.delete(specifier);
3173
+ _moduleCache.set(specifier, mod);
3174
+ }
3175
+ function seededModule(src) {
3176
+ const cached = _moduleCache.get(src);
3177
+ if (cached) {
3178
+ return cached;
3179
+ }
3180
+ const pending = _moduleLoaders.get(src);
3181
+ if (!pending) {
3182
+ return;
3183
+ }
3184
+ if (typeof pending !== "function") {
3185
+ return pending;
3186
+ }
3187
+ const load = Promise.resolve(pending()).then((mod) => {
3188
+ _moduleCache.set(src, mod);
3189
+ _moduleLoaders.delete(src);
3190
+ return mod;
3191
+ }, (error) => {
3192
+ _moduleLoaders.set(src, pending);
3193
+ throw error;
3194
+ });
3195
+ _moduleLoaders.set(src, load);
3196
+ return load;
3197
+ }
3198
+ async function resolveFunction(def, state, key, base) {
3199
+ if (def.body && def.$src) {
2752
3200
  throw new Error(`Jx: '${key}' declares both body and $src — these are mutually exclusive`);
2753
3201
  }
2754
- if (!def2.body && !def2.$src) {
2755
- const params = resolveParamNames(def2);
3202
+ if (!def.body && !def.$src) {
3203
+ const params = resolveParamNames(def);
2756
3204
  const noop = new Function(...params, "");
2757
3205
  Object.defineProperty(noop, "name", {
2758
3206
  configurable: true,
2759
- value: def2.name ?? key
3207
+ value: def.name ?? key
2760
3208
  });
2761
3209
  return noop;
2762
3210
  }
2763
3211
  let fn;
2764
- if (typeof def2.body === "string") {
2765
- const params = resolveParamNames(def2);
2766
- fn = new Function(...params, def2.body);
3212
+ if (typeof def.body === "string") {
3213
+ const params = resolveParamNames(def);
3214
+ fn = new Function(...params, def.body);
2767
3215
  Object.defineProperty(fn, "name", {
2768
3216
  configurable: true,
2769
- value: def2.name ?? key
3217
+ value: def.name ?? key
2770
3218
  });
2771
3219
  } else {
2772
- const src = def2.$src;
3220
+ const src = def.$src;
2773
3221
  if (!src) {
2774
3222
  throw new Error(`Jx: '${key}' has neither body nor $src`);
2775
3223
  }
2776
- const exportName = def2.$export ?? key;
3224
+ const exportName = def.$export ?? key;
2777
3225
  let mod;
2778
- if (_moduleCache.has(src)) {
2779
- mod = _moduleCache.get(src);
3226
+ const seeded = seededModule(src);
3227
+ if (seeded instanceof Promise) {
3228
+ mod = await seeded;
3229
+ } else if (seeded) {
3230
+ mod = seeded;
2780
3231
  } else {
2781
3232
  if (base) {
2782
3233
  const resolvedSrc = new URL(src, base).href;
@@ -2796,11 +3247,11 @@ async function resolveFunction(def2, state, key, base) {
2796
3247
  }
2797
3248
  fn = candidate;
2798
3249
  }
2799
- const hasParams = (def2.parameters ?? def2.arguments ?? []).length > 0;
3250
+ const hasParams = (def.parameters ?? def.arguments ?? []).length > 0;
2800
3251
  let isComputed = false;
2801
3252
  if (!hasParams) {
2802
- if (typeof def2.body === "string") {
2803
- isComputed = bodyReturnsValue(def2.body);
3253
+ if (typeof def.body === "string") {
3254
+ isComputed = bodyReturnsValue(def.body);
2804
3255
  } else if (fn) {
2805
3256
  isComputed = fn.length <= 1 && bodyReturnsValue(fn.toString());
2806
3257
  }
@@ -2810,8 +3261,8 @@ async function resolveFunction(def2, state, key, base) {
2810
3261
  }
2811
3262
  return fn;
2812
3263
  }
2813
- function resolveParamNames(def2) {
2814
- const names = def2.parameters ? paramNames(def2.parameters) : def2.arguments ?? [];
3264
+ function resolveParamNames(def) {
3265
+ const names = def.parameters ? paramNames(def.parameters) : def.arguments ?? [];
2815
3266
  return names.length > 0 && names[0] === "state" ? names : ["state", ...names];
2816
3267
  }
2817
3268
  var RESERVED_KEYS = new Set([
@@ -2860,19 +3311,19 @@ function elementNamespace(tagName, inherited) {
2860
3311
  }
2861
3312
  return inherited ?? null;
2862
3313
  }
2863
- function renderNode(def2, state, options) {
3314
+ function renderNode(def, state, options) {
2864
3315
  const path = options?._path ?? [];
2865
- if (typeof def2 === "string" || typeof def2 === "number" || typeof def2 === "boolean") {
2866
- const textNode = document.createTextNode(String(def2));
2867
- if (typeof def2 === "string" && isTemplateString(def2)) {
3316
+ if (typeof def === "string" || typeof def === "number" || typeof def === "boolean") {
3317
+ const textNode = document.createTextNode(String(def));
3318
+ if (typeof def === "string" && isTemplateString(def)) {
2868
3319
  effect(() => {
2869
- textNode.textContent = evaluateTemplate(def2, state);
3320
+ textNode.textContent = evaluateTemplate(def, state);
2870
3321
  });
2871
3322
  }
2872
3323
  return textNode;
2873
3324
  }
2874
3325
  let localState = state;
2875
- for (const [key, val] of Object.entries(def2)) {
3326
+ for (const [key, val] of Object.entries(def)) {
2876
3327
  if (key.startsWith("$") && !RESERVED_KEYS.has(key)) {
2877
3328
  if (localState === state) {
2878
3329
  localState = Object.create(state);
@@ -2880,38 +3331,41 @@ function renderNode(def2, state, options) {
2880
3331
  localState[key] = isRefObj(val) ? resolveRef(val.$ref, state) : val;
2881
3332
  }
2882
3333
  }
2883
- if ("$ref" in def2 && !def2.tagName) {
2884
- const refTarget = String(def2.$ref ?? "");
3334
+ if ("$ref" in def && !def.tagName) {
3335
+ const refTarget = String(def.$ref ?? "");
2885
3336
  if (!warnedRefChildren.has(refTarget)) {
2886
3337
  warnedRefChildren.add(refTarget);
2887
3338
  console.warn(`Jx: a $ref child ("${refTarget}") is not a supported component instance; register the ` + `component in $elements and use its custom-element tag instead (spec §13).`);
2888
3339
  }
2889
3340
  }
2890
- const tagName = resolveTagName(def2.tagName, localState);
3341
+ const tagName = resolveTagName(def.tagName, localState);
2891
3342
  const isCustomEl = tagName.includes("-") && customElements.get(tagName);
2892
- if (def2.$props && isCustomEl) {
2893
- return renderCustomElementWithProps(def2, localState, options, path);
3343
+ if (def.$props && isCustomEl) {
3344
+ return renderCustomElementWithProps(def, localState, options, path);
2894
3345
  }
2895
- if (def2.$props) {
3346
+ if (def.$props) {
2896
3347
  if (tagName.includes("-")) {
2897
- return renderCustomElementWithProps(def2, localState, options, path);
3348
+ return renderCustomElementWithProps(def, localState, options, path);
2898
3349
  }
2899
- const { $props: _$props, ...rest } = def2;
2900
- return renderNode(rest, mergeProps(def2, localState), options);
3350
+ const { $props: _$props, ...rest } = def;
3351
+ return renderNode(rest, mergeProps(def, localState), options);
2901
3352
  }
2902
- if (def2.$switch) {
2903
- return renderSwitch(def2, localState, options);
3353
+ if (def.$switch) {
3354
+ return renderSwitch(def, localState, options);
2904
3355
  }
2905
3356
  const ns = elementNamespace(tagName, options?._ns);
2906
3357
  const childNs = tagName === "foreignObject" ? null : ns;
2907
3358
  const el = ns ? document.createElementNS(ns, tagName) : document.createElement(tagName);
2908
3359
  if (options?.onNodeCreated) {
2909
- options.onNodeCreated(el, path, def2, localState);
3360
+ options.onNodeCreated(el, path, def, localState);
3361
+ }
3362
+ applyProperties(el, def, localState);
3363
+ if (def.style && tagName.includes("-")) {
3364
+ callSiteStyles.set(el, def.style);
2910
3365
  }
2911
- applyProperties(el, def2, localState);
2912
- applyStyle(el, def2.style ?? {}, localState["$media"] ?? {}, localState);
2913
- applyAttributes(el, def2.attributes ?? {}, localState);
2914
- const kids = def2.children;
3366
+ applyStyle(el, def.style ?? {}, localState["$media"] ?? {}, localState);
3367
+ applyAttributes(el, def.attributes ?? {}, localState);
3368
+ const kids = def.children;
2915
3369
  if (isMappedArray(kids)) {
2916
3370
  const arrOpts = options || childNs ? { ...options, _ns: childNs, _path: [...path, "children"] } : undefined;
2917
3371
  renderMappedArrayInto(el, kids, localState, arrOpts);
@@ -2928,49 +3382,58 @@ function renderNode(def2, state, options) {
2928
3382
  }
2929
3383
  return el;
2930
3384
  }
2931
- function applyProperties(el, def2, state) {
2932
- for (const [key, val] of Object.entries(def2)) {
3385
+ function applyProperties(el, def, state) {
3386
+ for (const [key, val] of Object.entries(def)) {
2933
3387
  if (RESERVED_KEYS.has(key)) {
2934
3388
  continue;
2935
3389
  }
2936
3390
  if (key.startsWith("$")) {
2937
3391
  continue;
2938
3392
  }
2939
- if (key.startsWith("on")) {
2940
- if (isRefObj(val)) {
2941
- const handler = resolveRef(val.$ref, state);
2942
- if (typeof handler === "function") {
2943
- const scope = state;
2944
- const handlerFn = handler;
2945
- el.addEventListener(key.slice(2), (e) => handlerFn(scope, e));
2946
- }
2947
- continue;
2948
- }
2949
- if (hasStructuredBody(val)) {
2950
- const { body } = val;
2951
- const scope = state;
2952
- el.addEventListener(key.slice(2), (e) => {
2953
- runStatements(body, scope, e);
2954
- });
2955
- continue;
2956
- }
2957
- if (isFunctionDef(val) && typeof val.body === "string") {
2958
- const params = resolveParamNames(val);
2959
- const fn = new Function(...params, val.body);
2960
- const scope = state;
2961
- el.addEventListener(key.slice(2), (e) => fn(scope, e));
2962
- continue;
2963
- }
2964
- if (isExpressionDef(val)) {
2965
- const node = val.$expression;
2966
- const scope = state;
2967
- el.addEventListener(key.slice(2), (e) => evaluateExpression(node, scope, e));
2968
- continue;
2969
- }
3393
+ if (key.startsWith("on") && bindHandler(el, key, val, state)) {
3394
+ continue;
2970
3395
  }
2971
3396
  bindProperty(el, key, val, state);
2972
3397
  }
2973
3398
  }
3399
+ function bindHandler(el, key, val, state) {
3400
+ const type = key.slice(2);
3401
+ const scope = state;
3402
+ if (isRefObj(val)) {
3403
+ const handler = resolveRef(val.$ref, state);
3404
+ if (typeof handler === "function") {
3405
+ const handlerFn = handler;
3406
+ el.addEventListener(type, (e) => handlerFn(scope, e));
3407
+ }
3408
+ return true;
3409
+ }
3410
+ if (hasStructuredBody(val)) {
3411
+ const { body } = val;
3412
+ el.addEventListener(type, (e) => {
3413
+ runStatements(body, scope, e);
3414
+ });
3415
+ return true;
3416
+ }
3417
+ if (isFunctionDef(val) && typeof val.body === "string") {
3418
+ const params = resolveParamNames(val);
3419
+ const fn = new Function(...params, val.body);
3420
+ el.addEventListener(type, (e) => fn(scope, e));
3421
+ return true;
3422
+ }
3423
+ if (isExpressionDef(val)) {
3424
+ const node = val.$expression;
3425
+ el.addEventListener(type, (e) => evaluateExpression(node, scope, e));
3426
+ return true;
3427
+ }
3428
+ return false;
3429
+ }
3430
+ function bindDefinitionHandlers(host, def, state) {
3431
+ for (const [key, val] of Object.entries(def)) {
3432
+ if (key.startsWith("on") && !RESERVED_KEYS.has(key)) {
3433
+ bindHandler(host, key, val, state);
3434
+ }
3435
+ }
3436
+ }
2974
3437
  function bindProperty(el, key, val, state) {
2975
3438
  const target = el;
2976
3439
  const boundProp = boundPropKey(key, val, state);
@@ -2984,6 +3447,9 @@ function bindProperty(el, key, val, state) {
2984
3447
  node.setAttribute("class", resolved == null ? "" : String(resolved));
2985
3448
  return;
2986
3449
  }
3450
+ if (target[key] === resolved) {
3451
+ return;
3452
+ }
2987
3453
  target[key] = resolved;
2988
3454
  };
2989
3455
  if (isRefObj(val)) {
@@ -3005,142 +3471,287 @@ function bindProperty(el, key, val, state) {
3005
3471
  }
3006
3472
  write(asAsset(val));
3007
3473
  }
3008
- function applyStyle(el, styleDef, mediaQueries = {}, state = {}) {
3009
- const nested = {};
3010
- const media = {};
3011
- const baseDecls = {};
3012
- const mediaOverriddenProps = new Set;
3013
- for (const [prop, val] of Object.entries(styleDef)) {
3014
- if (prop.startsWith("@") && val && typeof val === "object") {
3015
- for (const k of Object.keys(val)) {
3016
- if (!k.startsWith(":") && !k.startsWith(".") && !k.startsWith("&") && !k.startsWith("[") && !k.startsWith("@")) {
3017
- mediaOverriddenProps.add(k);
3474
+ function cssomSink(sheet, detach) {
3475
+ const entries = [];
3476
+ return {
3477
+ detach,
3478
+ insert(texts) {
3479
+ const rules = [];
3480
+ for (const text of texts) {
3481
+ const index = sheet.cssRules.length;
3482
+ try {
3483
+ sheet.insertRule(text, index);
3484
+ } catch {
3485
+ continue;
3486
+ }
3487
+ const rule = sheet.cssRules[index];
3488
+ if (rule) {
3489
+ rules.push(rule);
3018
3490
  }
3019
3491
  }
3020
- }
3021
- }
3022
- for (const [prop, val] of Object.entries(styleDef)) {
3023
- if (val !== null && typeof val === "object" && !Array.isArray(val)) {
3024
- if (prop.startsWith("@")) {
3025
- media[prop] = val;
3026
- } else {
3027
- nested[prop] = val;
3492
+ const inserted = { rules, texts };
3493
+ entries.push(inserted);
3494
+ return inserted;
3495
+ },
3496
+ remove(inserted) {
3497
+ const at = entries.indexOf(inserted);
3498
+ if (at !== -1) {
3499
+ entries.splice(at, 1);
3028
3500
  }
3029
- continue;
3030
- }
3031
- if (prop.startsWith("@") || isNestedSelector(prop)) {
3032
- continue;
3033
- }
3034
- if (val === undefined) {
3035
- continue;
3501
+ if (inserted.rules.length === 0) {
3502
+ return;
3503
+ }
3504
+ const list = [...sheet.cssRules];
3505
+ const indices = inserted.rules.map((rule) => list.indexOf(rule)).filter((index) => index >= 0).toSorted((a, b) => b - a);
3506
+ for (const index of indices) {
3507
+ sheet.deleteRule(index);
3508
+ }
3509
+ inserted.rules.length = 0;
3510
+ },
3511
+ replaceAll(texts) {
3512
+ entries.length = 0;
3513
+ entries.push({ rules: [], texts });
3514
+ sheet.replaceSync(texts.join(`
3515
+ `));
3516
+ },
3517
+ text() {
3518
+ return entries.flatMap((entry) => entry.texts.join(`
3519
+ `)).join(`
3520
+ `);
3036
3521
  }
3037
- const scalar = String(val);
3038
- if (prop.startsWith("--")) {
3039
- if (isTemplateString(val)) {
3040
- effect(() => {
3041
- el.style.setProperty(prop, canvasStyleValue(evaluateTemplate(val, state)));
3042
- });
3043
- } else {
3044
- el.style.setProperty(prop, canvasStyleValue(scalar));
3522
+ };
3523
+ }
3524
+ function textSink(tag) {
3525
+ const entries = [];
3526
+ const write = () => {
3527
+ tag.textContent = entries.map((entry) => entry.texts.join(`
3528
+ `)).join(`
3529
+ `);
3530
+ };
3531
+ return {
3532
+ detach() {
3533
+ tag.remove();
3534
+ },
3535
+ insert(texts) {
3536
+ const inserted = { rules: [], texts };
3537
+ entries.push(inserted);
3538
+ write();
3539
+ return inserted;
3540
+ },
3541
+ remove(inserted) {
3542
+ const index = entries.indexOf(inserted);
3543
+ if (index !== -1) {
3544
+ entries.splice(index, 1);
3545
+ write();
3045
3546
  }
3046
- } else if (isTemplateString(val)) {
3047
- effect(() => {
3048
- el.style[prop] = canvasStyleValue(evaluateTemplate(val, state));
3049
- });
3050
- } else if (mediaOverriddenProps.has(prop)) {
3051
- baseDecls[prop] = scalar;
3052
- } else {
3053
- el.style[prop] = canvasStyleValue(scalar);
3547
+ },
3548
+ replaceAll(texts) {
3549
+ entries.length = 0;
3550
+ entries.push({ rules: [], texts });
3551
+ write();
3552
+ },
3553
+ text() {
3554
+ return tag.textContent ?? "";
3054
3555
  }
3556
+ };
3557
+ }
3558
+ function openSink(doc) {
3559
+ const view = doc.defaultView;
3560
+ const SheetConstructor = view?.CSSStyleSheet;
3561
+ if (typeof SheetConstructor === "function" && Array.isArray(doc.adoptedStyleSheets)) {
3562
+ try {
3563
+ const sheet = new SheetConstructor;
3564
+ doc.adoptedStyleSheets = [...doc.adoptedStyleSheets, sheet];
3565
+ return cssomSink(sheet, () => {
3566
+ doc.adoptedStyleSheets = doc.adoptedStyleSheets.filter((s) => s !== sheet);
3567
+ });
3568
+ } catch {}
3055
3569
  }
3056
- const hasNested = Object.keys(nested).length > 0;
3057
- const hasMedia = Object.keys(media).length > 0;
3058
- const hasBaseDecls = Object.keys(baseDecls).length > 0;
3059
- if (!hasNested && !hasMedia && !hasBaseDecls) {
3570
+ const tag = doc.createElement("style");
3571
+ tag.dataset.jxSheet = "";
3572
+ (doc.head ?? doc.documentElement).append(tag);
3573
+ return tag.sheet ? cssomSink(tag.sheet, () => tag.remove()) : textSink(tag);
3574
+ }
3575
+ var sheetStates = new WeakMap;
3576
+ function sheetStateFor(doc) {
3577
+ const existing = sheetStates.get(doc);
3578
+ if (existing) {
3579
+ return existing;
3580
+ }
3581
+ const created = {
3582
+ hoisted: new Map,
3583
+ interned: new Map,
3584
+ scratch: null,
3585
+ scratchOwners: new Map,
3586
+ serial: 0,
3587
+ shared: openSink(doc)
3588
+ };
3589
+ sheetStates.set(doc, created);
3590
+ return created;
3591
+ }
3592
+ var elementStyles = new WeakMap;
3593
+ var SCOPE_TOKEN = "\x01jx-scope\x01";
3594
+ function rewriteScratch(state) {
3595
+ if (!state.scratch) {
3060
3596
  return;
3061
3597
  }
3062
- const uid = `jx-${Math.random().toString(36).slice(2, 7)}`;
3063
- el.dataset.jx = uid;
3064
- let css = "";
3065
- const baseCSS = toCSSText(baseDecls);
3066
- if (baseCSS) {
3067
- css += `[data-jx="${uid}"] { ${baseCSS} }
3068
- `;
3069
- }
3070
- function emitNested(scope, rules) {
3071
- const props = toCSSText(rules);
3072
- if (props) {
3073
- css += `${scope} { ${props} }
3074
- `;
3075
- }
3076
- for (const [sel, sub] of Object.entries(rules)) {
3077
- if (sub === null || typeof sub !== "object" || Array.isArray(sub)) {
3078
- continue;
3079
- }
3080
- if (sel.startsWith("@")) {
3081
- continue;
3082
- }
3083
- const resolved = sel.startsWith("&") ? sel.replace("&", scope) : sel.startsWith("[") ? `${scope}${sel}` : sel.startsWith(":") || sel.startsWith(".") ? `${scope}${sel}` : `${scope} ${sel}`;
3084
- emitNested(resolved, sub);
3085
- }
3086
- }
3087
- for (const [sel, rules] of Object.entries(nested)) {
3088
- const resolved = sel.startsWith("&") ? sel.replace("&", `[data-jx="${uid}"]`) : sel.startsWith("[") ? `[data-jx="${uid}"]${sel}` : sel.startsWith(":") || sel.startsWith(".") ? `[data-jx="${uid}"]${sel}` : `[data-jx="${uid}"] ${sel}`;
3089
- emitNested(resolved, rules);
3598
+ state.scratch.replaceAll([...state.scratchOwners.values()].flat());
3599
+ }
3600
+ function releaseInterned(state, table, key) {
3601
+ const entry = table.get(key);
3602
+ if (!entry) {
3603
+ return;
3090
3604
  }
3091
- function emitMediaNested(atRule, parentSel, obj) {
3092
- for (const [sel, sub] of Object.entries(obj)) {
3093
- if (sub === null || typeof sub !== "object" || Array.isArray(sub)) {
3094
- continue;
3095
- }
3096
- if (sel.startsWith("@")) {
3097
- continue;
3098
- }
3099
- const resolved = sel.startsWith("&") ? sel.replace("&", parentSel) : sel.startsWith("[") ? `${parentSel}${sel}` : sel.startsWith(":") || sel.startsWith(".") ? `${parentSel}${sel}` : `${parentSel} ${sel}`;
3100
- const props = toCSSText(sub);
3101
- if (props) {
3102
- css += `${atRule} { ${resolved} { ${props} } }
3103
- `;
3104
- }
3105
- emitMediaNested(atRule, resolved, sub);
3106
- }
3605
+ entry.refs -= 1;
3606
+ if (entry.refs <= 0) {
3607
+ table.delete(key);
3608
+ state.shared.remove(entry.inserted);
3107
3609
  }
3108
- for (const [key, rules] of Object.entries(media)) {
3109
- if (key === "@--") {
3110
- continue;
3111
- }
3112
- const query = resolveAtQuery(key, mediaQueries);
3113
- const atRule = query === null ? key : `@media ${query}`;
3114
- const scope = `[data-jx="${uid}"]`;
3115
- const scheme = query === null ? null : pureSchemeOf(query);
3116
- if (scheme) {
3117
- const { auto, forced } = schemeSelectors(scope, scheme);
3118
- css += `${atRule} { ${auto} { ${toCSSText(rules)} } }
3119
- `;
3120
- emitMediaNested(atRule, auto, rules);
3121
- emitNested(forced, rules);
3122
- } else {
3123
- css += `${atRule} { ${scope} { ${toCSSText(rules)} } }
3124
- `;
3125
- emitMediaNested(atRule, scope, rules);
3126
- }
3610
+ }
3611
+ function retainInterned(state, table, key, texts) {
3612
+ const existing = table.get(key);
3613
+ if (existing) {
3614
+ existing.refs += 1;
3615
+ return;
3127
3616
  }
3128
- const tag = document.createElement("style");
3129
- tag.textContent = css;
3130
- tag.dataset.jxOwner = uid;
3131
- document.head.append(tag);
3132
- elementStyleTags.set(el, tag);
3617
+ table.set(key, { inserted: state.shared.insert(texts), refs: 1 });
3133
3618
  }
3134
- var elementStyleTags = new WeakMap;
3135
- function reapplyStyle(el, styleDef, mediaQueries = {}, state = {}) {
3136
- const prev = elementStyleTags.get(el);
3137
- if (prev) {
3138
- prev.remove();
3139
- elementStyleTags.delete(el);
3619
+ function releaseElementStyles(el, only) {
3620
+ const entry = elementStyles.get(el);
3621
+ if (!entry || only !== undefined && entry !== only) {
3622
+ return;
3623
+ }
3624
+ elementStyles.delete(el);
3625
+ entry.stop?.();
3626
+ for (const name of entry.vars) {
3627
+ el.style.removeProperty(name);
3140
3628
  }
3141
- el.style.cssText = "";
3142
3629
  delete el.dataset.jx;
3143
- applyStyle(el, styleDef ?? {}, mediaQueries, state);
3630
+ if (sheetStates.get(entry.doc) !== entry.state) {
3631
+ return;
3632
+ }
3633
+ const { state } = entry;
3634
+ if (entry.interned === null) {
3635
+ if (state.scratchOwners.delete(el)) {
3636
+ rewriteScratch(state);
3637
+ }
3638
+ } else {
3639
+ releaseInterned(state, state.interned, entry.interned);
3640
+ }
3641
+ for (const key of entry.hoisted) {
3642
+ releaseInterned(state, state.hoisted, key);
3643
+ }
3644
+ }
3645
+ function resetDocumentStyles(doc = document) {
3646
+ const state = sheetStates.get(doc);
3647
+ if (!state) {
3648
+ return;
3649
+ }
3650
+ sheetStates.delete(doc);
3651
+ for (const el of state.scratchOwners.keys()) {
3652
+ elementStyles.delete(el);
3653
+ delete el.dataset.jx;
3654
+ }
3655
+ state.scratchOwners.clear();
3656
+ state.interned.clear();
3657
+ state.hoisted.clear();
3658
+ state.shared.detach();
3659
+ state.scratch?.detach();
3660
+ }
3661
+ function declaresDisplay(style) {
3662
+ return Boolean(style) && typeof style === "object" && "display" in style;
3663
+ }
3664
+ var callSiteStyles = new WeakMap;
3665
+ function mergeStyle(base, over) {
3666
+ if (!over) {
3667
+ return base;
3668
+ }
3669
+ const out = { ...base };
3670
+ for (const [key, value] of Object.entries(over)) {
3671
+ const prior = out[key];
3672
+ out[key] = prior !== null && typeof prior === "object" && value !== null && typeof value === "object" && !Array.isArray(value) ? mergeStyle(prior, value) : value;
3673
+ }
3674
+ return out;
3675
+ }
3676
+ function applyStyle(el, styleDef, mediaQueries = {}, state = {}) {
3677
+ applyStyleInto(el, styleDef, mediaQueries, state, false);
3678
+ }
3679
+ function reapplyStyle(el, styleDef, mediaQueries = {}, state = {}) {
3680
+ applyStyleInto(el, styleDef ?? {}, mediaQueries, state, true);
3681
+ }
3682
+ function applyStyleInto(el, styleDef, mediaQueries, state, live) {
3683
+ releaseElementStyles(el);
3684
+ const doc = el.ownerDocument ?? document;
3685
+ const sheetState = sheetStateFor(doc);
3686
+ const transposeSelector = (_canvasDelinkPopovers || _canvasDelinkCommands) && canvasStamped(el) ? (selector) => transposeCanvasOverlaySelector(selector, {
3687
+ dialog: el.tagName === "DIALOG"
3688
+ }) : (selector) => selector;
3689
+ let serial = -1;
3690
+ let indirect = 0;
3691
+ const sources = [];
3692
+ const rules = buildStyleRules(styleDef, {
3693
+ mediaQueries,
3694
+ resolveValue: (property, value, target) => {
3695
+ if (target === "self" && property.startsWith("--")) {
3696
+ sources.push({ name: property, value });
3697
+ return null;
3698
+ }
3699
+ if (serial < 0) {
3700
+ ({ serial } = sheetState);
3701
+ sheetState.serial += 1;
3702
+ }
3703
+ const name = `--jx-r${serial}-${indirect}`;
3704
+ indirect += 1;
3705
+ sources.push({ name, value });
3706
+ return `var(${name})`;
3707
+ },
3708
+ scope: SCOPE_TOKEN,
3709
+ transposeSelector,
3710
+ transposeValue: canvasStyleValue
3711
+ });
3712
+ if (rules.length === 0 && sources.length === 0) {
3713
+ return;
3714
+ }
3715
+ const scoped = rules.filter((rule) => rule.target !== "unscoped");
3716
+ const unscoped = rules.filter((rule) => rule.target === "unscoped");
3717
+ for (const rule of unscoped) {
3718
+ retainInterned(sheetState, sheetState.hoisted, rule.text, [rule.text]);
3719
+ }
3720
+ const uid = `jx-${hashCss(scoped.map((rule) => rule.key).join("|"))}`;
3721
+ const texts = scoped.map((rule) => rule.text.replaceAll(SCOPE_TOKEN, `[data-jx="${uid}"]`));
3722
+ if (texts.length > 0) {
3723
+ el.dataset.jx = uid;
3724
+ if (live) {
3725
+ sheetState.scratch ??= openSink(doc);
3726
+ sheetState.scratchOwners.set(el, texts);
3727
+ rewriteScratch(sheetState);
3728
+ } else {
3729
+ retainInterned(sheetState, sheetState.interned, uid, texts);
3730
+ }
3731
+ }
3732
+ const entry = {
3733
+ doc,
3734
+ hoisted: unscoped.map((rule) => rule.text),
3735
+ interned: texts.length > 0 && !live ? uid : null,
3736
+ state: sheetState,
3737
+ stop: null,
3738
+ uid,
3739
+ vars: sources.map((source) => source.name)
3740
+ };
3741
+ if (sources.length > 0) {
3742
+ const scope = effectScope();
3743
+ scope.run(() => {
3744
+ for (const { name, value } of sources) {
3745
+ effect(() => {
3746
+ const resolved = isRefObj(value) ? resolveRef(value.$ref, state) : evaluateTemplate(value, state);
3747
+ el.style.setProperty(name, canvasStyleValue(String(resolved ?? "")));
3748
+ });
3749
+ }
3750
+ });
3751
+ entry.stop = () => scope.stop();
3752
+ }
3753
+ elementStyles.set(el, entry);
3754
+ onScopeDispose(() => releaseElementStyles(el, entry), true);
3144
3755
  }
3145
3756
  var ENUMERATED_ATTRS = new Set(["contenteditable", "draggable", "spellcheck"]);
3146
3757
  function booleanAttrValue(name, value) {
@@ -3154,6 +3765,10 @@ function applyAttributes(el, attrs, state) {
3154
3765
  for (const [k, v] of Object.entries(attrs)) {
3155
3766
  const attr = canvasAttrName(el, k);
3156
3767
  const write = (resolved) => {
3768
+ if (resolved === null || resolved === undefined) {
3769
+ el.removeAttribute(attr);
3770
+ return;
3771
+ }
3157
3772
  if (typeof resolved === "boolean") {
3158
3773
  const text = booleanAttrValue(attr, resolved);
3159
3774
  if (text === null) {
@@ -3163,7 +3778,7 @@ function applyAttributes(el, attrs, state) {
3163
3778
  }
3164
3779
  return;
3165
3780
  }
3166
- el.setAttribute(attr, canvasAssetValue(el.tagName, k, String(resolved ?? "")));
3781
+ el.setAttribute(attr, canvasAssetValue(el.tagName, k, String(resolved)));
3167
3782
  };
3168
3783
  if (isRefObj(v)) {
3169
3784
  effect(() => write(resolveRef(v.$ref, state)));
@@ -3178,8 +3793,70 @@ function renderMappedArrayInto(parentEl, arrayDef, state, options) {
3178
3793
  const path = options?._path ?? [];
3179
3794
  const anchor = document.createComment("jx-array");
3180
3795
  parentEl.append(anchor);
3181
- const { items: itemsSrc, map: mapDef, filter: filterRef, sort: sortRef } = arrayDef;
3182
- effect(() => {
3796
+ const { items: itemsSrc, map: mapDef, filter: filterRef, sort: sortRef, key: keyRef } = arrayDef;
3797
+ const keyPointer = isRefObj(keyRef) ? keyRef.$ref : null;
3798
+ const instancePath = _instancePath;
3799
+ let warned = false;
3800
+ const warnOnce = (message) => {
3801
+ if (!warned) {
3802
+ warned = true;
3803
+ console.warn(`Jx $map: ${message}`);
3804
+ }
3805
+ };
3806
+ let rows = [];
3807
+ const teardown = (row) => {
3808
+ row.scope.stop();
3809
+ row.node.remove();
3810
+ };
3811
+ const createRow = (key, item, index) => {
3812
+ const map = shallowReactive({ index, item });
3813
+ const child = Object.create(state);
3814
+ child.$map = map;
3815
+ Object.defineProperty(child, "$map/item", { enumerable: true, get: () => map.item });
3816
+ Object.defineProperty(child, "$map/index", { enumerable: true, get: () => map.index });
3817
+ const scope = effectScope(true);
3818
+ const childOpts = options ? { ...options, _path: [...path, "map", index] } : undefined;
3819
+ const node = scope.run(() => renderNode(mapDef, child, childOpts));
3820
+ return { key, map, node, scope };
3821
+ };
3822
+ const reconcile = (list, keys) => {
3823
+ const previous = new Map;
3824
+ for (const row of rows) {
3825
+ previous.set(row.key, row);
3826
+ }
3827
+ const next = [];
3828
+ for (let index = 0;index < list.length; index++) {
3829
+ const key = keys[index];
3830
+ const item = list[index];
3831
+ const existing = previous.get(key);
3832
+ if (existing) {
3833
+ previous.delete(key);
3834
+ if (existing.map.item !== item) {
3835
+ existing.map.item = item;
3836
+ }
3837
+ if (existing.map.index !== index) {
3838
+ existing.map.index = index;
3839
+ options?.onNodeMoved?.(existing.node, [...path, "map", index]);
3840
+ }
3841
+ next.push(existing);
3842
+ } else {
3843
+ next.push(createRow(key, item, index));
3844
+ }
3845
+ }
3846
+ for (const row of previous.values()) {
3847
+ teardown(row);
3848
+ }
3849
+ let cursor = rows.find((row) => !previous.has(row.key))?.node ?? anchor;
3850
+ for (const row of next) {
3851
+ if (row.node === cursor) {
3852
+ cursor = cursor.nextSibling ?? anchor;
3853
+ continue;
3854
+ }
3855
+ cursor.before(row.node);
3856
+ }
3857
+ rows = next;
3858
+ };
3859
+ const update = () => {
3183
3860
  let items = isRefObj(itemsSrc) ? resolveRef(itemsSrc.$ref, state) : itemsSrc;
3184
3861
  if (Array.isArray(items) && isRefObj(filterRef)) {
3185
3862
  const fn = resolveRef(filterRef.$ref, state);
@@ -3193,30 +3870,65 @@ function renderMappedArrayInto(parentEl, arrayDef, state, options) {
3193
3870
  items = [...items].toSorted(fn);
3194
3871
  }
3195
3872
  }
3196
- if (!Array.isArray(items) || !mapDef) {
3197
- return;
3198
- }
3199
- const scope = effectScope(true);
3200
- const nodes = [];
3201
- scope.run(() => {
3202
- for (const [index, item] of items.entries()) {
3203
- const child = Object.create(state);
3204
- child.$map = { index, item };
3205
- child["$map/item"] = item;
3206
- child["$map/index"] = index;
3207
- const childOpts = options ? { ...options, _path: [...path, "map", index] } : undefined;
3208
- const node = renderNode(mapDef, child, childOpts);
3209
- anchor.before(node);
3210
- nodes.push(node);
3873
+ const list = Array.isArray(items) && mapDef ? items : [];
3874
+ const keys = list.map((item, index) => mappedRowKey(keyPointer, item, index, warnOnce));
3875
+ const seen = new Set;
3876
+ for (let i = 0;i < keys.length; i++) {
3877
+ if (seen.has(keys[i])) {
3878
+ warnOnce(`duplicate key ${String(keys[i])} — every occurrence after the first is rebuilt on each change`);
3879
+ keys[i] = { duplicate: i };
3880
+ } else {
3881
+ seen.add(keys[i]);
3211
3882
  }
3212
- });
3213
- onEffectCleanup(() => {
3214
- scope.stop();
3215
- for (const n of nodes) {
3216
- n.remove();
3883
+ }
3884
+ pauseTracking();
3885
+ try {
3886
+ underInstancePath(reexpand(instancePath), () => reconcile(list, keys));
3887
+ } finally {
3888
+ resetTracking();
3889
+ }
3890
+ };
3891
+ let queued = false;
3892
+ let stopped = false;
3893
+ const runner = effect(update, {
3894
+ scheduler: () => {
3895
+ if (queued) {
3896
+ return;
3217
3897
  }
3218
- });
3898
+ queued = true;
3899
+ queueMicrotask(() => {
3900
+ queued = false;
3901
+ if (!stopped) {
3902
+ runner();
3903
+ }
3904
+ });
3905
+ }
3219
3906
  });
3907
+ onScopeDispose(() => {
3908
+ stopped = true;
3909
+ for (const row of rows) {
3910
+ teardown(row);
3911
+ }
3912
+ rows = [];
3913
+ }, true);
3914
+ }
3915
+ function mappedRowKey(pointer, item, index, warnOnce) {
3916
+ if (pointer === null) {
3917
+ return index;
3918
+ }
3919
+ if (pointer === "$map/item") {
3920
+ return item;
3921
+ }
3922
+ if (pointer.startsWith("$map/item/")) {
3923
+ const key = readPath(item, pointer.slice("$map/item/".length));
3924
+ if (key === undefined || key === null) {
3925
+ warnOnce(`key "${pointer}" is empty for some items — those rows fall back to their index`);
3926
+ return index;
3927
+ }
3928
+ return key;
3929
+ }
3930
+ warnOnce(`key "${pointer}" is not a $map/item pointer — rows fall back to their index`);
3931
+ return index;
3220
3932
  }
3221
3933
  function resolveTagName(tagName, scope) {
3222
3934
  if (typeof tagName === "string") {
@@ -3232,65 +3944,87 @@ function resolveTagName(tagName, scope) {
3232
3944
  const key = evaluateOperand(expression.target, scope, null);
3233
3945
  return expression.cases[String(key)] ?? expression.default;
3234
3946
  }
3235
- function renderSwitch(def2, state, options) {
3947
+ function renderSwitch(def, state, options) {
3236
3948
  const path = options?._path ?? [];
3237
- const container = document.createElement(resolveTagName(def2.tagName, state));
3949
+ const container = document.createElement(resolveTagName(def.tagName, state));
3950
+ const instancePath = _instancePath;
3238
3951
  if (options?.onNodeCreated) {
3239
- options.onNodeCreated(container, path, def2, state);
3952
+ options.onNodeCreated(container, path, def, state);
3240
3953
  }
3241
- applyProperties(container, def2, state);
3242
- applyStyle(container, def2.style ?? {}, state["$media"] ?? {}, state);
3243
- applyAttributes(container, def2.attributes ?? {}, state);
3954
+ applyProperties(container, def, state);
3955
+ applyStyle(container, def.style ?? {}, state["$media"] ?? {}, state);
3956
+ applyAttributes(container, def.attributes ?? {}, state);
3244
3957
  let generation = 0;
3958
+ let live = null;
3959
+ let renderedKey = null;
3960
+ const retire = () => {
3961
+ live?.stop();
3962
+ live = null;
3963
+ };
3245
3964
  effect(() => {
3246
- container.replaceChildren();
3247
- if (!isRefObj(def2.$switch)) {
3965
+ if (!isRefObj(def.$switch)) {
3966
+ retire();
3967
+ container.replaceChildren();
3968
+ generation += 1;
3248
3969
  return;
3249
3970
  }
3250
- const key = resolveRef(def2.$switch.$ref, state);
3251
- const caseDef = def2.cases?.[key];
3971
+ const key = String(resolveRef(def.$switch.$ref, state));
3972
+ if (key === renderedKey) {
3973
+ return;
3974
+ }
3975
+ renderedKey = key;
3976
+ retire();
3977
+ container.replaceChildren();
3978
+ generation += 1;
3979
+ const caseDef = def.cases?.[key];
3252
3980
  if (!caseDef) {
3253
3981
  return;
3254
3982
  }
3983
+ const gen = generation;
3984
+ const scope = effectScope(true);
3985
+ live = scope;
3986
+ const childOpts = options ? { ...options, _path: [...path, "cases", key] } : undefined;
3255
3987
  if (isRefObj(caseDef)) {
3256
- generation += 1;
3257
- const gen = generation;
3258
- const { href } = new URL(caseDef.$ref, location.href);
3259
- resolve(href).then(async (doc) => {
3988
+ const { href } = new URL(caseDef.$ref, options?._ctx?.base ?? location.href);
3989
+ resolve(href, options?._ctx).then(async (doc) => {
3260
3990
  if (gen !== generation) {
3261
3991
  return;
3262
3992
  }
3263
- const childScope = await buildScope(doc, {}, href);
3993
+ const childScope = await buildScope(doc, {}, href, options?._ctx);
3264
3994
  if (gen !== generation) {
3265
3995
  return;
3266
3996
  }
3267
3997
  container.replaceChildren();
3268
- const childOpts2 = options ? { ...options, _path: [...path, "cases", key] } : undefined;
3269
- container.append(renderNode(doc, childScope, childOpts2));
3998
+ underInstancePath(reexpand(instancePath), () => scope.run(() => container.append(renderNode(doc, childScope, childOpts))));
3270
3999
  }).catch((error) => console.error("Jx $switch: failed to load external case", caseDef.$ref, error));
3271
4000
  return;
3272
4001
  }
3273
- const childOpts = options ? { ...options, _path: [...path, "cases", key] } : undefined;
3274
- container.append(renderNode(caseDef, state, childOpts));
4002
+ pauseTracking();
4003
+ try {
4004
+ underInstancePath(reexpand(instancePath), () => scope.run(() => container.append(renderNode(caseDef, state, childOpts))));
4005
+ } finally {
4006
+ resetTracking();
4007
+ }
3275
4008
  });
4009
+ onScopeDispose(retire, true);
3276
4010
  return container;
3277
4011
  }
3278
- async function resolvePrototype(def2, state, key, base) {
3279
- if (def2.$src) {
3280
- return resolveExternalPrototype(def2, state, key, base);
4012
+ async function resolvePrototype(def, state, key, base, ctx) {
4013
+ if (def.$src) {
4014
+ return resolveExternalPrototype(def, state, key, base);
3281
4015
  }
3282
- switch (def2.$prototype) {
4016
+ switch (def.$prototype) {
3283
4017
  case "Request": {
3284
4018
  const s = ref(null);
3285
- const debounceMs = def2.debounce ?? 0;
4019
+ const debounceMs = def.debounce ?? 0;
3286
4020
  let debounceTimer = null;
3287
- if (!def2.manual && !_autoRequestConfig.skip) {
4021
+ if (!def.manual && !(ctx?.skipAutoRequests ?? _autoRequestConfig.skip)) {
3288
4022
  effect(() => {
3289
4023
  let url;
3290
- if (isTemplateString(def2.url)) {
3291
- url = evaluateTemplate(def2.url, state);
4024
+ if (isTemplateString(def.url)) {
4025
+ url = evaluateTemplate(def.url, state);
3292
4026
  } else {
3293
- ({ url } = def2);
4027
+ ({ url } = def);
3294
4028
  }
3295
4029
  if (!url || url === "undefined" || url.includes("undefined")) {
3296
4030
  return;
@@ -3303,11 +4037,11 @@ async function resolvePrototype(def2, state, key, base) {
3303
4037
  }
3304
4038
  });
3305
4039
  const doFetch = () => fetch(url, {
3306
- method: def2.method ?? "GET",
4040
+ method: def.method ?? "GET",
3307
4041
  signal: controller.signal,
3308
- ...def2.headers && { headers: def2.headers },
3309
- ...def2.body && {
3310
- body: typeof def2.body === "object" ? JSON.stringify(def2.body) : def2.body
4042
+ ...def.headers && { headers: def.headers },
4043
+ ...def.body && {
4044
+ body: typeof def.body === "object" ? JSON.stringify(def.body) : def.body
3311
4045
  }
3312
4046
  }).then((r) => r.ok ? r.json() : Promise.reject(r.statusText)).then((d) => {
3313
4047
  s.value = d;
@@ -3328,7 +4062,7 @@ async function resolvePrototype(def2, state, key, base) {
3328
4062
  case "URLSearchParams": {
3329
4063
  return computed(() => {
3330
4064
  const p = {};
3331
- for (const [k, v] of Object.entries(def2)) {
4065
+ for (const [k, v] of Object.entries(def)) {
3332
4066
  if (k !== "$prototype") {
3333
4067
  p[k] = isRefObj(v) ? resolveRef(v.$ref, state) : isTemplateString(v) ? evaluateTemplate(v, state) : v;
3334
4068
  }
@@ -3338,14 +4072,14 @@ async function resolvePrototype(def2, state, key, base) {
3338
4072
  }
3339
4073
  case "LocalStorage":
3340
4074
  case "SessionStorage": {
3341
- const store = def2.$prototype === "LocalStorage" ? localStorage : sessionStorage;
3342
- const k = def2.key ?? key;
4075
+ const store = def.$prototype === "LocalStorage" ? localStorage : sessionStorage;
4076
+ const k = def.key ?? key;
3343
4077
  let init;
3344
4078
  try {
3345
4079
  const s = store.getItem(k);
3346
- init = s !== null ? JSON.parse(s) : def2.default ?? null;
4080
+ init = s !== null ? JSON.parse(s) : def.default ?? null;
3347
4081
  } catch {
3348
- init = def2.default ?? null;
4082
+ init = def.default ?? null;
3349
4083
  }
3350
4084
  const storageState = ref(init);
3351
4085
  effect(() => {
@@ -3363,7 +4097,7 @@ async function resolvePrototype(def2, state, key, base) {
3363
4097
  return storageState;
3364
4098
  }
3365
4099
  case "Cookie": {
3366
- const name = def2.name ?? key;
4100
+ const name = def.name ?? key;
3367
4101
  const read = () => {
3368
4102
  const raw = readCookie(document.cookie, name);
3369
4103
  if (raw === null) {
@@ -3375,9 +4109,9 @@ async function resolvePrototype(def2, state, key, base) {
3375
4109
  return raw;
3376
4110
  }
3377
4111
  };
3378
- const cookieState = ref(read() ?? def2.default ?? null);
4112
+ const cookieState = ref(read() ?? def.default ?? null);
3379
4113
  effect(() => {
3380
- document.cookie = serializeCookie(name, cookieState.value, def2);
4114
+ document.cookie = serializeCookie(name, cookieState.value, def);
3381
4115
  });
3382
4116
  return cookieState;
3383
4117
  }
@@ -3390,7 +4124,7 @@ async function resolvePrototype(def2, state, key, base) {
3390
4124
  keyPath = "id",
3391
4125
  autoIncrement = true,
3392
4126
  indexes = []
3393
- } = def2;
4127
+ } = def;
3394
4128
  if (!database || !store) {
3395
4129
  throw new Error(`Jx: IndexedDB entry '${key}' requires database and store`);
3396
4130
  }
@@ -3420,28 +4154,28 @@ async function resolvePrototype(def2, state, key, base) {
3420
4154
  return idbState;
3421
4155
  }
3422
4156
  case "Set": {
3423
- return new Set(Array.isArray(def2.default) ? def2.default : []);
4157
+ return new Set(Array.isArray(def.default) ? def.default : []);
3424
4158
  }
3425
4159
  case "Map": {
3426
- return new Map(Object.entries(isJsonObject(def2.default) ? def2.default : {}));
4160
+ return new Map(Object.entries(isJsonObject(def.default) ? def.default : {}));
3427
4161
  }
3428
4162
  case "FormData": {
3429
4163
  const fd = new FormData;
3430
- for (const [k, v] of Object.entries(def2.fields ?? {})) {
4164
+ for (const [k, v] of Object.entries(def.fields ?? {})) {
3431
4165
  fd.append(k, v);
3432
4166
  }
3433
4167
  return fd;
3434
4168
  }
3435
4169
  case "Blob": {
3436
- return new Blob(def2.parts ?? [], {
3437
- type: typeof def2.type === "string" ? def2.type : "text/plain"
4170
+ return new Blob(def.parts ?? [], {
4171
+ type: typeof def.type === "string" ? def.type : "text/plain"
3438
4172
  });
3439
4173
  }
3440
4174
  case "ReadableStream": {
3441
4175
  return null;
3442
4176
  }
3443
4177
  default: {
3444
- console.warn(`Jx: unknown $prototype "${def2.$prototype}" for "${key}". Did you mean to add '$src'?`);
4178
+ console.warn(`Jx: unknown $prototype "${def.$prototype}" for "${key}". Did you mean to add '$src'?`);
3445
4179
  return ref(null);
3446
4180
  }
3447
4181
  }
@@ -3458,17 +4192,20 @@ var EXTERNAL_RESERVED = new Set([
3458
4192
  "arguments",
3459
4193
  "name"
3460
4194
  ]);
3461
- async function resolveExternalPrototype(def2, state, key, base) {
3462
- const src = def2.$src;
4195
+ async function resolveExternalPrototype(def, state, key, base) {
4196
+ const src = def.$src;
3463
4197
  if (!src || !src.endsWith(".class.json")) {
3464
- throw new Error(`Jx: $prototype "${def2.$prototype}" requires a .class.json $src, got "${src}". Wrap the class in a .class.json schema with $implementation.`);
4198
+ throw new Error(`Jx: $prototype "${def.$prototype}" requires a .class.json $src, got "${src}". Wrap the class in a .class.json schema with $implementation.`);
3465
4199
  }
3466
- return resolveClassJson(def2, state, key, base);
4200
+ return resolveClassJson(def, state, key, base);
3467
4201
  }
3468
- async function importAndInstantiate(def2, src, exportName, base) {
4202
+ async function importAndInstantiate(def, src, exportName, base) {
3469
4203
  let mod;
3470
- if (_moduleCache.has(src)) {
3471
- mod = _moduleCache.get(src);
4204
+ const seeded = seededModule(src);
4205
+ if (seeded instanceof Promise) {
4206
+ mod = await seeded;
4207
+ } else if (seeded) {
4208
+ mod = seeded;
3472
4209
  } else {
3473
4210
  try {
3474
4211
  mod = await import(src);
@@ -3490,7 +4227,7 @@ async function importAndInstantiate(def2, src, exportName, base) {
3490
4227
  throw new TypeError(`Jx: "${exportName}" from "${src}" is not a class`);
3491
4228
  }
3492
4229
  const config = {};
3493
- for (const [k, v] of Object.entries(def2)) {
4230
+ for (const [k, v] of Object.entries(def)) {
3494
4231
  if (!EXTERNAL_RESERVED.has(k)) {
3495
4232
  config[k] = v;
3496
4233
  }
@@ -3513,15 +4250,15 @@ async function importAndInstantiate(def2, src, exportName, base) {
3513
4250
  }
3514
4251
  return s;
3515
4252
  }
3516
- async function resolveClassJson(def2, state, key, base) {
3517
- const src = def2.$src;
4253
+ async function resolveClassJson(def, state, key, base) {
4254
+ const src = def.$src;
3518
4255
  if (!src) {
3519
4256
  throw new Error(`Jx: class entry '${key}' has no $src`);
3520
4257
  }
3521
4258
  let classDef;
3522
4259
  const isBareSpecifier = !src.startsWith(".") && !src.startsWith("/") && !src.startsWith("http") && !src.startsWith("file:");
3523
4260
  if (isBareSpecifier) {
3524
- return resolveViaDevProxy(def2, state, key, base);
4261
+ return resolveViaDevProxy(def, state, key, base);
3525
4262
  }
3526
4263
  try {
3527
4264
  const url = base ? new URL(src, base).href : src;
@@ -3531,25 +4268,25 @@ async function resolveClassJson(def2, state, key, base) {
3531
4268
  }
3532
4269
  classDef = await res.json();
3533
4270
  } catch {
3534
- return resolveViaDevProxy(def2, state, key, base);
4271
+ return resolveViaDevProxy(def, state, key, base);
3535
4272
  }
3536
4273
  if (classDef.$implementation) {
3537
4274
  const schemaStr = JSON.stringify(classDef);
3538
4275
  if (schemaStr.includes('"#/$context/')) {
3539
- return resolveViaDevProxy(def2, state, key, base);
4276
+ return resolveViaDevProxy(def, state, key, base);
3540
4277
  }
3541
4278
  const schemaUrl = base ? new URL(src, base).href : new URL(src, location.href).href;
3542
4279
  const implSrc = new URL(classDef.$implementation, schemaUrl).href;
3543
- const exportName = def2.$export ?? classDef.title ?? def2.$prototype;
4280
+ const exportName = def.$export ?? classDef.title ?? def.$prototype;
3544
4281
  try {
3545
- return await importAndInstantiate(def2, implSrc, exportName, base);
4282
+ return await importAndInstantiate(def, implSrc, exportName, base);
3546
4283
  } catch {
3547
- return resolveViaDevProxy(def2, state, key, base);
4284
+ return resolveViaDevProxy(def, state, key, base);
3548
4285
  }
3549
4286
  }
3550
4287
  const DynClass = classFromSchema(classDef);
3551
4288
  const config = {};
3552
- for (const [k, v] of Object.entries(def2)) {
4289
+ for (const [k, v] of Object.entries(def)) {
3553
4290
  if (!EXTERNAL_RESERVED.has(k)) {
3554
4291
  config[k] = v;
3555
4292
  }
@@ -3636,9 +4373,9 @@ function classFromSchema(classDef) {
3636
4373
  const dynCtor = DynClass;
3637
4374
  return dynCtor;
3638
4375
  }
3639
- async function resolveViaDevProxy(def2, state, key, base) {
4376
+ async function resolveViaDevProxy(def, state, key, base) {
3640
4377
  const config = {};
3641
- for (const [k, v] of Object.entries(def2)) {
4378
+ for (const [k, v] of Object.entries(def)) {
3642
4379
  if (!EXTERNAL_RESERVED.has(k)) {
3643
4380
  config[k] = v;
3644
4381
  }
@@ -3647,9 +4384,9 @@ async function resolveViaDevProxy(def2, state, key, base) {
3647
4384
  const doResolve = (resolvedConfig) => fetch(resolveProxyPath("/__jx_resolve__"), {
3648
4385
  body: JSON.stringify({
3649
4386
  $base: base,
3650
- $export: def2.$export,
3651
- $prototype: def2.$prototype,
3652
- $src: def2.$src,
4387
+ $export: def.$export,
4388
+ $prototype: def.$prototype,
4389
+ $src: def.$src,
3653
4390
  ...resolvedConfig
3654
4391
  }),
3655
4392
  headers: { "Content-Type": "application/json" },
@@ -3678,12 +4415,15 @@ async function resolveViaDevProxy(def2, state, key, base) {
3678
4415
  }
3679
4416
  return s;
3680
4417
  }
3681
- async function resolveServerFunction(def2, state, key, base) {
3682
- const src = def2.$src;
3683
- const exportName = def2.$export;
4418
+ async function resolveServerFunction(def, state, key, base) {
4419
+ const src = def.$src;
4420
+ const exportName = def.$export;
3684
4421
  let mod;
3685
- if (_moduleCache.has(src)) {
3686
- mod = _moduleCache.get(src);
4422
+ const seeded = seededModule(src);
4423
+ if (seeded instanceof Promise) {
4424
+ mod = await seeded;
4425
+ } else if (seeded) {
4426
+ mod = seeded;
3687
4427
  } else {
3688
4428
  try {
3689
4429
  mod = await import(src);
@@ -3693,10 +4433,10 @@ async function resolveServerFunction(def2, state, key, base) {
3693
4433
  const resolvedSrc = new URL(src, base).href;
3694
4434
  mod = await import(resolvedSrc);
3695
4435
  } catch {
3696
- return resolveServerFunctionViaProxy(def2, state, key, base);
4436
+ return resolveServerFunctionViaProxy(def, state, key, base);
3697
4437
  }
3698
4438
  } else {
3699
- return resolveServerFunctionViaProxy(def2, state, key, base);
4439
+ return resolveServerFunctionViaProxy(def, state, key, base);
3700
4440
  }
3701
4441
  }
3702
4442
  _moduleCache.set(src, mod);
@@ -3709,7 +4449,7 @@ async function resolveServerFunction(def2, state, key, base) {
3709
4449
  throw new TypeError(`Jx: "${exportName}" from "${src}" is not a function`);
3710
4450
  }
3711
4451
  const fn = candidate;
3712
- const rawArgs = def2.arguments ?? {};
4452
+ const rawArgs = def.arguments ?? {};
3713
4453
  const hasReactiveArg = Object.values(rawArgs).some((v) => isRefObj(v));
3714
4454
  const resolveArgs = () => {
3715
4455
  const args = {};
@@ -3732,8 +4472,8 @@ async function resolveServerFunction(def2, state, key, base) {
3732
4472
  }
3733
4473
  return s;
3734
4474
  }
3735
- async function resolveServerFunctionViaProxy(def2, state, key, base) {
3736
- const rawArgs = def2.arguments ?? {};
4475
+ async function resolveServerFunctionViaProxy(def, state, key, base) {
4476
+ const rawArgs = def.arguments ?? {};
3737
4477
  const hasReactiveArg = Object.values(rawArgs).some((v) => isRefObj(v));
3738
4478
  const resolveArgs = () => {
3739
4479
  const args = {};
@@ -3745,8 +4485,8 @@ async function resolveServerFunctionViaProxy(def2, state, key, base) {
3745
4485
  const doResolve = (args) => fetch(resolveProxyPath("/__jx_server__"), {
3746
4486
  body: JSON.stringify({
3747
4487
  $base: base,
3748
- $export: def2.$export,
3749
- $src: def2.$src,
4488
+ $export: def.$export,
4489
+ $src: def.$src,
3750
4490
  arguments: args
3751
4491
  }),
3752
4492
  headers: { "Content-Type": "application/json" },
@@ -3777,8 +4517,8 @@ function resolveRef(refPath, state) {
3777
4517
  if (typeof refPath !== "string") {
3778
4518
  return refPath;
3779
4519
  }
3780
- if (refPath.startsWith("$map/")) {
3781
- const parts = refPath.split("/");
4520
+ if (refPath.startsWith("$map/") || refPath.startsWith("#/$map/")) {
4521
+ const parts = (refPath.startsWith("#/") ? refPath.slice(2) : refPath).split("/");
3782
4522
  const [, key] = parts;
3783
4523
  const map = state.$map;
3784
4524
  const base = map?.[key] ?? state[`$map/${key}`];
@@ -3801,12 +4541,41 @@ function resolveRef(refPath, state) {
3801
4541
  function isRefObj(v) {
3802
4542
  return isRef(v);
3803
4543
  }
3804
- function isNestedSelector(k) {
3805
- return k.startsWith(":") || k.startsWith(".") || k.startsWith("&") || k.startsWith("[");
3806
- }
3807
4544
  function getPath2(obj, path) {
3808
4545
  return readPath(obj, path);
3809
4546
  }
4547
+ function absorbAttribute(state, name, value, defaults) {
4548
+ const camelKey = name.replaceAll(/-([a-z])/g, (_, c) => c.toUpperCase());
4549
+ const current = state[camelKey];
4550
+ if (typeof current === "boolean") {
4551
+ state[camelKey] = value !== null && value !== "false";
4552
+ return;
4553
+ }
4554
+ if (value === null) {
4555
+ const declared = defaults?.get(camelKey);
4556
+ state[camelKey] = declared === undefined ? typeof current === "number" ? 0 : "" : declared;
4557
+ return;
4558
+ }
4559
+ state[camelKey] = typeof current === "number" ? Number(value) : value;
4560
+ }
4561
+ function declaredDefaults(def) {
4562
+ const out = new Map;
4563
+ for (const [key, entry] of Object.entries(def.state ?? {})) {
4564
+ if (entry !== null && typeof entry === "object") {
4565
+ const obj = entry;
4566
+ if ("default" in obj) {
4567
+ out.set(key, obj["default"]);
4568
+ continue;
4569
+ }
4570
+ if (COMPUTED_STATE_KEYS.some((marker) => (marker in obj))) {
4571
+ continue;
4572
+ }
4573
+ }
4574
+ out.set(key, entry);
4575
+ }
4576
+ return out;
4577
+ }
4578
+ var COMPUTED_STATE_KEYS = ["$expression", "$prototype", "$ref", "$src"];
3810
4579
  var _privatePropWarned = new Set;
3811
4580
  function instanceSupplies(el, key) {
3812
4581
  if (Object.hasOwn(el, key)) {
@@ -3829,9 +4598,9 @@ function refusePrivateProp(key, where) {
3829
4598
  }
3830
4599
  return true;
3831
4600
  }
3832
- function mergeProps(def2, parentState) {
4601
+ function mergeProps(def, parentState) {
3833
4602
  const child = Object.create(parentState);
3834
- for (const [k, v] of Object.entries(def2.$props ?? {})) {
4603
+ for (const [k, v] of Object.entries(def.$props ?? {})) {
3835
4604
  if (refusePrivateProp(k, "$props")) {
3836
4605
  continue;
3837
4606
  }
@@ -3839,15 +4608,32 @@ function mergeProps(def2, parentState) {
3839
4608
  }
3840
4609
  return child;
3841
4610
  }
3842
- function toCSSText(rules) {
3843
- return Object.entries(rules).filter(([k, v]) => !isNestedSelector(k) && (v === null || typeof v !== "object")).map(([p, v]) => `${camelToKebab(p)}: ${canvasStyleValue(String(v))}`).join("; ");
3844
- }
3845
4611
  var _rootMedia = {};
3846
4612
  var _elementDefs = new Map;
4613
+ async function redefineElement(doc, baseUrl) {
4614
+ const base = baseUrl ?? location.href;
4615
+ const { tagName } = doc;
4616
+ if (!tagName || !tagName.includes("-")) {
4617
+ throw new Error(`Jx redefineElement: tagName "${tagName}" must contain a hyphen`);
4618
+ }
4619
+ if (!customElements.get(tagName)) {
4620
+ await defineElement(doc, base);
4621
+ return;
4622
+ }
4623
+ if (doc.$elements) {
4624
+ await registerElements(doc.$elements, base);
4625
+ }
4626
+ const before = _elementDefs.get(tagName)?.doc.observedAttributes ?? [];
4627
+ const after = doc.observedAttributes ?? [];
4628
+ if (JSON.stringify(before) !== JSON.stringify(after)) {
4629
+ console.warn(`Jx redefineElement: <${tagName}> keeps observedAttributes [${before.join(", ")}] as first defined; [${after.join(", ")}] takes effect only in a fresh realm`);
4630
+ }
4631
+ _elementDefs.set(tagName, { base, doc });
4632
+ }
3847
4633
  function setRootMedia(map) {
3848
4634
  _rootMedia = map ?? {};
3849
4635
  }
3850
- async function registerElements(elements, base) {
4636
+ async function registerElements(elements, base, ctx) {
3851
4637
  for (const entry of elements) {
3852
4638
  if (typeof entry === "string") {
3853
4639
  try {
@@ -3862,7 +4648,7 @@ async function registerElements(elements, base) {
3862
4648
  continue;
3863
4649
  }
3864
4650
  const { href } = new URL(entry.$ref, base);
3865
- const doc = await resolve(href);
4651
+ const doc = await resolve(href, ctx);
3866
4652
  if (!doc.tagName || !doc.tagName.includes("-")) {
3867
4653
  continue;
3868
4654
  }
@@ -3870,11 +4656,70 @@ async function registerElements(elements, base) {
3870
4656
  continue;
3871
4657
  }
3872
4658
  if (doc.$elements) {
3873
- await registerElements(doc.$elements, href);
4659
+ await registerElements(doc.$elements, href, ctx);
3874
4660
  }
3875
4661
  await defineElement(doc, href);
3876
4662
  }
3877
4663
  }
4664
+ var MAX_COMPONENT_NESTING = 32;
4665
+ var ROOT_INSTANCE_PATH = { expansion: 0, frames: [] };
4666
+ var _instancePath = ROOT_INSTANCE_PATH;
4667
+ function underInstancePath(path, fn) {
4668
+ const was = _instancePath;
4669
+ _instancePath = path;
4670
+ try {
4671
+ return fn();
4672
+ } finally {
4673
+ _instancePath = was;
4674
+ }
4675
+ }
4676
+ function reexpand(path) {
4677
+ return { expansion: path.frames.length, frames: path.frames };
4678
+ }
4679
+ function samePropValue(a, b) {
4680
+ if (Object.is(a, b)) {
4681
+ return true;
4682
+ }
4683
+ if (!isPlainData(a) || !isPlainData(b)) {
4684
+ return false;
4685
+ }
4686
+ try {
4687
+ return JSON.stringify(a) === JSON.stringify(b);
4688
+ } catch {
4689
+ return false;
4690
+ }
4691
+ }
4692
+ function isPlainData(value) {
4693
+ if (typeof value !== "object" || value === null) {
4694
+ return false;
4695
+ }
4696
+ const raw = toRaw(value);
4697
+ if (Array.isArray(raw)) {
4698
+ return true;
4699
+ }
4700
+ const proto = Object.getPrototypeOf(raw);
4701
+ return proto === Object.prototype || proto === null;
4702
+ }
4703
+ function sameFrame(a, b) {
4704
+ if (a.tag !== b.tag) {
4705
+ return false;
4706
+ }
4707
+ const keys = Object.keys(a.props);
4708
+ if (keys.length !== Object.keys(b.props).length) {
4709
+ return false;
4710
+ }
4711
+ return keys.every((key) => Object.hasOwn(b.props, key) && samePropValue(a.props[key], b.props[key]));
4712
+ }
4713
+ function extendInstancePath(path, frame) {
4714
+ const chain = [...path.frames.map((f) => f.tag), frame.tag].join(" → ");
4715
+ if (path.frames.slice(path.expansion).some((f) => sameFrame(f, frame))) {
4716
+ throw new Error(`Component <${frame.tag}> renders itself: ${chain}. A component cannot appear inside its own ` + `definition with the same props — the expansion would never end.`);
4717
+ }
4718
+ if (path.frames.length >= MAX_COMPONENT_NESTING) {
4719
+ throw new Error(`Component nesting exceeds ${MAX_COMPONENT_NESTING} levels: ${chain}. A component that renders itself with changing props needs a case that stops.`);
4720
+ }
4721
+ return { expansion: path.expansion, frames: [...path.frames, frame] };
4722
+ }
3878
4723
  var _definedSources = new Set;
3879
4724
  async function defineElement(source, baseUrl) {
3880
4725
  let base = baseUrl;
@@ -3900,8 +4745,9 @@ async function defineElement(source, baseUrl) {
3900
4745
  await registerElements(source_.$elements, base);
3901
4746
  }
3902
4747
  _elementDefs.set(tagName, { base, doc: source_ });
3903
- const def2 = source_;
3904
- const observedAttrs = def2.observedAttributes ?? [];
4748
+ const initialDef = source_;
4749
+ const initialBase = base;
4750
+ const observedAttrs = initialDef.observedAttributes ?? [];
3905
4751
  const ElementClass = class extends HTMLElement {
3906
4752
  _jxInitialized = false;
3907
4753
  _state = null;
@@ -3916,7 +4762,18 @@ async function defineElement(source, baseUrl) {
3916
4762
  return;
3917
4763
  }
3918
4764
  this._jxInitialized = true;
3919
- const state = await buildScope(def2, {}, base);
4765
+ const enclosingPath = _instancePath;
4766
+ const supplied = {};
4767
+ const live = _elementDefs.get(tagName) ?? { base: initialBase, doc: initialDef };
4768
+ const def = live.doc;
4769
+ const defBase = live.base;
4770
+ const state = await buildScope(def, {}, defBase, { base: defBase, media: null, root: this });
4771
+ for (const attr of observedAttrs) {
4772
+ if (this.hasAttribute(attr)) {
4773
+ absorbAttribute(state, attr, this.getAttribute(attr));
4774
+ supplied[attr] = this.getAttribute(attr);
4775
+ }
4776
+ }
3920
4777
  const propsAttr = this.dataset.jxProps;
3921
4778
  if (propsAttr) {
3922
4779
  try {
@@ -3925,8 +4782,9 @@ async function defineElement(source, baseUrl) {
3925
4782
  if (refusePrivateProp(key, "data-jx-props")) {
3926
4783
  continue;
3927
4784
  }
3928
- if (key in (def2.state ?? {})) {
4785
+ if (key in (def.state ?? {})) {
3929
4786
  state[key] = val;
4787
+ supplied[key] = val;
3930
4788
  }
3931
4789
  }
3932
4790
  } catch {}
@@ -3939,20 +4797,39 @@ async function defineElement(source, baseUrl) {
3939
4797
  this.removeAttribute(name);
3940
4798
  continue;
3941
4799
  }
3942
- if (key in (def2.state ?? {})) {
4800
+ if (key in (def.state ?? {})) {
3943
4801
  state[key] = this.getAttribute(name);
4802
+ supplied[key] = this.getAttribute(name);
3944
4803
  this.removeAttribute(name);
3945
4804
  }
3946
4805
  }
3947
- for (const key of Object.keys(def2.state ?? {})) {
4806
+ for (const key of Object.keys(def.state ?? {})) {
3948
4807
  if (isPrivateStateKey(key)) {
3949
4808
  continue;
3950
4809
  }
3951
4810
  if (key in this && this[key] !== undefined && instanceSupplies(this, key)) {
3952
4811
  state[key] = this[key];
4812
+ supplied[key] = this[key];
3953
4813
  }
3954
4814
  }
3955
- for (const key of Object.keys(def2.state ?? {})) {
4815
+ let ownPath;
4816
+ try {
4817
+ ownPath = extendInstancePath(enclosingPath, { props: supplied, tag: tagName });
4818
+ } catch (error) {
4819
+ this.replaceChildren();
4820
+ if (!this.isConnected) {
4821
+ return;
4822
+ }
4823
+ console.error(`Jx: <${tagName}> was not rendered:`, error);
4824
+ this.dispatchEvent(new ErrorEvent("jx-error", {
4825
+ bubbles: true,
4826
+ composed: true,
4827
+ error,
4828
+ message: error instanceof Error ? error.message : String(error)
4829
+ }));
4830
+ return;
4831
+ }
4832
+ for (const key of Object.keys(def.state ?? {})) {
3956
4833
  if (isPrivateStateKey(key)) {
3957
4834
  continue;
3958
4835
  }
@@ -3969,22 +4846,30 @@ async function defineElement(source, baseUrl) {
3969
4846
  this._state = state;
3970
4847
  const slottedChildren = [...this.childNodes];
3971
4848
  this.replaceChildren();
3972
- if (!this.style.display) {
3973
- this.style.display = "block";
3974
- }
3975
- applyStyle(this, def2.style ?? {}, state["$media"] ?? {}, state);
3976
- applyAttributes(this, def2.attributes ?? {}, state);
3977
- if (!Array.isArray(def2.children) && def2.textContent !== undefined) {
3978
- bindProperty(this, "textContent", def2.textContent, state);
3979
- }
3980
- const children = Array.isArray(def2.children) ? def2.children : [];
3981
- for (const childDef of children) {
3982
- this.append(renderNode(childDef, state));
4849
+ const defStyle = declaresDisplay(def.style) ? def.style : { display: "block", ...def.style };
4850
+ const hostStyle = mergeStyle(defStyle, callSiteStyles.get(this));
4851
+ applyStyle(this, hostStyle, state["$media"] ?? {}, state);
4852
+ applyAttributes(this, def.attributes ?? {}, state);
4853
+ bindDefinitionHandlers(this, def, state);
4854
+ if (!Array.isArray(def.children) && def.textContent !== undefined) {
4855
+ bindProperty(this, "textContent", def.textContent, state);
4856
+ }
4857
+ const children = Array.isArray(def.children) ? def.children : [];
4858
+ const wasInside = _canvasInsideStampedHost;
4859
+ _canvasInsideStampedHost ||= this.dataset.jxPath !== undefined;
4860
+ try {
4861
+ underInstancePath(ownPath, () => {
4862
+ for (const childDef of children) {
4863
+ this.append(renderNode(childDef, state));
4864
+ }
4865
+ });
4866
+ } finally {
4867
+ _canvasInsideStampedHost = wasInside;
3983
4868
  }
3984
4869
  distributeSlots(this, slottedChildren);
3985
4870
  const { onMount } = state;
3986
4871
  if (typeof onMount === "function") {
3987
- queueMicrotask(() => onMount(state));
4872
+ queueMicrotask(() => onMount(state, this));
3988
4873
  }
3989
4874
  }
3990
4875
  disconnectedCallback() {
@@ -4001,25 +4886,17 @@ async function defineElement(source, baseUrl) {
4001
4886
  if (!this._state || oldVal === newVal) {
4002
4887
  return;
4003
4888
  }
4004
- const camelKey = name.replaceAll(/-([a-z])/g, (_, c) => c.toUpperCase());
4005
- const current = this._state[camelKey];
4006
- if (typeof current === "number") {
4007
- this._state[camelKey] = Number(newVal);
4008
- } else if (typeof current === "boolean") {
4009
- this._state[camelKey] = newVal !== null && newVal !== "false";
4010
- } else {
4011
- this._state[camelKey] = newVal;
4012
- }
4889
+ absorbAttribute(this._state, name, newVal, declaredDefaults(_elementDefs.get(tagName)?.doc ?? initialDef));
4013
4890
  }
4014
4891
  };
4015
4892
  customElements.define(tagName, ElementClass);
4016
4893
  }
4017
- function renderCustomElementWithProps(def2, state, options, path) {
4018
- const el = document.createElement(resolveTagName(def2.tagName, state));
4894
+ function renderCustomElementWithProps(def, state, options, path) {
4895
+ const el = document.createElement(resolveTagName(def.tagName, state));
4019
4896
  if (options?.onNodeCreated) {
4020
- options.onNodeCreated(el, path ?? [], def2, state);
4897
+ options.onNodeCreated(el, path ?? [], def, state);
4021
4898
  }
4022
- for (const [key, val] of Object.entries(def2.$props ?? {})) {
4899
+ for (const [key, val] of Object.entries(def.$props ?? {})) {
4023
4900
  if (refusePrivateProp(key, "$props")) {
4024
4901
  continue;
4025
4902
  }
@@ -4040,19 +4917,25 @@ function renderCustomElementWithProps(def2, state, options, path) {
4040
4917
  write(val);
4041
4918
  }
4042
4919
  }
4043
- applyStyle(el, def2.style ?? {}, state["$media"] ?? {}, state);
4044
- applyAttributes(el, def2.attributes ?? {}, state);
4045
- const children = Array.isArray(def2.children) ? def2.children : [];
4920
+ if (def.style) {
4921
+ callSiteStyles.set(el, def.style);
4922
+ }
4923
+ applyStyle(el, def.style ?? {}, state["$media"] ?? {}, state);
4924
+ applyAttributes(el, def.attributes ?? {}, state);
4925
+ applyProperties(el, def, state);
4926
+ const children = Array.isArray(def.children) ? def.children : [];
4046
4927
  for (let i = 0;i < children.length; i++) {
4928
+ const child = children[i];
4047
4929
  const childOpts = options && path ? { ...options, _path: [...path, "children", i] } : undefined;
4048
- el.append(renderNode(children[i], state, childOpts));
4930
+ if (isMappedArray(child)) {
4931
+ renderMappedArrayInto(el, child, state, childOpts);
4932
+ } else {
4933
+ el.append(renderNode(child, state, childOpts));
4934
+ }
4049
4935
  }
4050
4936
  return el;
4051
4937
  }
4052
4938
  function distributeSlots(host, slottedChildren) {
4053
- if (slottedChildren.length === 0) {
4054
- return;
4055
- }
4056
4939
  const slots = host.querySelectorAll("slot");
4057
4940
  if (slots.length === 0) {
4058
4941
  return;
@@ -4073,12 +4956,7 @@ function distributeSlots(host, slottedChildren) {
4073
4956
  for (const slot of slots) {
4074
4957
  const name = slot.getAttribute("name");
4075
4958
  const matches = name ? named.get(name) ?? [] : unnamed;
4076
- if (matches.length > 0) {
4077
- slot.replaceChildren();
4078
- for (const child of matches) {
4079
- slot.append(child);
4080
- }
4081
- }
4959
+ slot.replaceWith(...matches.length > 0 ? matches : [...slot.childNodes]);
4082
4960
  }
4083
4961
  }
4084
4962
 
@@ -4124,14 +5002,18 @@ function mountedRefFor(value, entryDir, mount) {
4124
5002
  }
4125
5003
 
4126
5004
  // src/canvas/path-mapping.ts
5005
+ function jxPathSelector(serialized) {
5006
+ const escaped = serialized.replaceAll("\\", String.raw`\\`).replaceAll("'", String.raw`\'`);
5007
+ return `[data-jx-path='${escaped}']`;
5008
+ }
4127
5009
  function wrapsPageContent(path, prefix) {
4128
5010
  if (!prefix) {
4129
5011
  return false;
4130
5012
  }
4131
5013
  return path.length <= prefix.length && path.every((seg, i) => prefix[i] === seg);
4132
5014
  }
4133
- function classifyRenderNode(path, def2, ctx) {
4134
- const marker = ctx.layoutWrapped && typeof def2 === "object" ? def2?.$__layout : undefined;
5015
+ function classifyRenderNode(path, def, ctx) {
5016
+ const marker = ctx.layoutWrapped && typeof def === "object" ? def?.$__layout : undefined;
4135
5017
  if (marker) {
4136
5018
  const origin = typeof marker === "object" ? marker : {};
4137
5019
  return {
@@ -4150,7 +5032,7 @@ function classifyRenderNode(path, def2, ctx) {
4150
5032
  mappedPath = typeof containerIdx === "number" ? ["children", containerIdx - (ctx.pageContentOffset ?? 0), ...rest.slice(1)] : ["children", ...rest];
4151
5033
  }
4152
5034
  }
4153
- if ((ctx.canvasMode === "design" || ctx.canvasMode === "edit") && ctx.arrayPaths.size > 0) {
5035
+ if (ctx.arrayPaths.size > 0) {
4154
5036
  let changed = true;
4155
5037
  while (changed) {
4156
5038
  changed = false;
@@ -4172,57 +5054,160 @@ function parseJxPath(serialized) {
4172
5054
  return JSON.parse(serialized);
4173
5055
  }
4174
5056
 
5057
+ // src/canvas/iframe-position.ts
5058
+ function isIslandBoundary(el) {
5059
+ if (el.dataset.jxDefinitionRoot !== undefined) {
5060
+ return false;
5061
+ }
5062
+ return el.getAttribute("contenteditable") === "false" || el.tagName.includes("-");
5063
+ }
5064
+ function activeBlockAt(node, isEditable) {
5065
+ let el = node instanceof Element ? node : node?.parentElement ?? null;
5066
+ while (el) {
5067
+ if (el instanceof HTMLElement) {
5068
+ if (el.dataset.jxPath && isEditable(el)) {
5069
+ return { el, path: parseJxPath(el.dataset.jxPath) };
5070
+ }
5071
+ if (isIslandBoundary(el)) {
5072
+ return null;
5073
+ }
5074
+ }
5075
+ el = el.parentElement;
5076
+ }
5077
+ return null;
5078
+ }
5079
+ function elementForPath(container, path) {
5080
+ const el = container.querySelector(jxPathSelector(serializeJxPath(path)));
5081
+ return el instanceof HTMLElement ? el : null;
5082
+ }
5083
+ function blockTextLength(block) {
5084
+ return block.textContent?.length ?? 0;
5085
+ }
5086
+ function offsetOf(block, node, offset) {
5087
+ if (!block.contains(node)) {
5088
+ return null;
5089
+ }
5090
+ const range = block.ownerDocument.createRange();
5091
+ range.setStart(block, 0);
5092
+ try {
5093
+ range.setEnd(node, offset);
5094
+ } catch {
5095
+ return null;
5096
+ }
5097
+ return range.toString().length;
5098
+ }
5099
+ function domPositionAt(block, offset) {
5100
+ const target = Math.max(0, Math.min(offset, blockTextLength(block)));
5101
+ const walker = block.ownerDocument.createTreeWalker(block, NodeFilter.SHOW_TEXT);
5102
+ let seen = 0;
5103
+ let last = null;
5104
+ let current = walker.nextNode();
5105
+ while (current) {
5106
+ const len = current.length;
5107
+ if (seen + len >= target) {
5108
+ return { node: current, offset: target - seen };
5109
+ }
5110
+ seen += len;
5111
+ last = current;
5112
+ current = walker.nextNode();
5113
+ }
5114
+ return last ? { node: last, offset: last.length } : { node: block, offset: 0 };
5115
+ }
5116
+ function toDocPos(node, offset, isEditable) {
5117
+ const block = activeBlockAt(node, isEditable);
5118
+ if (!block || !node) {
5119
+ return null;
5120
+ }
5121
+ const charOffset = offsetOf(block.el, node, offset);
5122
+ return charOffset === null ? null : { offset: charOffset, path: block.path };
5123
+ }
5124
+ function toDomPosition(container, pos) {
5125
+ const el = elementForPath(container, pos.path);
5126
+ return el ? domPositionAt(el, pos.offset) : null;
5127
+ }
5128
+ function isAtBlockStart(pos) {
5129
+ return pos.offset <= 0;
5130
+ }
5131
+ function samePath(a, b) {
5132
+ return serializeJxPath(a.path) === serializeJxPath(b.path);
5133
+ }
5134
+ function blocksInOrder(container, isEditable) {
5135
+ const out = [];
5136
+ for (const el of container.querySelectorAll("[data-jx-path]")) {
5137
+ if (isEditable(el) && !sealedInIsland(el, container)) {
5138
+ out.push(el);
5139
+ }
5140
+ }
5141
+ return out;
5142
+ }
5143
+ function sealedInIsland(el, container) {
5144
+ let cur = el.parentElement;
5145
+ while (cur && cur !== container) {
5146
+ if (isIslandBoundary(cur)) {
5147
+ return true;
5148
+ }
5149
+ cur = cur.parentElement;
5150
+ }
5151
+ return false;
5152
+ }
5153
+ function adjacentBlock(container, path, direction, isEditable) {
5154
+ const blocks = blocksInOrder(container, isEditable);
5155
+ const key = serializeJxPath(path);
5156
+ const index = blocks.findIndex((el) => el.dataset.jxPath === key);
5157
+ const neighbour = index === -1 ? undefined : blocks[index + direction];
5158
+ return neighbour ? { el: neighbour, path: parseJxPath(neighbour.dataset.jxPath) } : null;
5159
+ }
5160
+
4175
5161
  // ../site/src/site-style.ts
4176
5162
  var SITE_STYLE_ID = "jx-site-style";
4177
- function toDecls(props, transpose) {
4178
- return Object.entries(props).map(([key, value]) => `${camelToKebab(key)}: ${transpose(String(value))}`).join("; ");
4179
- }
4180
- function buildSiteStyleCSS(siteStyle, mediaQueries, transpose) {
5163
+ function buildSiteStyleCSS(siteStyle, mediaQueries, transpose, resolveValue) {
4181
5164
  const rules = [];
5165
+ const push = (style, selector) => {
5166
+ for (const rule of buildStyleRules(style, {
5167
+ mediaQueries,
5168
+ scope: selector,
5169
+ transposeValue: transpose,
5170
+ ...resolveValue ? { resolveValue: (property, value) => resolveValue(property, value, selector) } : {}
5171
+ })) {
5172
+ rules.push(rule.text);
5173
+ }
5174
+ };
4182
5175
  const rootProps = {};
4183
5176
  const bodyProps = {};
4184
5177
  const condBlocks = [];
5178
+ const selectorBlocks = [];
4185
5179
  for (const [key, value] of Object.entries(siteStyle)) {
4186
5180
  if (value !== null && typeof value === "object" && !Array.isArray(value)) {
4187
5181
  if (key.startsWith("@")) {
4188
5182
  condBlocks.push([key, value]);
5183
+ } else if (!key.startsWith("--")) {
5184
+ selectorBlocks.push([key, value]);
4189
5185
  }
4190
5186
  continue;
4191
5187
  }
4192
- if (key.startsWith(":") || key.startsWith(".") || key.startsWith("[") || key.startsWith("@")) {
5188
+ if (isNestedSelectorKey(key) || key.startsWith("@")) {
4193
5189
  continue;
4194
5190
  }
4195
- if (key.startsWith("--")) {
5191
+ if (key.startsWith("--") || key === "colorScheme" || key === "color-scheme") {
4196
5192
  rootProps[key] = value;
4197
5193
  } else {
4198
5194
  bodyProps[key] = value;
4199
5195
  }
4200
5196
  }
4201
- const rootCSS = toDecls(rootProps, transpose);
4202
- if (rootCSS) {
4203
- rules.push(`:root { ${rootCSS} }`);
4204
- }
4205
- const bodyCSS = toDecls(bodyProps, transpose);
4206
- if (bodyCSS) {
4207
- rules.push(`body { ${bodyCSS} }`);
4208
- }
4209
- for (const [atKey, block] of condBlocks) {
4210
- const query = resolveAtQuery(atKey, mediaQueries);
4211
- if (query === null) {
4212
- continue;
5197
+ push(rootProps, ":root");
5198
+ push(bodyProps, "body");
5199
+ for (const [key, block] of selectorBlocks) {
5200
+ if (key.startsWith("&")) {
5201
+ push({ [key]: block }, ":root");
5202
+ } else {
5203
+ push(block, key);
5204
+ }
5205
+ }
5206
+ for (const [atKey, block] of condBlocks) {
5207
+ if (isDeclarationAtRule(atKey) || isKeyframesAtRule(atKey)) {
5208
+ push({ [atKey]: block }, null);
5209
+ continue;
4213
5210
  }
4214
- const scheme = pureSchemeOf(query);
4215
- const emit = (selector, props) => {
4216
- if (!props) {
4217
- return;
4218
- }
4219
- if (scheme) {
4220
- const { auto, forced } = schemeSelectors(selector, scheme);
4221
- rules.push(`@media ${query} { ${auto} { ${props} } }`, `${forced} { ${props} }`);
4222
- } else {
4223
- rules.push(`@media ${query} { ${selector} { ${props} } }`);
4224
- }
4225
- };
4226
5211
  const condRoot = {};
4227
5212
  const condBody = {};
4228
5213
  const condSubs = [];
@@ -4239,14 +5224,14 @@ function buildSiteStyleCSS(siteStyle, mediaQueries, transpose) {
4239
5224
  condBody[k] = v;
4240
5225
  }
4241
5226
  }
4242
- emit(":root", toDecls(condRoot, transpose));
4243
- emit("body", toDecls(condBody, transpose));
5227
+ push({ [atKey]: condRoot }, ":root");
5228
+ push({ [atKey]: condBody }, "body");
4244
5229
  for (const [sel, sub] of condSubs) {
4245
- emit(sel, toDecls(sub, transpose));
5230
+ push({ [atKey]: sub }, sel);
4246
5231
  }
4247
5232
  }
4248
- if (Object.values(mediaQueries).some((q) => pureSchemeOf(q) !== null)) {
4249
- rules.push(":root { color-scheme: light dark }");
5233
+ if (Object.values(mediaQueries).some((q) => pureSchemeOf(q) !== null) && !("colorScheme" in siteStyle)) {
5234
+ rules.push(":root { color-scheme: light dark }", `:root:where([${COLOR_SCHEME_ATTR}="light"]) { color-scheme: light }`, `:root:where([${COLOR_SCHEME_ATTR}="dark"]) { color-scheme: dark }`);
4250
5235
  }
4251
5236
  return rules.join(`
4252
5237
  `);
@@ -4386,6 +5371,196 @@ function syncEditModeCss(doc, mode) {
4386
5371
  style.textContent = EDIT_PLACEHOLDER_CSS;
4387
5372
  doc.head.append(style);
4388
5373
  }
5374
+ var CANVAS_OVERLAY_STYLE_ID = "jx-canvas-overlay-css";
5375
+ var CANVAS_OVERLAY_CSS = `
5376
+ /* The UA rule a de-popovered element lost, re-supplied at UA-EQUIVALENT PRECEDENCE.
5377
+
5378
+ The cascade layer is the mechanism and it is the whole point. An unlayered author declaration
5379
+ beats a layered one whatever its specificity, and every declaration applyStyle emits is an
5380
+ unlayered rule in an adopted sheet. That is exactly how author origin beats UA origin on the
5381
+ shipped page, so the canvas reproduces the real cascade rather than an approximation of it.
5382
+
5383
+ This used to read "a base declaration is written as an INLINE style", which was true and is the
5384
+ reason the guarantee survived the move: inline beat layered harder still, and unlayered beats it
5385
+ by the same rule one step down. Jx emits no layer of its own, which is what keeps that true.
5386
+
5387
+ Deliberately NOT forced with a priority flag. A popover whose base rule sets display is laid out
5388
+ on every page whether open or not; that is a real defect, @jxsuite/schema/overlays reports it as
5389
+ base-display, and the canvas's job is to SHOW it, not to hide it behind a stronger rule. */
5390
+ @layer jx-canvas-ua {
5391
+ [data-jx-popover]:not([data-jx-popover-open]) {
5392
+ display: none;
5393
+ }
5394
+ dialog[data-jx-dialog-open] {
5395
+ display: block;
5396
+ }
5397
+ }
5398
+ /* SHOWN IN PLACE, and the position declaration is what makes that true.
5399
+
5400
+ Dropping the popover attribute drops the UA rule that made the panel fixed, so a panel that never
5401
+ set position itself — every one in the fleet — lands in normal flow at its document position,
5402
+ contributes to #jx-canvas-root's scrollHeight, and the host grows the artboard to fit it. That is
5403
+ the whole geometry fix, and it is why an open panel is reachable at all.
5404
+
5405
+ The two alignment declarations are the other half, and they were found by measuring rather than
5406
+ by reasoning. Every drawer in the fleet is declared inside its header's flex row, so in flow it
5407
+ becomes a FLEX ITEM: align-items:center on the row centres a 904px panel on a 64px header and
5408
+ half of it sits above the artboard at a negative offset, where it contributes nothing to the
5409
+ scrollable overflow the host measures. Pinned to the start and refused any flex sizing, the same
5410
+ panel hangs down from its own position and the artboard grows by its full height.
5411
+
5412
+ Forced, because a panel that DOES set position: fixed would otherwise keep it and be laid out
5413
+ against the frame's own viewport — which in a de-linked mode is the document's full height, so a
5414
+ drawer pinned with inset: 0 lands halfway down a long page and a short component frame clips it.
5415
+ This is a presentation override for a canvas affordance, the same kind as the layout-region
5416
+ dimming in EDIT_PLACEHOLDER_CSS, and it is NOT the same move as forcing display: that would hide
5417
+ a real defect in the document (base-display), while this hides nothing — Preview renders the
5418
+ panel natively, top layer and all. */
5419
+ [data-jx-popover][data-jx-popover-open] {
5420
+ position: relative !important;
5421
+ inset: auto !important;
5422
+ align-self: start !important;
5423
+ flex: none !important;
5424
+ outline: 1px dashed color-mix(in srgb, #808080 55%, transparent);
5425
+ outline-offset: 2px;
5426
+ }
5427
+ [data-jx-popover][data-jx-popover-open]::before {
5428
+ content: "POPOVER \\00B7 SHOWN IN PLACE";
5429
+ position: absolute;
5430
+ top: 0;
5431
+ left: 0;
5432
+ z-index: 2;
5433
+ padding: 1px 5px;
5434
+ border-radius: 0 0 3px 0;
5435
+ background: color-mix(in srgb, #808080 78%, transparent);
5436
+ color: #fff;
5437
+ font: 700 9px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
5438
+ letter-spacing: 0.08em;
5439
+ pointer-events: none;
5440
+ }
5441
+ dialog[data-jx-dialog-open] {
5442
+ position: relative !important;
5443
+ inset: auto !important;
5444
+ align-self: start !important;
5445
+ flex: none !important;
5446
+ outline: 1px dashed color-mix(in srgb, #808080 55%, transparent);
5447
+ outline-offset: 2px;
5448
+ }
5449
+ dialog[data-jx-dialog-open]::before {
5450
+ content: "DIALOG \\00B7 SHOWN IN PLACE";
5451
+ position: absolute;
5452
+ top: 0;
5453
+ left: 0;
5454
+ z-index: 2;
5455
+ padding: 1px 5px;
5456
+ border-radius: 0 0 3px 0;
5457
+ background: color-mix(in srgb, #808080 78%, transparent);
5458
+ color: #fff;
5459
+ font: 700 9px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
5460
+ letter-spacing: 0.08em;
5461
+ pointer-events: none;
5462
+ }
5463
+ `;
5464
+ function syncCanvasOverlayCss(doc, mode) {
5465
+ const existing = doc.head.querySelector(`#${CANVAS_OVERLAY_STYLE_ID}`);
5466
+ if (mode === "preview") {
5467
+ existing?.remove();
5468
+ return;
5469
+ }
5470
+ if (existing) {
5471
+ return;
5472
+ }
5473
+ const style = doc.createElement("style");
5474
+ style.id = CANVAS_OVERLAY_STYLE_ID;
5475
+ style.textContent = CANVAS_OVERLAY_CSS;
5476
+ doc.head.append(style);
5477
+ }
5478
+ var DIFF_MARK_STYLE_ID = "jx-canvas-diff-css";
5479
+ var DIFF_MARK_CSS = `
5480
+ [data-jx-diff] {
5481
+ position: relative;
5482
+ border-left-width: 3px;
5483
+ border-left-color: currentColor;
5484
+ }
5485
+ [data-jx-diff]::before {
5486
+ position: absolute;
5487
+ top: 0;
5488
+ left: -3px;
5489
+ width: 3px;
5490
+ content: "";
5491
+ }
5492
+ [data-jx-diff="added"] {
5493
+ border-left-style: solid;
5494
+ border-left-color: #0a7c42;
5495
+ background: color-mix(in srgb, #0a7c42 12%, transparent);
5496
+ }
5497
+ [data-jx-diff="removed"] {
5498
+ border-left-style: double;
5499
+ border-left-color: #c9252d;
5500
+ background: color-mix(in srgb, #c9252d 12%, transparent);
5501
+ }
5502
+ [data-jx-diff="modified-before"] {
5503
+ border-left-style: dashed;
5504
+ border-left-color: #c9252d;
5505
+ background: color-mix(in srgb, #c9252d 8%, transparent);
5506
+ }
5507
+ [data-jx-diff="modified-after"] {
5508
+ border-left-style: dashed;
5509
+ border-left-color: #0a7c42;
5510
+ background: color-mix(in srgb, #0a7c42 8%, transparent);
5511
+ }
5512
+ [data-jx-diff-within] {
5513
+ border-left: 3px dotted color-mix(in srgb, #808080 60%, transparent);
5514
+ }
5515
+ @media (forced-colors: active) {
5516
+ [data-jx-diff],
5517
+ [data-jx-diff-within] {
5518
+ background: none;
5519
+ border-left-color: CanvasText;
5520
+ }
5521
+ }
5522
+ `;
5523
+ function syncDiffCss(doc, on) {
5524
+ const existing = doc.head.querySelector(`#${DIFF_MARK_STYLE_ID}`);
5525
+ if (!on) {
5526
+ existing?.remove();
5527
+ return;
5528
+ }
5529
+ if (existing) {
5530
+ return;
5531
+ }
5532
+ const style = doc.createElement("style");
5533
+ style.id = DIFF_MARK_STYLE_ID;
5534
+ style.textContent = DIFF_MARK_CSS;
5535
+ doc.head.append(style);
5536
+ }
5537
+ function applyDiffMarks(container, marks) {
5538
+ for (const stale of container.querySelectorAll("[data-jx-diff], [data-jx-diff-within]")) {
5539
+ if (stale instanceof HTMLElement) {
5540
+ delete stale.dataset.jxDiff;
5541
+ delete stale.dataset.jxDiffWithin;
5542
+ }
5543
+ }
5544
+ if (!marks?.length) {
5545
+ return;
5546
+ }
5547
+ for (const mark of marks) {
5548
+ const exact = elementForPath(container, mark.path);
5549
+ if (exact) {
5550
+ exact.dataset.jxDiff = mark.kind;
5551
+ continue;
5552
+ }
5553
+ let { path } = mark;
5554
+ while (path.length >= 2) {
5555
+ path = path.slice(0, -2);
5556
+ const ancestor = elementForPath(container, path);
5557
+ if (ancestor) {
5558
+ ancestor.dataset.jxDiffWithin = "";
5559
+ break;
5560
+ }
5561
+ }
5562
+ }
5563
+ }
4389
5564
  function syncPreviewShell(doc, mode) {
4390
5565
  doc.documentElement.toggleAttribute("data-jx-preview", mode === "preview");
4391
5566
  }
@@ -4528,6 +5703,30 @@ function applyPreviewColorScheme(doc, scheme) {
4528
5703
  delete doc.documentElement.dataset.colorScheme;
4529
5704
  }
4530
5705
  }
5706
+ function applyCanvasPopoverOpen(root, path) {
5707
+ for (const el of root.querySelectorAll("[data-jx-popover-open]")) {
5708
+ delete el.dataset.jxPopoverOpen;
5709
+ }
5710
+ if (path === null) {
5711
+ return;
5712
+ }
5713
+ const target = root.querySelector(`[data-jx-popover]${jxPathSelector(path)}`);
5714
+ if (target) {
5715
+ target.dataset.jxPopoverOpen = "";
5716
+ }
5717
+ }
5718
+ function applyCanvasDialogOpen(root, path) {
5719
+ for (const el of root.querySelectorAll("[data-jx-dialog-open]")) {
5720
+ delete el.dataset.jxDialogOpen;
5721
+ }
5722
+ if (path === null) {
5723
+ return;
5724
+ }
5725
+ const target = root.querySelector(`dialog${jxPathSelector(path)}`);
5726
+ if (target) {
5727
+ target.dataset.jxDialogOpen = "";
5728
+ }
5729
+ }
4531
5730
  function injectHead(doc, assets = null) {
4532
5731
  const head = doc.$head;
4533
5732
  if (!Array.isArray(head)) {
@@ -4570,11 +5769,11 @@ function injectHead(doc, assets = null) {
4570
5769
  function makeStamper(ctx) {
4571
5770
  const islands = [];
4572
5771
  const insideIsland = (path) => islands.some((island) => path.length > island.length && island.every((seg, i) => path[i] === seg));
4573
- return (created, path, def2) => {
5772
+ return (created, path, def) => {
4574
5773
  if (!(created instanceof HTMLElement)) {
4575
5774
  return;
4576
5775
  }
4577
- const isDefinitionRoot = path.length === 0 && def2?.tagName?.includes("-");
5776
+ const isDefinitionRoot = path.length === 0 && def?.tagName?.includes("-");
4578
5777
  if (isDefinitionRoot) {
4579
5778
  created.dataset.jxDefinitionRoot = "";
4580
5779
  }
@@ -4583,7 +5782,7 @@ function makeStamper(ctx) {
4583
5782
  created.contentEditable = "false";
4584
5783
  islands.push(path);
4585
5784
  }
4586
- const classified = classifyRenderNode(path, def2, ctx);
5785
+ const classified = classifyRenderNode(path, def, ctx);
4587
5786
  if (classified.kind === "layout") {
4588
5787
  created.dataset.jxLayoutPath = serializeJxPath(classified.layoutPath);
4589
5788
  if (classified.layoutFile) {
@@ -4634,36 +5833,32 @@ async function renderResolvedDocument(opts) {
4634
5833
  setSkipAutoRequests(opts.mode !== "preview" && !opts.allowAutoRequests);
4635
5834
  setCanvasViewportTranspose(true);
4636
5835
  setCanvasDelinkAnchors(opts.mode !== "preview");
5836
+ setCanvasDelinkPopovers(opts.mode !== "preview");
5837
+ setCanvasDelinkCommands(opts.mode !== "preview");
4637
5838
  setStampPropBindings(opts.mode === "design" || opts.mode === "edit");
4638
5839
  applySiteStyle(opts.siteStyle, opts.doc.$media ?? {});
4639
5840
  injectHead(opts.doc, assets);
4640
5841
  syncEditModeCss(opts.container.ownerDocument, opts.mode);
5842
+ syncCanvasOverlayCss(opts.container.ownerDocument, opts.mode);
4641
5843
  syncPreviewShell(opts.container.ownerDocument, opts.mode);
4642
5844
  syncStylebookCss(opts.container.ownerDocument, opts.mode);
5845
+ syncDiffCss(opts.container.ownerDocument, Boolean(opts.diffMarks?.length));
4643
5846
  setRootMedia(opts.doc.$media ?? {});
4644
5847
  await registerElements2(opts.doc, opts.docBase);
4645
5848
  const $defs = await buildScope(opts.doc, {}, opts.docBase);
4646
5849
  const onNodeCreated = makeStamper(opts.mapperCtx);
4647
5850
  const { result: el, stop } = runScoped(() => renderNode(opts.doc, $defs, { _path: [], onNodeCreated }));
4648
5851
  opts.container.replaceChildren(el);
5852
+ applyCanvasPopoverOpen(opts.container, opts.popoverOpen ?? null);
5853
+ applyCanvasDialogOpen(opts.container, opts.dialogOpen ?? null);
4649
5854
  syncEditableRoot(opts.container, opts.mode);
5855
+ applyDiffMarks(opts.container, opts.diffMarks ?? null);
4650
5856
  return {
4651
5857
  ctx: { defs: $defs, docBase: opts.docBase, mapperCtx: opts.mapperCtx, mode: opts.mode },
4652
5858
  dispose: stop
4653
5859
  };
4654
5860
  }
4655
5861
 
4656
- // src/utils/geometry.ts
4657
- function rectOf(el) {
4658
- return el.getBoundingClientRect();
4659
- }
4660
- function rectOfRange(r) {
4661
- return r.getBoundingClientRect();
4662
- }
4663
- function elementAtPoint(x, y, root = document) {
4664
- return root.elementFromPoint(x, y);
4665
- }
4666
-
4667
5862
  // src/state.ts
4668
5863
  function getNodeAtPath(doc, path) {
4669
5864
  let node = doc;
@@ -4811,41 +6006,35 @@ var elements_meta_default = {
4811
6006
  {
4812
6007
  tag: "strong",
4813
6008
  label: "Bold",
4814
- icon: "sp-icon-text-bold",
4815
6009
  command: "bold"
4816
6010
  },
4817
6011
  {
4818
6012
  tag: "em",
4819
6013
  label: "Italic",
4820
- icon: "sp-icon-text-italic",
4821
6014
  command: "italic"
4822
6015
  },
4823
6016
  {
4824
6017
  tag: "u",
4825
6018
  label: "Underline",
4826
- icon: "sp-icon-text-underline",
4827
6019
  command: "underline"
4828
6020
  },
4829
6021
  {
4830
6022
  tag: "del",
4831
6023
  label: "Strikethrough",
4832
- icon: "sp-icon-text-strikethrough",
4833
6024
  command: "strikethrough"
4834
6025
  },
4835
6026
  {
4836
6027
  tag: "sup",
4837
6028
  label: "Superscript",
4838
- icon: "sp-icon-text-superscript",
4839
6029
  command: "superscript"
4840
6030
  },
4841
6031
  {
4842
6032
  tag: "sub",
4843
6033
  label: "Subscript",
4844
- icon: "sp-icon-text-subscript",
4845
6034
  command: "subscript"
4846
6035
  },
4847
- { tag: "code", label: "Code", icon: "sp-icon-code", command: "code" },
4848
- { tag: "a", label: "Link", icon: "sp-icon-link", command: "link" }
6036
+ { tag: "code", label: "Code", command: "code" },
6037
+ { tag: "a", label: "Link", command: "link" }
4849
6038
  ]
4850
6039
  },
4851
6040
  h1: {
@@ -4855,17 +6044,15 @@ var elements_meta_default = {
4855
6044
  {
4856
6045
  tag: "strong",
4857
6046
  label: "Bold",
4858
- icon: "sp-icon-text-bold",
4859
6047
  command: "bold"
4860
6048
  },
4861
6049
  {
4862
6050
  tag: "em",
4863
6051
  label: "Italic",
4864
- icon: "sp-icon-text-italic",
4865
6052
  command: "italic"
4866
6053
  },
4867
- { tag: "code", label: "Code", icon: "sp-icon-code", command: "code" },
4868
- { tag: "a", label: "Link", icon: "sp-icon-link", command: "link" }
6054
+ { tag: "code", label: "Code", command: "code" },
6055
+ { tag: "a", label: "Link", command: "link" }
4869
6056
  ]
4870
6057
  },
4871
6058
  h2: {
@@ -5088,16 +6275,14 @@ var elements_meta_default = {
5088
6275
  {
5089
6276
  tag: "strong",
5090
6277
  label: "Bold",
5091
- icon: "sp-icon-text-bold",
5092
6278
  command: "bold"
5093
6279
  },
5094
6280
  {
5095
6281
  tag: "em",
5096
6282
  label: "Italic",
5097
- icon: "sp-icon-text-italic",
5098
6283
  command: "italic"
5099
6284
  },
5100
- { tag: "code", label: "Code", icon: "sp-icon-code", command: "code" }
6285
+ { tag: "code", label: "Code", command: "code" }
5101
6286
  ]
5102
6287
  },
5103
6288
  span: {
@@ -5526,7 +6711,7 @@ function findTemplateExpressions(text) {
5526
6711
 
5527
6712
  // src/editor/editable-tags.ts
5528
6713
  var EXCLUDED = new Set(["pre"]);
5529
- var BUILTIN_EDITABLE_TAGS = new Set(Object.entries(elements_meta_default.$defs).filter(([tag, def2]) => !EXCLUDED.has(tag) && (def2.$inlineChildren?.length ?? 0) > 0).map(([tag]) => tag));
6714
+ var BUILTIN_EDITABLE_TAGS = new Set(Object.entries(elements_meta_default.$defs).filter(([tag, def]) => !EXCLUDED.has(tag) && (def.$inlineChildren?.length ?? 0) > 0).map(([tag]) => tag));
5530
6715
  function isEditableTag(tag, verdicts) {
5531
6716
  const declared = verdicts?.[tag.toLowerCase()];
5532
6717
  return declared ?? BUILTIN_EDITABLE_TAGS.has(tag.toLowerCase());
@@ -5978,18 +7163,64 @@ function nearestHit(target) {
5978
7163
  }
5979
7164
  return measureHit(el, el.dataset.jxPath);
5980
7165
  }
7166
+ function isRendered(el) {
7167
+ const check = el.checkVisibility;
7168
+ return typeof check === "function" ? check.call(el, { checkVisibilityCSS: true }) : true;
7169
+ }
5981
7170
  function measureHits(paths, doc = document) {
5982
- const out = [];
7171
+ const hits = [];
7172
+ const hidden = [];
5983
7173
  for (const path of paths) {
5984
- const serialized = serializeJxPath(path);
5985
- const esc = serialized.replaceAll("\\", String.raw`\\`).replaceAll("'", String.raw`\'`);
5986
- const el = doc.querySelector(`[data-jx-path='${esc}']`);
5987
- if (el) {
7174
+ const el = doc.querySelector(jxPathSelector(serializeJxPath(path)));
7175
+ if (!el) {
7176
+ continue;
7177
+ }
7178
+ if (isRendered(el)) {
5988
7179
  const r = rectOf(el);
5989
- out.push({ path, rect: { height: r.height, width: r.width, x: r.x, y: r.y } });
7180
+ hits.push({ path, rect: { height: r.height, width: r.width, x: r.x, y: r.y } });
7181
+ } else {
7182
+ hidden.push(path);
5990
7183
  }
5991
7184
  }
5992
- return out;
7185
+ return { hidden, hits };
7186
+ }
7187
+ var INVOKER_ACTIONS = new Set(["toggle", "show", "hide"]);
7188
+ function postPopoverInvoke(target, channel) {
7189
+ if (!(target instanceof Element)) {
7190
+ return;
7191
+ }
7192
+ const invoker = target.closest("[popovertarget]");
7193
+ const id = invoker?.getAttribute("popovertarget");
7194
+ if (!invoker || !id) {
7195
+ return;
7196
+ }
7197
+ const panels = invoker.ownerDocument.querySelectorAll("[data-jx-popover]");
7198
+ const panel = [...panels].find((el) => el.id === id);
7199
+ const serialized = panel?.dataset.jxPath;
7200
+ if (!serialized) {
7201
+ return;
7202
+ }
7203
+ const raw = invoker.getAttribute("popovertargetaction") ?? "toggle";
7204
+ const action = INVOKER_ACTIONS.has(raw) ? raw : "toggle";
7205
+ channel.post({ action, kind: "popoverTargetClick", targetPath: parseJxPath(serialized) });
7206
+ }
7207
+ function postCommandInvoke(target, channel) {
7208
+ if (!(target instanceof Element)) {
7209
+ return;
7210
+ }
7211
+ const invoker = target.closest("[data-jx-commandfor]");
7212
+ const id = invoker?.dataset["jxCommandfor"];
7213
+ const command = invoker?.getAttribute("command")?.trim().toLowerCase() ?? "";
7214
+ if (!invoker || !id || !command) {
7215
+ return;
7216
+ }
7217
+ const overlays = invoker.ownerDocument.querySelectorAll("[data-jx-popover], dialog[data-jx-path]");
7218
+ const overlay = [...overlays].find((el) => el.id === id);
7219
+ const serialized = overlay?.dataset.jxPath;
7220
+ if (!serialized) {
7221
+ return;
7222
+ }
7223
+ channel.post({ command, kind: "commandTargetClick", targetPath: parseJxPath(serialized) });
5993
7224
  }
5994
7225
  function startInteraction(channel, doc = document, deps) {
5995
7226
  let lastHoverKey = null;
@@ -6020,6 +7251,8 @@ function startInteraction(channel, doc = document, deps) {
6020
7251
  kind: "hit"
6021
7252
  });
6022
7253
  }
7254
+ postPopoverInvoke(e.target, channel);
7255
+ postCommandInvoke(e.target, channel);
6023
7256
  };
6024
7257
  const reportHover = (el) => {
6025
7258
  if (isPreview()) {
@@ -6251,112 +7484,6 @@ function scrollDirection(y, viewportH, band = AUTO_SCROLL_BAND) {
6251
7484
  return 0;
6252
7485
  }
6253
7486
 
6254
- // src/canvas/iframe-position.ts
6255
- function isIslandBoundary(el) {
6256
- if (el.dataset.jxDefinitionRoot !== undefined) {
6257
- return false;
6258
- }
6259
- return el.getAttribute("contenteditable") === "false" || el.tagName.includes("-");
6260
- }
6261
- function activeBlockAt(node, isEditable) {
6262
- let el = node instanceof Element ? node : node?.parentElement ?? null;
6263
- while (el) {
6264
- if (el instanceof HTMLElement) {
6265
- if (el.dataset.jxPath && isEditable(el)) {
6266
- return { el, path: parseJxPath(el.dataset.jxPath) };
6267
- }
6268
- if (isIslandBoundary(el)) {
6269
- return null;
6270
- }
6271
- }
6272
- el = el.parentElement;
6273
- }
6274
- return null;
6275
- }
6276
- function elementForPath(container, path) {
6277
- const serialized = serializeJxPath(path);
6278
- const esc = serialized.replaceAll("\\", String.raw`\\`).replaceAll("'", String.raw`\'`);
6279
- const el = container.querySelector(`[data-jx-path='${esc}']`);
6280
- return el instanceof HTMLElement ? el : null;
6281
- }
6282
- function blockTextLength(block) {
6283
- return block.textContent?.length ?? 0;
6284
- }
6285
- function offsetOf(block, node, offset) {
6286
- if (!block.contains(node)) {
6287
- return null;
6288
- }
6289
- const range = block.ownerDocument.createRange();
6290
- range.setStart(block, 0);
6291
- try {
6292
- range.setEnd(node, offset);
6293
- } catch {
6294
- return null;
6295
- }
6296
- return range.toString().length;
6297
- }
6298
- function domPositionAt(block, offset) {
6299
- const target = Math.max(0, Math.min(offset, blockTextLength(block)));
6300
- const walker = block.ownerDocument.createTreeWalker(block, NodeFilter.SHOW_TEXT);
6301
- let seen = 0;
6302
- let last = null;
6303
- let current = walker.nextNode();
6304
- while (current) {
6305
- const len = current.length;
6306
- if (seen + len >= target) {
6307
- return { node: current, offset: target - seen };
6308
- }
6309
- seen += len;
6310
- last = current;
6311
- current = walker.nextNode();
6312
- }
6313
- return last ? { node: last, offset: last.length } : { node: block, offset: 0 };
6314
- }
6315
- function toDocPos(node, offset, isEditable) {
6316
- const block = activeBlockAt(node, isEditable);
6317
- if (!block || !node) {
6318
- return null;
6319
- }
6320
- const charOffset = offsetOf(block.el, node, offset);
6321
- return charOffset === null ? null : { offset: charOffset, path: block.path };
6322
- }
6323
- function toDomPosition(container, pos) {
6324
- const el = elementForPath(container, pos.path);
6325
- return el ? domPositionAt(el, pos.offset) : null;
6326
- }
6327
- function isAtBlockStart(pos) {
6328
- return pos.offset <= 0;
6329
- }
6330
- function samePath(a, b) {
6331
- return serializeJxPath(a.path) === serializeJxPath(b.path);
6332
- }
6333
- function blocksInOrder(container, isEditable) {
6334
- const out = [];
6335
- for (const el of container.querySelectorAll("[data-jx-path]")) {
6336
- if (isEditable(el) && !sealedInIsland(el, container)) {
6337
- out.push(el);
6338
- }
6339
- }
6340
- return out;
6341
- }
6342
- function sealedInIsland(el, container) {
6343
- let cur = el.parentElement;
6344
- while (cur && cur !== container) {
6345
- if (isIslandBoundary(cur)) {
6346
- return true;
6347
- }
6348
- cur = cur.parentElement;
6349
- }
6350
- return false;
6351
- }
6352
- function adjacentBlock(container, path, direction, isEditable) {
6353
- const blocks = blocksInOrder(container, isEditable);
6354
- const key = serializeJxPath(path);
6355
- const index = blocks.findIndex((el) => el.dataset.jxPath === key);
6356
- const neighbour = index === -1 ? undefined : blocks[index + direction];
6357
- return neighbour ? { el: neighbour, path: parseJxPath(neighbour.dataset.jxPath) } : null;
6358
- }
6359
-
6360
7487
  // src/canvas/editable-actions.ts
6361
7488
  var DELETE_BACKWARD = new Set([
6362
7489
  "deleteContentBackward",
@@ -6465,7 +7592,7 @@ function restoreDocSelection(container, range) {
6465
7592
  }
6466
7593
  function startEditableRoot(container, deps) {
6467
7594
  const doc = container.ownerDocument;
6468
- let activeEl2 = null;
7595
+ let activeEl = null;
6469
7596
  let activeKey = null;
6470
7597
  const editingAllowed = () => {
6471
7598
  const mode = deps.getMode?.();
@@ -6480,7 +7607,7 @@ function startEditableRoot(container, deps) {
6480
7607
  };
6481
7608
  const commitTick = () => {
6482
7609
  commitTimer = 0;
6483
- if (!activeEl2 || !deps.onCommitTick) {
7610
+ if (!activeEl || !deps.onCommitTick) {
6484
7611
  return;
6485
7612
  }
6486
7613
  const caret = captureDocSelection(container, deps.isEditableBlock);
@@ -6491,7 +7618,7 @@ function startEditableRoot(container, deps) {
6491
7618
  };
6492
7619
  let composing = false;
6493
7620
  const scheduleCommitTick = () => {
6494
- if (!deps.onCommitTick || !activeEl2 || composing) {
7621
+ if (!deps.onCommitTick || !activeEl || composing) {
6495
7622
  return;
6496
7623
  }
6497
7624
  cancelCommitTick();
@@ -6507,15 +7634,15 @@ function startEditableRoot(container, deps) {
6507
7634
  };
6508
7635
  const forget = () => {
6509
7636
  cancelCommitTick();
6510
- activeEl2 = null;
7637
+ activeEl = null;
6511
7638
  activeKey = null;
6512
7639
  };
6513
7640
  const deactivate = () => {
6514
- if (!activeEl2) {
7641
+ if (!activeEl) {
6515
7642
  return;
6516
7643
  }
6517
7644
  cancelCommitTick();
6518
- activeEl2 = null;
7645
+ activeEl = null;
6519
7646
  activeKey = null;
6520
7647
  deps.onDeactivate();
6521
7648
  };
@@ -6535,7 +7662,7 @@ function startEditableRoot(container, deps) {
6535
7662
  }
6536
7663
  const block = activeBlockAt(focus, deps.isEditableBlock);
6537
7664
  if (!block) {
6538
- if (activeEl2?.contains(focus)) {
7665
+ if (activeEl?.contains(focus)) {
6539
7666
  deps.onSelectionChange?.();
6540
7667
  return;
6541
7668
  }
@@ -6548,7 +7675,7 @@ function startEditableRoot(container, deps) {
6548
7675
  return;
6549
7676
  }
6550
7677
  deactivate();
6551
- activeEl2 = block.el;
7678
+ activeEl = block.el;
6552
7679
  activeKey = key;
6553
7680
  deps.onActivate(block.el, block.path);
6554
7681
  };
@@ -6584,7 +7711,7 @@ function startEditableRoot(container, deps) {
6584
7711
  atBlockStart: resolved ? isAtBlockStart(resolved.from) : false,
6585
7712
  data: e.data ?? "",
6586
7713
  from: resolved?.from ?? null,
6587
- inPropHost: activeEl2 !== null && activeKey === null,
7714
+ inPropHost: activeEl !== null && activeKey === null,
6588
7715
  inputType: e.inputType,
6589
7716
  to: resolved?.to ?? null
6590
7717
  });
@@ -6627,10 +7754,10 @@ function startEditableRoot(container, deps) {
6627
7754
  let el = e.target instanceof Element ? e.target : null;
6628
7755
  while (el && el !== container) {
6629
7756
  if (el.dataset?.jxBoundProp) {
6630
- if (el !== activeEl2) {
7757
+ if (el !== activeEl) {
6631
7758
  deactivate();
6632
7759
  if (deps.onPropActivate(el)) {
6633
- activeEl2 = el;
7760
+ activeEl = el;
6634
7761
  activeKey = null;
6635
7762
  }
6636
7763
  }
@@ -6891,8 +8018,8 @@ function prepareForEditMode(node) {
6891
8018
  propsOut[pk] = templateToEditDisplay(pv);
6892
8019
  }
6893
8020
  } else if (pv && typeof pv === "object" && pv.$ref) {
6894
- const ref2 = pv.$ref;
6895
- const label = ref2.startsWith("#/state/") ? ref2.slice(8) : ref2;
8021
+ const ref = pv.$ref;
8022
+ const label = ref.startsWith("#/state/") ? ref.slice(8) : ref;
6896
8023
  propsOut[pk] = `{${label}}`;
6897
8024
  } else {
6898
8025
  propsOut[pk] = pv;
@@ -6943,8 +8070,8 @@ function prepareForEditMode(node) {
6943
8070
  const isUrlAttr = ak === "href" || ak === "action";
6944
8071
  processed[ak] = isUrlAttr ? "" : templateToEditDisplay(av);
6945
8072
  } else if (av && typeof av === "object" && av.$ref) {
6946
- const ref2 = av.$ref;
6947
- const label = ref2.startsWith("#/state/") ? ref2.slice(8) : ref2;
8073
+ const ref = av.$ref;
8074
+ const label = ref.startsWith("#/state/") ? ref.slice(8) : ref;
6948
8075
  processed[ak] = `{${label}}`;
6949
8076
  } else {
6950
8077
  processed[ak] = av;
@@ -6955,7 +8082,9 @@ function prepareForEditMode(node) {
6955
8082
  if (v && typeof v === "object") {
6956
8083
  const s = {};
6957
8084
  for (const [sk, sv] of Object.entries(v)) {
6958
- s[sk] = typeof sv === "string" && sv.includes("${") ? "" : sv;
8085
+ if (typeof sv !== "string" || !sv.includes("${")) {
8086
+ s[sk] = sv;
8087
+ }
6959
8088
  }
6960
8089
  out.style = s;
6961
8090
  } else {
@@ -6971,8 +8100,8 @@ function prepareForEditMode(node) {
6971
8100
  out[k] = isUrlAttr ? "" : templateToEditDisplay(v);
6972
8101
  }
6973
8102
  } else if (v && typeof v === "object" && v.$ref) {
6974
- const ref2 = v.$ref;
6975
- const label = ref2.startsWith("#/state/") ? ref2.slice(8) : ref2;
8103
+ const ref = v.$ref;
8104
+ const label = ref.startsWith("#/state/") ? ref.slice(8) : ref;
6976
8105
  const isMediaSrc = (k === "src" || k === "poster") && mediaTags.has(obj.tagName || "");
6977
8106
  if (isMediaSrc) {
6978
8107
  needsMediaPlaceholder = true;
@@ -7700,8 +8829,8 @@ function renderSubtreeIframe(shadowDoc, docPath, ctx) {
7700
8829
  if (node === undefined) {
7701
8830
  throw new Error(`iframe-patch-node-not-found:${docPath.join("/")}`);
7702
8831
  }
7703
- const def2 = typeof node === "string" ? node : prepareForEditMode(stripEventHandlers(node));
7704
- const { result: rendered, stop } = runScoped(() => renderNode(def2, ctx.defs, {
8832
+ const def = typeof node === "string" ? node : prepareForEditMode(stripEventHandlers(node));
8833
+ const { result: rendered, stop } = runScoped(() => renderNode(def, ctx.defs, {
7705
8834
  _path: docPathToRenderPath(docPath, ctx),
7706
8835
  onNodeCreated: makeStamper(ctx.mapperCtx)
7707
8836
  }));
@@ -7717,11 +8846,7 @@ function disposeSubtree(el) {
7717
8846
  if (!(t instanceof HTMLElement)) {
7718
8847
  continue;
7719
8848
  }
7720
- const tag = elementStyleTags.get(t);
7721
- if (tag) {
7722
- tag.remove();
7723
- elementStyleTags.delete(t);
7724
- }
8849
+ releaseElementStyles(t);
7725
8850
  const stop = elScope.get(t);
7726
8851
  if (stop) {
7727
8852
  stop();
@@ -7869,9 +8994,9 @@ function syncPlaceholderClass(el, node) {
7869
8994
  el.classList.add(placeholder);
7870
8995
  }
7871
8996
  }
7872
- function insertAt(parentEl, node, ref2) {
7873
- if (ref2) {
7874
- ref2.before(node);
8997
+ function insertAt(parentEl, node, ref) {
8998
+ if (ref) {
8999
+ ref.before(node);
7875
9000
  } else {
7876
9001
  parentEl.append(node);
7877
9002
  }
@@ -7945,7 +9070,10 @@ function editModeStyle(style) {
7945
9070
  }
7946
9071
  const out = {};
7947
9072
  for (const [k, v] of Object.entries(style)) {
7948
- out[k] = typeof v === "string" && v.includes("${") ? "" : v;
9073
+ if (typeof v === "string" && v.includes("${")) {
9074
+ continue;
9075
+ }
9076
+ out[k] = v;
7949
9077
  }
7950
9078
  return out;
7951
9079
  }
@@ -7958,8 +9086,8 @@ function textDisplayValue(node) {
7958
9086
  return v.includes("${") ? templateToEditDisplay(v) : v;
7959
9087
  }
7960
9088
  if (typeof v === "object" && v.$ref) {
7961
- const ref2 = String(v.$ref);
7962
- const label = ref2.startsWith("#/state/") ? ref2.slice(8) : ref2;
9089
+ const ref = String(v.$ref);
9090
+ const label = ref.startsWith("#/state/") ? ref.slice(8) : ref;
7963
9091
  return `{${label}}`;
7964
9092
  }
7965
9093
  return String(v);
@@ -8094,6 +9222,32 @@ function serializeDataScope(defs) {
8094
9222
  return out;
8095
9223
  }
8096
9224
 
9225
+ // ../ui/src/loaders.ts
9226
+ var KIT_LOADERS = {
9227
+ "jx-ui:/behaviors/color-area.ts": () => (__preload("v17df7b8"), import("./chunks/color-area-v17df7b8.js")),
9228
+ "jx-ui:/behaviors/color-field.ts": () => (__preload("anwybnkn"), import("./chunks/color-field-anwybnkn.js")),
9229
+ "jx-ui:/behaviors/color-slider.ts": () => (__preload("44ez96fc"), import("./chunks/color-slider-44ez96fc.js")),
9230
+ "jx-ui:/behaviors/combobox.ts": () => (__preload("5e7jev23"), import("./chunks/combobox-5e7jev23.js")),
9231
+ "jx-ui:/behaviors/dialog.ts": () => (__preload("s890b918"), import("./chunks/dialog-s890b918.js")),
9232
+ "jx-ui:/behaviors/listbox.ts": () => (__preload("6v2ac08j"), import("./chunks/listbox-6v2ac08j.js")),
9233
+ "jx-ui:/behaviors/menu.ts": () => (__preload("q99sy9rf"), import("./chunks/menu-q99sy9rf.js")),
9234
+ "jx-ui:/behaviors/number-field.ts": () => (__preload("5cndyft8"), import("./chunks/number-field-5cndyft8.js")),
9235
+ "jx-ui:/behaviors/action-group.ts": () => (__preload("sp6tna8v"), import("./chunks/action-group-sp6tna8v.js")),
9236
+ "jx-ui:/behaviors/popover.ts": () => (__preload("47z3ytva"), import("./chunks/popover-47z3ytva.js")),
9237
+ "jx-ui:/behaviors/select.ts": () => (__preload("2r16ez41"), import("./chunks/select-2r16ez41.js")),
9238
+ "jx-ui:/behaviors/split.ts": () => (__preload("g7nm1rgb"), import("./chunks/split-g7nm1rgb.js")),
9239
+ "jx-ui:/behaviors/swatch-group.ts": () => (__preload("t0n6h2rn"), import("./chunks/swatch-group-t0n6h2rn.js")),
9240
+ "jx-ui:/behaviors/tabs.ts": () => (__preload("wbp1cdws"), import("./chunks/tabs-wbp1cdws.js")),
9241
+ "jx-ui:/behaviors/toolbar.ts": () => (__preload("xbh5bhtr"), import("./chunks/toolbar-xbh5bhtr.js")),
9242
+ "jx-ui:/behaviors/tree.ts": () => (__preload("a73p0pqm"), import("./chunks/tree-a73p0pqm.js")),
9243
+ "jx-ui:/behaviors/tooltip.ts": () => (__preload("eptb614w"), import("./chunks/tooltip-eptb614w.js")),
9244
+ "jx-ui:/behaviors/textfield.ts": () => (__preload("abk6qyee"), import("./chunks/textfield-abk6qyee.js")),
9245
+ "jx-ui:/behaviors/toast.ts": () => (__preload("780pvfbv"), import("./chunks/toast-780pvfbv.js")),
9246
+ "jx-ui:/behaviors/toast-host.ts": () => (__preload("3sqv46sn"), import("./chunks/toast-host-3sqv46sn.js")),
9247
+ "jx-ui:/color.ts": () => (__preload("xgg3kfw7"), import("./chunks/color-xgg3kfw7.js")),
9248
+ "jx-ui:/icons.ts": () => (__preload("yy10wk9r"), import("./chunks/icons-yy10wk9r.js"))
9249
+ };
9250
+
8097
9251
  // src/services/bundle-base.ts
8098
9252
  var base;
8099
9253
  function setBundleBase(entryUrl) {
@@ -8102,6 +9256,9 @@ function setBundleBase(entryUrl) {
8102
9256
 
8103
9257
  // src/canvas/iframe-entry.ts
8104
9258
  setBundleBase(import.meta.url);
9259
+ for (const [specifier, load] of Object.entries(KIT_LOADERS)) {
9260
+ preloadModule(specifier, load);
9261
+ }
8105
9262
  function previewAt(cursor, src, shadowDoc, doc) {
8106
9263
  const targetEl = resolveDropTarget(cursor.x, cursor.y, doc);
8107
9264
  if (!targetEl) {
@@ -8147,8 +9304,8 @@ function layoutHitFor(target) {
8147
9304
  }
8148
9305
  var IN_PLACE_KEYS = new Set(["style", "textContent"]);
8149
9306
  function patchDisturbsActiveEdit(forwardOps) {
8150
- const activePath2 = getActivePath();
8151
- if (!activePath2) {
9307
+ const activePath = getActivePath();
9308
+ if (!activePath) {
8152
9309
  return false;
8153
9310
  }
8154
9311
  for (const op of forwardOps) {
@@ -8160,14 +9317,14 @@ function patchDisturbsActiveEdit(forwardOps) {
8160
9317
  affected = op.path;
8161
9318
  } else if (op.op === "move-child") {
8162
9319
  affected = op.fromParentPath;
8163
- if (isAncestor(affected, activePath2) || isAncestor(op.toParentPath, activePath2)) {
9320
+ if (isAncestor(affected, activePath) || isAncestor(op.toParentPath, activePath)) {
8164
9321
  return true;
8165
9322
  }
8166
9323
  continue;
8167
9324
  } else {
8168
9325
  affected = op.parentPath;
8169
9326
  }
8170
- if (isAncestor(affected, activePath2)) {
9327
+ if (isAncestor(affected, activePath)) {
8171
9328
  return true;
8172
9329
  }
8173
9330
  }
@@ -8184,6 +9341,8 @@ function startCanvasIframe(opts) {
8184
9341
  let shadowDoc = null;
8185
9342
  let renderedGen = -1;
8186
9343
  let currentMode = "design";
9344
+ let popoverOpen = null;
9345
+ let dialogOpen = null;
8187
9346
  let forwardTable = NO_TABLE;
8188
9347
  let renderCtx = null;
8189
9348
  let dragSrc = null;
@@ -8264,8 +9423,18 @@ function startCanvasIframe(opts) {
8264
9423
  const stopImageRetry = installCanvasImageRetry(container);
8265
9424
  const MAX_CANVAS_HEIGHT = 30000;
8266
9425
  let lastPostedHeight = -1;
9426
+ function openPopoverBottom() {
9427
+ const panels = container.querySelectorAll("[data-jx-popover][data-jx-popover-open], dialog[data-jx-dialog-open]");
9428
+ const root = rectOf(container);
9429
+ let bottom = 0;
9430
+ for (const panel of panels) {
9431
+ bottom = Math.max(bottom, rectOf(panel).bottom - root.top);
9432
+ }
9433
+ return bottom;
9434
+ }
8267
9435
  function postContentHeight() {
8268
- const measured = Math.min(container.scrollHeight, MAX_CANVAS_HEIGHT);
9436
+ const content = Math.max(container.scrollHeight, openPopoverBottom());
9437
+ const measured = Math.min(content, MAX_CANVAS_HEIGHT);
8269
9438
  if (measured > 0 && Math.abs(measured - lastPostedHeight) >= 1) {
8270
9439
  lastPostedHeight = measured;
8271
9440
  const root = container.firstElementChild;
@@ -8313,6 +9482,7 @@ function startCanvasIframe(opts) {
8313
9482
  const quiet = sample.fonts && sample.animations === 0 && sample.images === 0;
8314
9483
  quietFrames = quiet ? changed ? 1 : quietFrames + 1 : 0;
8315
9484
  if (quietFrames >= IDLE_QUIET_FRAMES) {
9485
+ postContentHeight();
8316
9486
  return;
8317
9487
  }
8318
9488
  if (Date.now() >= idleDeadline) {
@@ -8436,11 +9606,8 @@ function startCanvasIframe(opts) {
8436
9606
  container.ownerDocument.addEventListener("drop", onNativeDrop, true);
8437
9607
  const off = channel.onMessage((msg) => {
8438
9608
  if (msg.kind === "measure") {
8439
- channel.post({
8440
- hits: measureHits(msg.paths, container.ownerDocument),
8441
- kind: "geometry",
8442
- reqId: msg.reqId
8443
- });
9609
+ const { hidden, hits } = measureHits(msg.paths, container.ownerDocument);
9610
+ channel.post({ hidden, hits, kind: "geometry", reqId: msg.reqId });
8444
9611
  return;
8445
9612
  }
8446
9613
  if (msg.kind === "evalExpr") {
@@ -8449,13 +9616,13 @@ function startCanvasIframe(opts) {
8449
9616
  return;
8450
9617
  }
8451
9618
  if (msg.kind === "patch") {
8452
- const { gen: gen2 } = msg;
8453
- if (gen2 < renderedGen) {
8454
- channel.post({ gen: gen2, kind: "patchError", message: "patch-behind-render" });
9619
+ const { gen } = msg;
9620
+ if (gen < renderedGen) {
9621
+ channel.post({ gen, kind: "patchError", message: "patch-behind-render" });
8455
9622
  return;
8456
9623
  }
8457
- if (gen2 > renderedGen || !shadowDoc || !renderCtx) {
8458
- channel.post({ gen: gen2, kind: "patchError", message: "patch-ahead-of-render" });
9624
+ if (gen > renderedGen || !shadowDoc || !renderCtx) {
9625
+ channel.post({ gen, kind: "patchError", message: "patch-ahead-of-render" });
8459
9626
  return;
8460
9627
  }
8461
9628
  const echoed = new Set((msg.echoPaths ?? []).map((p) => serializeJxPath(p)));
@@ -8467,14 +9634,16 @@ function startCanvasIframe(opts) {
8467
9634
  }
8468
9635
  try {
8469
9636
  applyIframePatch(shadowDoc, msg.forwardOps, container, renderCtx, msg.echoPaths);
9637
+ applyCanvasPopoverOpen(container, popoverOpen);
9638
+ applyCanvasDialogOpen(container, dialogOpen);
8470
9639
  if (caret) {
8471
9640
  restoreDocSelection(container, caret);
8472
9641
  }
8473
- channel.post({ gen: gen2, kind: "patchComplete" });
9642
+ channel.post({ gen, kind: "patchComplete" });
8474
9643
  armIdleWatch();
8475
9644
  } catch (error) {
8476
9645
  channel.post({
8477
- gen: gen2,
9646
+ gen,
8478
9647
  kind: "patchError",
8479
9648
  message: String(error?.message ?? error)
8480
9649
  });
@@ -8534,6 +9703,29 @@ function startCanvasIframe(opts) {
8534
9703
  applyPreviewColorScheme(container.ownerDocument, msg.scheme);
8535
9704
  return;
8536
9705
  }
9706
+ if (msg.kind === "setPopoverOpen") {
9707
+ if (currentMode === "preview") {
9708
+ return;
9709
+ }
9710
+ popoverOpen = msg.path === null ? null : serializeJxPath(msg.path);
9711
+ applyCanvasPopoverOpen(container, popoverOpen);
9712
+ applyCanvasDialogOpen(container, dialogOpen);
9713
+ postContentHeight();
9714
+ return;
9715
+ }
9716
+ if (msg.kind === "setDialogOpen") {
9717
+ if (currentMode === "preview") {
9718
+ return;
9719
+ }
9720
+ dialogOpen = msg.path === null ? null : serializeJxPath(msg.path);
9721
+ applyCanvasDialogOpen(container, dialogOpen);
9722
+ postContentHeight();
9723
+ return;
9724
+ }
9725
+ if (msg.kind === "redefineElement") {
9726
+ redefineElement(msg.doc, msg.base);
9727
+ return;
9728
+ }
8537
9729
  if (msg.kind === "setLocale") {
8538
9730
  const root = container.ownerDocument.documentElement;
8539
9731
  if (msg.locale === null) {
@@ -8566,6 +9758,7 @@ function startCanvasIframe(opts) {
8566
9758
  stopDataScopeWatch?.();
8567
9759
  stopDataScopeWatch = null;
8568
9760
  handle?.dispose();
9761
+ resetDocumentStyles(container.ownerDocument);
8569
9762
  handle = await renderResolvedDocument({
8570
9763
  assets: msg.assets ?? null,
8571
9764
  container,
@@ -8578,11 +9771,18 @@ function startCanvasIframe(opts) {
8578
9771
  pageContentOffset: mapperCtx.pageContentOffset,
8579
9772
  pageContentPrefix: mapperCtx.pageContentPrefix
8580
9773
  },
9774
+ diffMarks: msg.diffMarks ?? null,
8581
9775
  mode: msg.mode,
9776
+ popoverOpen: msg.popoverOpen ? serializeJxPath(msg.popoverOpen) : null,
9777
+ dialogOpen: msg.dialogOpen ? serializeJxPath(msg.dialogOpen) : null,
8582
9778
  siteStyle: msg.siteStyle,
8583
9779
  ...msg.allowAutoRequests ? { allowAutoRequests: true } : {}
8584
9780
  });
8585
9781
  if (gen === latestGen) {
9782
+ popoverOpen = msg.popoverOpen ? serializeJxPath(msg.popoverOpen) : null;
9783
+ dialogOpen = msg.dialogOpen ? serializeJxPath(msg.dialogOpen) : null;
9784
+ applyCanvasPopoverOpen(container, popoverOpen);
9785
+ applyCanvasDialogOpen(container, dialogOpen);
8586
9786
  shadowDoc = rawDoc;
8587
9787
  renderCtx = handle.ctx;
8588
9788
  renderedGen = gen;
@@ -8665,5 +9865,5 @@ export {
8665
9865
  startCanvasIframe
8666
9866
  };
8667
9867
 
8668
- //# debugId=BF2F5E4A39B13F1864756E2164756E21
9868
+ //# debugId=2FA5492A7FC7235764756E2164756E21
8669
9869
  //# sourceMappingURL=iframe-entry.js.map