@jxsuite/studio 4.0.0 → 5.0.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 (613) 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-xp8vnwma.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-fyf63pmy.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-2w1f35qs.js +18 -0
  15. package/dist/chunks/content-source-3wqeas45.js +46 -0
  16. package/dist/chunks/convert-to-repeater-d0cgnb6d.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-d6g50j7s.js +42 -0
  20. package/dist/chunks/extension-sections-fy7q4fe9.js +651 -0
  21. package/dist/chunks/file-ops-3btj6st0.js +47 -0
  22. package/dist/chunks/files-ef8fgs3w.js +72 -0
  23. package/dist/chunks/format-host-3c33nwr8.js +40 -0
  24. package/dist/chunks/fs-events-13tj6sex.js +18 -0
  25. package/dist/chunks/git-panel-4wp2rn9b.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-jgesd924.js +115 -0
  31. package/dist/chunks/{index-nj44jpzj.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-1nq5evsh.js +29 -0
  37. package/dist/chunks/layers-panel-ar2gh90g.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-2qbetq73.js +33 -0
  41. package/dist/chunks/menu-q99sy9rf.js +285 -0
  42. package/dist/chunks/menu-rf7n72mf.js +14 -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-rws7k4br.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-98f7sr3g.js +27 -0
  52. package/dist/chunks/quick-search-srzz83k2.js +45 -0
  53. package/dist/chunks/references-n553f0e1.js +26 -0
  54. package/dist/chunks/right-panel-rrgx2dn3.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-18avnhjp.js +2430 -0
  60. package/dist/chunks/{studio-kryvsw6j.js → studio-1k325pwf.js} +48 -48
  61. package/dist/chunks/{studio-hbtp0eh1.js → studio-25hkdrkw.js} +356 -85
  62. package/dist/chunks/studio-26d7myd1.js +258 -0
  63. package/dist/chunks/{studio-p8j86wag.js → studio-38emantv.js} +22 -5
  64. package/dist/chunks/studio-3gxv60tn.js +422 -0
  65. package/dist/chunks/{studio-8swrjbny.js → studio-3hxkd92t.js} +275 -163
  66. package/dist/chunks/studio-4m0haseg.js +185 -0
  67. package/dist/chunks/{studio-ynxwkrzd.js → studio-4wbb9ct0.js} +1953 -1785
  68. package/dist/chunks/{studio-gv8vvshc.js → studio-5523bpzt.js} +11 -11
  69. package/dist/chunks/{studio-5q69gfag.js → studio-57mf66w2.js} +1753 -2484
  70. package/dist/chunks/studio-5jy81t3s.js +17463 -0
  71. package/dist/chunks/studio-5rt7sqa0.js +1168 -0
  72. package/dist/chunks/studio-5te5tqng.js +81 -0
  73. package/dist/chunks/{studio-p4ngc046.js → studio-6ewqv390.js} +9 -9
  74. package/dist/chunks/{studio-9ab8gqsm.js → studio-6nzaktvy.js} +7 -7
  75. package/dist/chunks/studio-6xa21ej9.js +28 -0
  76. package/dist/chunks/{studio-5ehhackm.js → studio-727xh0e3.js} +19 -19
  77. package/dist/chunks/{studio-eenfkt4e.js → studio-79y5sz51.js} +52 -38
  78. package/dist/chunks/{studio-4q0wznzf.js → studio-7e11jpa9.js} +461 -145
  79. package/dist/chunks/{studio-q7v10cg2.js → studio-7r42c6ca.js} +17 -7
  80. package/dist/chunks/studio-7xptrg0j.js +954 -0
  81. package/dist/chunks/{studio-cft9r4r7.js → studio-81390n6f.js} +22 -22
  82. package/dist/chunks/{studio-q43a5y9h.js → studio-8jn5yhyn.js} +16 -16
  83. package/dist/chunks/studio-8qtrpzr3.js +839 -0
  84. package/dist/chunks/{studio-dg7zyt5x.js → studio-98b3z07n.js} +1 -1
  85. package/dist/chunks/studio-9xxz1q69.js +1356 -0
  86. package/dist/chunks/studio-akbww0j5.js +13615 -0
  87. package/dist/chunks/studio-arfpe389.js +195 -0
  88. package/dist/chunks/{studio-2zkz8evn.js → studio-az3zd49t.js} +473 -281
  89. package/dist/chunks/studio-cpvb2ed1.js +1171 -0
  90. package/dist/chunks/{studio-9r6j5ev1.js → studio-d0300636.js} +5078 -19
  91. package/dist/chunks/{studio-bgxt93ga.js → studio-d055c37e.js} +13 -13
  92. package/dist/chunks/studio-d1ja2x9r.js +362 -0
  93. package/dist/chunks/{studio-b9b6t1dg.js → studio-e1f24eff.js} +5664 -5666
  94. package/dist/chunks/{studio-4pqqxbrt.js → studio-em9nw8eh.js} +301 -112
  95. package/dist/chunks/studio-erdatzvp.js +1190 -0
  96. package/dist/chunks/{studio-g1wdche0.js → studio-esrxgwh6.js} +80 -80
  97. package/dist/chunks/studio-fabxk6r9.js +2811 -0
  98. package/dist/chunks/{studio-3n4ndrzh.js → studio-h4nvnjvz.js} +303 -303
  99. package/dist/chunks/studio-hhvs4sdg.js +195 -0
  100. package/dist/chunks/{studio-68n67094.js → studio-kcgff4r0.js} +15 -18
  101. package/dist/chunks/{studio-685s0ytq.js → studio-nqgawfks.js} +572 -572
  102. package/dist/chunks/{studio-xrapxwwc.js → studio-p1dw8wfw.js} +55 -29
  103. package/dist/chunks/studio-pm6hgy7r.js +4763 -0
  104. package/dist/chunks/studio-q0a3yk4n.js +33 -0
  105. package/dist/chunks/{studio-wcy8trfx.js → studio-q3x06vmj.js} +23 -22
  106. package/dist/chunks/studio-qahxvnan.js +111 -0
  107. package/dist/chunks/{studio-53n8bp04.js → studio-r6xmmtfh.js} +1 -1
  108. package/dist/chunks/{studio-kjjsjak4.js → studio-rkstz940.js} +3661 -6136
  109. package/dist/chunks/{studio-tvgyw9xw.js → studio-rnyf3f8e.js} +83 -250
  110. package/dist/chunks/studio-rpyah2ew.js +2805 -0
  111. package/dist/chunks/studio-rs76k67r.js +12 -0
  112. package/dist/chunks/{studio-yxrkyv8a.js → studio-s6z853aq.js} +12108 -11766
  113. package/dist/chunks/{studio-sf7zjgv1.js → studio-sgdc5pfc.js} +8 -8
  114. package/dist/chunks/studio-tb3ev01j.js +763 -0
  115. package/dist/chunks/{studio-t5wqwzdh.js → studio-tjjk7jvk.js} +1 -1
  116. package/dist/chunks/studio-vhv5tgxm.js +51 -0
  117. package/dist/chunks/{studio-htsf0w6m.js → studio-vz68yn9e.js} +31 -154
  118. package/dist/chunks/studio-wqpngpsk.js +174 -0
  119. package/dist/chunks/{studio-eesa1myv.js → studio-x3rty6tr.js} +58 -58
  120. package/dist/chunks/studio-xsykpvsh.js +8448 -0
  121. package/dist/chunks/{studio-92ab2jqe.js → studio-y9zedyjn.js} +14 -14
  122. package/dist/chunks/studio-yxa81f45.js +12 -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-wrew8zk4.js +69 -0
  133. package/dist/chunks/{ws-client-6shc5rze.js → ws-client-ny4zc51r.js} +20 -22
  134. package/dist/iframe-entry.js +1812 -698
  135. package/dist/manifest.json +7 -0
  136. package/dist/studio.js +52900 -36573
  137. package/dist/workers/editor.worker.js +1040 -1085
  138. package/dist/workers/json.worker.js +1946 -1991
  139. package/dist/workers/ts.worker.js +4960 -5005
  140. package/favicon.ico +0 -0
  141. package/index.html +2 -0
  142. package/package.json +22 -55
  143. package/src/about/about-modal.ts +73 -123
  144. package/src/browse/library-commands.ts +20 -49
  145. package/src/browse/library-layouts.ts +87 -218
  146. package/src/browse/library-pane.ts +406 -438
  147. package/src/canvas/canvas-helpers.ts +0 -49
  148. package/src/canvas/canvas-live-render.ts +25 -2
  149. package/src/canvas/canvas-render.ts +666 -231
  150. package/src/canvas/canvas-surface.ts +0 -1
  151. package/src/canvas/canvas-utils.ts +310 -117
  152. package/src/canvas/dialog-path.ts +76 -0
  153. package/src/canvas/dialog-state.ts +67 -0
  154. package/src/canvas/diff-marks.ts +252 -0
  155. package/src/canvas/diff-toolbar.ts +349 -0
  156. package/src/canvas/diff-view.ts +123 -0
  157. package/src/canvas/edit-width-drag.ts +187 -0
  158. package/src/canvas/edit-width.ts +150 -0
  159. package/src/canvas/iframe-entry.ts +125 -8
  160. package/src/canvas/iframe-host.ts +271 -15
  161. package/src/canvas/iframe-interaction.ts +133 -13
  162. package/src/canvas/iframe-patch.ts +7 -1
  163. package/src/canvas/iframe-position.ts +5 -6
  164. package/src/canvas/iframe-protocol.ts +106 -5
  165. package/src/canvas/iframe-render.ts +373 -5
  166. package/src/canvas/iframe-slash.ts +2 -2
  167. package/src/canvas/iframe-subtree.ts +5 -8
  168. package/src/canvas/path-mapping.ts +25 -3
  169. package/src/canvas/popover-path.ts +106 -0
  170. package/src/canvas/popover-state.ts +155 -0
  171. package/src/canvas/surface-registry.ts +21 -10
  172. package/src/collab/presence-chips.ts +51 -73
  173. package/src/commands/app-commands.ts +10 -0
  174. package/src/commands/budget.ts +12 -0
  175. package/src/commands/command-args.ts +369 -6
  176. package/src/commands/defaults.ts +44 -14
  177. package/src/commands/live-context.ts +11 -5
  178. package/src/commands/reference.ts +12 -2
  179. package/src/commands/registry.ts +165 -8
  180. package/src/content/entry-commands.ts +16 -23
  181. package/src/content/entry-editor.ts +144 -101
  182. package/src/editor/canvas-slash-bridge.ts +2 -2
  183. package/src/editor/context-menu.ts +49 -200
  184. package/src/editor/convert-to-component.ts +42 -82
  185. package/src/editor/convert-to-repeater.ts +132 -173
  186. package/src/editor/inline-edit.ts +11 -0
  187. package/src/editor/shortcuts.ts +65 -26
  188. package/src/editor/slash-menu.ts +150 -157
  189. package/src/files/file-ops.ts +117 -11
  190. package/src/files/files.ts +949 -664
  191. package/src/files/json-layout.ts +474 -0
  192. package/src/files/media-paths.ts +13 -42
  193. package/src/files/media-usage.ts +41 -131
  194. package/src/files/serialize-document.ts +8 -1
  195. package/src/format/convert-file.ts +16 -10
  196. package/src/format/format-host.ts +50 -1
  197. package/src/github/github-auth.ts +71 -29
  198. package/src/github/github-publish.ts +57 -65
  199. package/src/grid/cell-editors.ts +46 -4
  200. package/src/grid/cell-popovers.ts +65 -62
  201. package/src/grid/grid-open.ts +86 -64
  202. package/src/grid/grid-panel.ts +390 -469
  203. package/src/grid/redirects-grid.ts +40 -44
  204. package/src/hosting/document.ts +2 -0
  205. package/src/hosting/layout.ts +10 -0
  206. package/src/i18n/i18n-commands.ts +127 -62
  207. package/src/media/media-pane.ts +83 -133
  208. package/src/new-project/add-repo-modal.ts +125 -176
  209. package/src/new-project/import-tab.ts +201 -250
  210. package/src/new-project/location-fields.ts +101 -165
  211. package/src/new-project/new-project-modal.ts +376 -367
  212. package/src/packages/jxsuite-update.ts +76 -18
  213. package/src/panels/activity-panel.ts +137 -104
  214. package/src/panels/ai-chat/chat-markdown.ts +15 -13
  215. package/src/panels/ai-chat/chat-view.ts +307 -472
  216. package/src/panels/ai-chat/composer.ts +175 -153
  217. package/src/panels/ai-chat/sessions-view.ts +31 -62
  218. package/src/panels/ai-panel.ts +210 -185
  219. package/src/panels/block-action-bar.ts +437 -643
  220. package/src/panels/bottom-dock.ts +112 -47
  221. package/src/panels/chat-panel.ts +19 -14
  222. package/src/panels/data-explorer.ts +141 -102
  223. package/src/panels/data-grid.ts +183 -150
  224. package/src/panels/dnd.ts +75 -20
  225. package/src/panels/editors.ts +26 -19
  226. package/src/panels/elements-panel.ts +243 -186
  227. package/src/panels/empty-state.ts +26 -42
  228. package/src/panels/events-panel.ts +950 -729
  229. package/src/panels/formula-workspace.ts +434 -207
  230. package/src/panels/frontmatter-fields.ts +185 -153
  231. package/src/panels/frontmatter-panel.ts +411 -187
  232. package/src/panels/git-diff-open.ts +148 -0
  233. package/src/panels/git-panel.ts +679 -605
  234. package/src/panels/head-panel.ts +617 -435
  235. package/src/panels/i18n-panel.ts +153 -133
  236. package/src/panels/imports-panel.ts +388 -414
  237. package/src/panels/jump-bar.ts +174 -147
  238. package/src/panels/layers-panel.ts +785 -671
  239. package/src/panels/left-panel.ts +166 -94
  240. package/src/panels/navigator-panels.ts +1 -1
  241. package/src/panels/pane-context.ts +529 -677
  242. package/src/panels/pane-grid.ts +265 -241
  243. package/src/panels/panel-registry.ts +15 -4
  244. package/src/panels/problems-panel.ts +177 -99
  245. package/src/panels/properties-panel.ts +1525 -966
  246. package/src/panels/provenance.ts +31 -113
  247. package/src/panels/quick-search.ts +217 -172
  248. package/src/panels/right-panel.ts +135 -169
  249. package/src/panels/seo-modal.ts +346 -167
  250. package/src/panels/settings-menu.ts +109 -646
  251. package/src/panels/settings-pane.ts +102 -86
  252. package/src/panels/signals-panel.ts +1318 -1172
  253. package/src/panels/statement-editor.ts +556 -428
  254. package/src/panels/style-panel.ts +1367 -630
  255. package/src/panels/style-utils.ts +36 -32
  256. package/src/panels/stylebook-doc.ts +30 -73
  257. package/src/panels/stylebook-layers-panel.ts +202 -109
  258. package/src/panels/stylebook-panel.ts +124 -88
  259. package/src/panels/tab-strip.ts +385 -287
  260. package/src/platforms/cloud.ts +347 -86
  261. package/src/platforms/devserver.ts +13 -0
  262. package/src/publish/deploy-checklist.ts +124 -78
  263. package/src/publish/publish-commands.ts +4 -12
  264. package/src/publish/publish-panel.ts +165 -299
  265. package/src/services/a11y-report.ts +100 -43
  266. package/src/services/ai-command-tools.ts +468 -0
  267. package/src/services/ai-import-tools.ts +50 -12
  268. package/src/services/ai-models.ts +108 -1
  269. package/src/services/ai-session-store.ts +6 -0
  270. package/src/services/ai-system-prompt.ts +110 -18
  271. package/src/services/ai-tools.ts +28 -143
  272. package/src/services/ai-write-report.ts +174 -0
  273. package/src/services/context-manager.ts +12 -1
  274. package/src/services/document-assistant.ts +50 -2
  275. package/src/services/idle.ts +16 -14
  276. package/src/services/live-surfaces.ts +237 -0
  277. package/src/services/model-uri.ts +75 -0
  278. package/src/services/notify.ts +63 -6
  279. package/src/services/popover-report.ts +325 -0
  280. package/src/services/surface-registry.ts +56 -0
  281. package/src/services/tool-executor.ts +17 -0
  282. package/src/settings/contexts-section.ts +301 -299
  283. package/src/settings/contributed-section.ts +361 -222
  284. package/src/settings/css-vars-editor.ts +320 -325
  285. package/src/settings/defs-editor.ts +572 -535
  286. package/src/settings/dependencies-editor.ts +79 -107
  287. package/src/settings/extension-commands.ts +379 -0
  288. package/src/settings/extension-rows.ts +113 -0
  289. package/src/settings/extensions-section.ts +287 -0
  290. package/src/settings/general-settings.ts +130 -181
  291. package/src/settings/head-editor.ts +10 -305
  292. package/src/settings/locales-section.ts +146 -196
  293. package/src/settings/preferences-accounts.ts +193 -12
  294. package/src/settings/preferences-dialog.ts +384 -307
  295. package/src/settings/project-sections.ts +108 -181
  296. package/src/settings/schema-field-ui.ts +11 -399
  297. package/src/settings/section-registry.ts +8 -1
  298. package/src/settings/settings-document.ts +31 -29
  299. package/src/shell/tree.ts +42 -87
  300. package/src/shell.ts +48 -36
  301. package/src/studio.ts +90 -67
  302. package/src/style/token-ref.ts +0 -39
  303. package/src/surfaces/about.json +263 -0
  304. package/src/surfaces/about.ts +163 -0
  305. package/src/surfaces/add-repo.json +486 -0
  306. package/src/surfaces/add-repo.ts +236 -0
  307. package/src/surfaces/ai-chat.json +2185 -0
  308. package/src/surfaces/ai-chat.ts +549 -0
  309. package/src/surfaces/ai-credentials-form.json +332 -0
  310. package/src/surfaces/ai-credentials-form.ts +151 -0
  311. package/src/surfaces/ai-managed-connect.json +108 -0
  312. package/src/surfaces/ai-managed-connect.ts +123 -0
  313. package/src/surfaces/ai-model-picker.json +114 -0
  314. package/src/surfaces/ai-model-picker.ts +139 -0
  315. package/src/surfaces/block-action-bar.json +640 -0
  316. package/src/surfaces/block-action-bar.ts +368 -0
  317. package/src/surfaces/bottom-dock.json +154 -0
  318. package/src/surfaces/bottom-dock.ts +125 -0
  319. package/src/surfaces/canvas-stage.json +444 -0
  320. package/src/surfaces/canvas-stage.ts +253 -0
  321. package/src/surfaces/cf-account-picker.json +300 -0
  322. package/src/surfaces/cf-account-picker.ts +214 -0
  323. package/src/surfaces/commandbar.json +984 -0
  324. package/src/{panels/toolbar.ts → surfaces/commandbar.ts} +414 -389
  325. package/src/surfaces/convert-repeater.json +309 -0
  326. package/src/surfaces/convert-repeater.ts +276 -0
  327. package/src/surfaces/data-actions.json +157 -0
  328. package/src/surfaces/data-actions.ts +115 -0
  329. package/src/surfaces/dialog.json +289 -0
  330. package/src/surfaces/dialog.ts +343 -0
  331. package/src/surfaces/diff-toolbar.json +266 -0
  332. package/src/surfaces/diff-toolbar.ts +139 -0
  333. package/src/surfaces/doc-header.json +739 -0
  334. package/src/surfaces/doc-header.ts +223 -0
  335. package/src/surfaces/empty-state.json +152 -0
  336. package/src/surfaces/empty-state.ts +173 -0
  337. package/src/surfaces/entry-editor.json +281 -0
  338. package/src/surfaces/entry-editor.ts +191 -0
  339. package/src/surfaces/expression-editor.json +977 -0
  340. package/src/surfaces/expression-editor.ts +266 -0
  341. package/src/surfaces/files-panel.json +592 -0
  342. package/src/surfaces/files-panel.ts +301 -0
  343. package/src/surfaces/formula-palette.json +364 -0
  344. package/src/surfaces/formula-palette.ts +340 -0
  345. package/src/surfaces/git-panel.json +1822 -0
  346. package/src/surfaces/git-panel.ts +272 -0
  347. package/src/surfaces/github-auth.json +112 -0
  348. package/src/surfaces/github-auth.ts +139 -0
  349. package/src/surfaces/github-publish.json +206 -0
  350. package/src/surfaces/github-publish.ts +169 -0
  351. package/src/surfaces/grid-cell.json +218 -0
  352. package/src/surfaces/grid-cell.ts +120 -0
  353. package/src/surfaces/grid-open.json +147 -0
  354. package/src/surfaces/grid-open.ts +87 -0
  355. package/src/surfaces/grid-panel.json +706 -0
  356. package/src/surfaces/grid-panel.ts +206 -0
  357. package/src/surfaces/grid-replace.json +179 -0
  358. package/src/surfaces/grid-replace.ts +89 -0
  359. package/src/surfaces/grid-views.json +446 -0
  360. package/src/surfaces/grid-views.ts +172 -0
  361. package/src/surfaces/inspector-dock.json +187 -0
  362. package/src/surfaces/inspector-dock.ts +161 -0
  363. package/src/surfaces/jump-bar.json +243 -0
  364. package/src/surfaces/jump-bar.ts +129 -0
  365. package/src/surfaces/jxsuite-update.json +146 -0
  366. package/src/surfaces/jxsuite-update.ts +149 -0
  367. package/src/surfaces/library-pane.json +1509 -0
  368. package/src/surfaces/library-pane.ts +363 -0
  369. package/src/surfaces/logic-panel.json +1949 -0
  370. package/src/surfaces/logic-panel.ts +369 -0
  371. package/src/surfaces/logic-workspace.json +685 -0
  372. package/src/surfaces/logic-workspace.ts +238 -0
  373. package/src/surfaces/media-browser.json +303 -0
  374. package/src/surfaces/media-browser.ts +145 -0
  375. package/src/surfaces/media-field.json +163 -0
  376. package/src/surfaces/media-field.ts +116 -0
  377. package/src/surfaces/media-pane.json +513 -0
  378. package/src/surfaces/media-pane.ts +197 -0
  379. package/src/surfaces/menu.json +232 -0
  380. package/src/surfaces/menu.ts +375 -0
  381. package/src/surfaces/navigator-dock.json +113 -0
  382. package/src/surfaces/navigator-dock.ts +130 -0
  383. package/src/surfaces/new-project.json +1654 -0
  384. package/src/surfaces/new-project.ts +411 -0
  385. package/src/surfaces/palette.json +535 -0
  386. package/src/surfaces/pane-context.json +1497 -0
  387. package/src/surfaces/pane-context.ts +513 -0
  388. package/src/surfaces/pane-grid.json +188 -0
  389. package/src/surfaces/pane-grid.ts +250 -0
  390. package/src/surfaces/panel-activity.json +563 -0
  391. package/src/surfaces/panel-activity.ts +187 -0
  392. package/src/surfaces/panel-data.json +141 -0
  393. package/src/surfaces/panel-data.ts +156 -0
  394. package/src/surfaces/panel-deploy-checklist.json +283 -0
  395. package/src/surfaces/panel-deploy-checklist.ts +175 -0
  396. package/src/surfaces/panel-elements.json +367 -0
  397. package/src/surfaces/panel-elements.ts +175 -0
  398. package/src/surfaces/panel-i18n.json +569 -0
  399. package/src/surfaces/panel-i18n.ts +212 -0
  400. package/src/surfaces/panel-imports.json +780 -0
  401. package/src/surfaces/panel-imports.ts +209 -0
  402. package/src/surfaces/panel-outline.json +734 -0
  403. package/src/surfaces/panel-outline.ts +326 -0
  404. package/src/surfaces/panel-page.json +1017 -0
  405. package/src/surfaces/panel-page.ts +240 -0
  406. package/src/surfaces/panel-problems.json +497 -0
  407. package/src/surfaces/panel-problems.ts +157 -0
  408. package/src/surfaces/panel-signals.json +1973 -0
  409. package/src/surfaces/panel-signals.ts +436 -0
  410. package/src/surfaces/panel-stylebook-layers.json +317 -0
  411. package/src/surfaces/panel-stylebook-layers.ts +155 -0
  412. package/src/surfaces/preferences.json +850 -0
  413. package/src/surfaces/preferences.ts +332 -0
  414. package/src/surfaces/progress-modal.json +110 -0
  415. package/src/surfaces/progress-modal.ts +150 -0
  416. package/src/surfaces/properties-panel.json +1578 -0
  417. package/src/surfaces/properties-panel.ts +337 -0
  418. package/src/surfaces/publish.json +945 -0
  419. package/src/surfaces/publish.ts +392 -0
  420. package/src/surfaces/push-plan.json +110 -0
  421. package/src/surfaces/push-plan.ts +87 -0
  422. package/src/surfaces/rail.json +255 -0
  423. package/src/surfaces/rail.ts +206 -0
  424. package/src/surfaces/reference-field.json +191 -0
  425. package/src/surfaces/reference-field.ts +168 -0
  426. package/src/surfaces/schema-builder.json +1011 -0
  427. package/src/surfaces/schema-builder.ts +181 -0
  428. package/src/surfaces/schema-form.json +1013 -0
  429. package/src/surfaces/schema-form.ts +287 -0
  430. package/src/surfaces/secret-field.json +60 -0
  431. package/src/surfaces/secret-field.ts +146 -0
  432. package/src/surfaces/seo.json +932 -0
  433. package/src/surfaces/seo.ts +334 -0
  434. package/src/surfaces/settings-contexts.json +538 -0
  435. package/src/surfaces/settings-contexts.ts +196 -0
  436. package/src/surfaces/settings-contributed.json +489 -0
  437. package/src/surfaces/settings-contributed.ts +199 -0
  438. package/src/surfaces/settings-css-vars.json +820 -0
  439. package/src/surfaces/settings-css-vars.ts +199 -0
  440. package/src/surfaces/settings-defs.json +1363 -0
  441. package/src/surfaces/settings-defs.ts +232 -0
  442. package/src/surfaces/settings-deploy.json +108 -0
  443. package/src/surfaces/settings-deploy.ts +135 -0
  444. package/src/surfaces/settings-extensions.json +486 -0
  445. package/src/surfaces/settings-extensions.ts +201 -0
  446. package/src/surfaces/settings-head.json +779 -0
  447. package/src/surfaces/settings-head.ts +397 -0
  448. package/src/surfaces/settings-locales.json +420 -0
  449. package/src/surfaces/settings-locales.ts +137 -0
  450. package/src/surfaces/settings-overview.json +423 -0
  451. package/src/surfaces/settings-overview.ts +218 -0
  452. package/src/surfaces/settings-packages.json +549 -0
  453. package/src/surfaces/settings-packages.ts +158 -0
  454. package/src/surfaces/settings-pane.json +147 -0
  455. package/src/surfaces/settings-pane.ts +150 -0
  456. package/src/surfaces/settings-rawjson.json +104 -0
  457. package/src/surfaces/settings-rawjson.ts +107 -0
  458. package/src/surfaces/shell.json +99 -0
  459. package/src/surfaces/shell.ts +30 -0
  460. package/src/surfaces/slash-menu.json +180 -0
  461. package/src/surfaces/slash-menu.ts +272 -0
  462. package/src/surfaces/statements.json +607 -0
  463. package/src/surfaces/statements.ts +238 -0
  464. package/src/surfaces/statusbar.json +94 -0
  465. package/src/{panels → surfaces}/statusbar.ts +163 -90
  466. package/src/surfaces/style-panel.json +1643 -0
  467. package/src/surfaces/style-panel.ts +338 -0
  468. package/src/surfaces/stylebook-chrome.json +122 -0
  469. package/src/surfaces/stylebook-chrome.ts +122 -0
  470. package/src/surfaces/tab-strip.json +448 -0
  471. package/src/surfaces/tab-strip.ts +200 -0
  472. package/src/surfaces/target-line.json +419 -0
  473. package/src/surfaces/target-line.ts +349 -0
  474. package/src/surfaces/toasts.json +189 -0
  475. package/src/surfaces/welcome.json +571 -0
  476. package/src/surfaces/welcome.ts +338 -0
  477. package/src/tabs/project-config.ts +71 -15
  478. package/src/tabs/tab.ts +52 -1
  479. package/src/types.ts +49 -3
  480. package/src/ui/ai-credentials-form.ts +104 -100
  481. package/src/ui/ai-managed-connect.ts +116 -49
  482. package/src/ui/ai-model-picker.ts +102 -47
  483. package/src/ui/cf-account-picker.ts +135 -0
  484. package/src/ui/color-selector.ts +80 -301
  485. package/src/ui/dynamic-slot.ts +43 -283
  486. package/src/ui/expression-editor.ts +797 -576
  487. package/src/ui/field-input.ts +8 -181
  488. package/src/ui/form-controls.ts +838 -366
  489. package/src/ui/formula-chips.ts +80 -85
  490. package/src/ui/kit.ts +33 -0
  491. package/src/ui/layers.ts +293 -593
  492. package/src/ui/media-picker.ts +203 -284
  493. package/src/ui/panel-resize.ts +251 -64
  494. package/src/ui/popover-surface.ts +153 -0
  495. package/src/ui/progress-modal.ts +22 -60
  496. package/src/ui/regions.ts +13 -7
  497. package/src/ui/schema-form.ts +906 -440
  498. package/src/ui/surface.ts +122 -0
  499. package/src/ui/unit-selector.ts +15 -131
  500. package/src/ui/value-source.ts +14 -0
  501. package/src/ui/virtual-window.ts +88 -9
  502. package/src/utils/canvas-media.ts +72 -0
  503. package/src/utils/edit-display.ts +7 -2
  504. package/src/utils/element-selectors.ts +69 -0
  505. package/src/utils/link-target.ts +2 -2
  506. package/src/utils/studio-utils.ts +0 -10
  507. package/src/view.ts +15 -0
  508. package/src/workspace/pane-derive.ts +36 -14
  509. package/src/workspace/workspace.ts +15 -6
  510. package/styles/canvas.css +72 -467
  511. package/styles/forced-colors.css +48 -82
  512. package/styles/forced-colors.json +41 -0
  513. package/styles/inspector.css +2 -1301
  514. package/styles/overlays.css +96 -2823
  515. package/styles/panels.css +6 -1423
  516. package/styles/shell-frame.css +272 -0
  517. package/styles/shell-frame.json +227 -0
  518. package/styles/shell.css +0 -1532
  519. package/styles/tokens.css +65 -136
  520. package/styles/tokens.json +84 -0
  521. package/dist/chunks/Overlay.dev-723bv0df.js +0 -23
  522. package/dist/chunks/adapter-jkcyhgtr.js +0 -14
  523. package/dist/chunks/cf-settings-45fgyx8y.js +0 -21
  524. package/dist/chunks/components-zmx10t62.js +0 -21
  525. package/dist/chunks/content-source-t88vb2dr.js +0 -49
  526. package/dist/chunks/convert-to-repeater-dhw1nrz9.js +0 -268
  527. package/dist/chunks/editor-cptq5jp1.js +0 -37
  528. package/dist/chunks/entry-commands-6z6bhxg0.js +0 -46
  529. package/dist/chunks/extension-sections-3ydgjbdy.js +0 -311
  530. package/dist/chunks/file-ops-rskmhxyq.js +0 -48
  531. package/dist/chunks/files-0cratsr0.js +0 -92
  532. package/dist/chunks/flow-qbfgrpeh.js +0 -634
  533. package/dist/chunks/focus-trap.esm-ggawf1hy.js +0 -1024
  534. package/dist/chunks/focus-visible-6eg70z18.js +0 -167
  535. package/dist/chunks/format-host-tgcmfs4p.js +0 -43
  536. package/dist/chunks/fs-events-hrrg328g.js +0 -21
  537. package/dist/chunks/git-panel-0yywyk76.js +0 -58
  538. package/dist/chunks/index-zqxmqxmh.js +0 -109
  539. package/dist/chunks/jx-validate-8q2g05x1.js +0 -19
  540. package/dist/chunks/layers-panel-z3tqe5cd.js +0 -67
  541. package/dist/chunks/layers-q75tj2mf.js +0 -45
  542. package/dist/chunks/lit-html-j055mkh8.js +0 -22
  543. package/dist/chunks/monaco-buffer-dqw0xxpx.js +0 -25
  544. package/dist/chunks/notify-yjm79n4s.js +0 -30
  545. package/dist/chunks/parse-24d0gay3.js +0 -19
  546. package/dist/chunks/platform-ba1j1gbh.js +0 -16
  547. package/dist/chunks/publish-panel-54jvhskq.js +0 -28
  548. package/dist/chunks/quick-search-cgfmjwq5.js +0 -65
  549. package/dist/chunks/references-3qx8fkyp.js +0 -29
  550. package/dist/chunks/right-panel-cqp8bc4v.js +0 -46
  551. package/dist/chunks/section-registry-sx1ykvkw.js +0 -30
  552. package/dist/chunks/sp-overlay.dev-yztmtrs1.js +0 -18
  553. package/dist/chunks/sp-popover.dev-n2etmqdp.js +0 -9
  554. package/dist/chunks/sp-progress-circle.dev-hryhj0es.js +0 -15
  555. package/dist/chunks/sp-tooltip.dev-xcvpnqr0.js +0 -15
  556. package/dist/chunks/sp-tray.dev-tkbscm06.js +0 -199
  557. package/dist/chunks/studio-0a5qrh01.js +0 -3
  558. package/dist/chunks/studio-18j3bmcz.js +0 -14
  559. package/dist/chunks/studio-1hbqnt19.js +0 -1823
  560. package/dist/chunks/studio-4rzyrtjz.js +0 -426
  561. package/dist/chunks/studio-5rywrkxz.js +0 -1903
  562. package/dist/chunks/studio-7afd21nw.js +0 -67
  563. package/dist/chunks/studio-7nte2hj4.js +0 -613
  564. package/dist/chunks/studio-8jpzqk5s.js +0 -128
  565. package/dist/chunks/studio-8q517dfr.js +0 -24
  566. package/dist/chunks/studio-9c4k4vhv.js +0 -357
  567. package/dist/chunks/studio-9n4mrmnb.js +0 -184
  568. package/dist/chunks/studio-a0tswy0w.js +0 -58
  569. package/dist/chunks/studio-ahjfdy6e.js +0 -387
  570. package/dist/chunks/studio-ahnzd9rg.js +0 -23
  571. package/dist/chunks/studio-bw5dz6km.js +0 -608
  572. package/dist/chunks/studio-cb12yz9w.js +0 -15055
  573. package/dist/chunks/studio-d1ks410h.js +0 -175
  574. package/dist/chunks/studio-eqq1w638.js +0 -113
  575. package/dist/chunks/studio-ex8zyp98.js +0 -1813
  576. package/dist/chunks/studio-h4rxscfc.js +0 -10
  577. package/dist/chunks/studio-kyh9avvv.js +0 -28
  578. package/dist/chunks/studio-kyk2v92a.js +0 -659
  579. package/dist/chunks/studio-m3hwn7y1.js +0 -120
  580. package/dist/chunks/studio-mmv72a27.js +0 -4344
  581. package/dist/chunks/studio-n8b9wqwy.js +0 -291
  582. package/dist/chunks/studio-pdfjntbk.js +0 -6939
  583. package/dist/chunks/studio-qq6jym04.js +0 -53
  584. package/dist/chunks/studio-r1r7afxx.js +0 -1066
  585. package/dist/chunks/studio-rdx8jcxy.js +0 -83
  586. package/dist/chunks/studio-rn9ta2mz.js +0 -111
  587. package/dist/chunks/studio-t7qb9db8.js +0 -665
  588. package/dist/chunks/studio-tmc5emrw.js +0 -8
  589. package/dist/chunks/studio-txwkm6p9.js +0 -12
  590. package/dist/chunks/studio-v38hmvx1.js +0 -390
  591. package/dist/chunks/studio-vadzgkrz.js +0 -318
  592. package/dist/chunks/studio-wx5609xd.js +0 -341
  593. package/dist/chunks/studio-z6nw2ek2.js +0 -18
  594. package/dist/chunks/studio-zczps70q.js +0 -378
  595. package/dist/chunks/workspace-xpres40v.js +0 -89
  596. package/src/panels/activity-bar.ts +0 -251
  597. package/src/panels/panel-scheduler.ts +0 -219
  598. package/src/panels/style-inputs.ts +0 -237
  599. package/src/panels/target-line.ts +0 -274
  600. package/src/panels/welcome-screen.ts +0 -346
  601. package/src/ui/button-group.ts +0 -102
  602. package/src/ui/choice-field.ts +0 -72
  603. package/src/ui/field-row.ts +0 -249
  604. package/src/ui/formula-palette.ts +0 -213
  605. package/src/ui/icons.ts +0 -52
  606. package/src/ui/jx-theme.ts +0 -130
  607. package/src/ui/spectrum.ts +0 -340
  608. package/src/ui/value-selector.ts +0 -174
  609. package/src/ui/widgets.ts +0 -172
  610. /package/dist/chunks/{javascript-s56a8dpk.css → javascript-4abzjcy9.css} +0 -0
  611. /package/dist/chunks/{jsonMode-zr9sw0c7.css → jsonMode-vvmpmk34.css} +0 -0
  612. /package/dist/chunks/{monaco-setup-pav68602.css → monaco-setup-8vbyqnhr.css} +0 -0
  613. /package/dist/chunks/{tsMode-ef187txx.css → tsMode-hnxxb73h.css} +0 -0
@@ -3,8 +3,10 @@
3
3
  * In-iframe render core — turns a fully-resolved document into live DOM via @jxsuite/runtime,
4
4
  * stamping `data-jx-path` so the editor can map nodes back to document paths across the frame
5
5
  * boundary. The parent does the heavy resolution (layout distribution, site-context, `$head`,
6
- * components, edit-mode transforms) and posts the result; this core stays dependency-light (runtime
7
- * + reactivity + the pure path-mapping helpers) so the iframe bundle is small.
6
+ * components, edit-mode transforms) and posts the result; this core stays dependency-light
7
+ * (runtime
8
+ *
9
+ * - Reactivity + the pure path-mapping helpers) so the iframe bundle is small.
8
10
  *
9
11
  * Because the iframe is served from the real project origin, the runtime's verbatim
10
12
  * `el.setAttribute("src", "/images/foo.jpg")` resolves natively — the fix that motivated the whole
@@ -19,6 +21,8 @@ import {
19
21
  runScoped,
20
22
  setCanvasAssetResolver,
21
23
  setCanvasDelinkAnchors,
24
+ setCanvasDelinkCommands,
25
+ setCanvasDelinkPopovers,
22
26
  setCanvasViewportTranspose,
23
27
  setRootMedia,
24
28
  setSkipAutoRequests,
@@ -26,10 +30,14 @@ import {
26
30
  setStampPropBindings,
27
31
  } from "@jxsuite/runtime";
28
32
  import { resolveAssetRef } from "./asset-resolve";
29
- import { classifyRenderNode, serializeJxPath } from "./path-mapping";
33
+ import { classifyRenderNode, jxPathSelector, serializeJxPath } from "./path-mapping";
34
+ /* No cycle: `iframe-position.ts` imports only `path-mapping` and a type. It already owns the
35
+ stamped-attribute lookup, escaping included, so a second query built here would be a second
36
+ answer to "which element is this path". */
37
+ import { elementForPath } from "./iframe-position";
30
38
  import type { AssetContext } from "./asset-resolve";
31
39
  import { SITE_STYLE_ID, buildSiteStyleCSS } from "@jxsuite/site/site-style";
32
- import type { CanvasMode } from "./iframe-protocol";
40
+ import type { CanvasMode, WireDiffMarks } from "./iframe-protocol";
33
41
  import type { JxDocument } from "@jxsuite/schema/types";
34
42
  import type { PathMapCtx } from "./path-mapping";
35
43
 
@@ -125,6 +133,10 @@ export const EDIT_PLACEHOLDER_STYLE_ID = "jx-canvas-edit-css";
125
133
  * both rules match the same element, and the emptiness test broke the specificity tie that used to
126
134
  * decide it by source order.
127
135
  */
136
+ /*
137
+ * NOTE: this is a template literal, so a BACKTICK anywhere inside — including in a CSS comment —
138
+ * ends the string and produces a syntax error several lines later. Quote property names bare.
139
+ */
128
140
  export const EDIT_PLACEHOLDER_CSS = `
129
141
  .empty-media-placeholder {
130
142
  display: inline-block;
@@ -236,6 +248,270 @@ export function syncEditModeCss(doc: Document, mode: CanvasMode): void {
236
248
  doc.head.append(style);
237
249
  }
238
250
 
251
+ /** Id of the injected canvas UA-substitute stylesheet (the overlay rules). */
252
+ export const CANVAS_OVERLAY_STYLE_ID = "jx-canvas-overlay-css";
253
+
254
+ /**
255
+ * The rules that stand in for the UA behaviour a de-linked overlay has just lost.
256
+ *
257
+ * **This sheet's predicate IS the predicate of `setCanvasDelinkPopovers` /
258
+ * `setCanvasDelinkCommands` — every mode but Preview — and that is the invariant to keep.** An
259
+ * attribute renamed without its substitute rule is an overlay that can never be drawn: `popover`
260
+ * renamed to `data-jx-popover` with no replacement hide rule lays every CLOSED panel out as
261
+ * ordinary in-flow content and inflates the artboard, and a dialog stamped `data-jx-dialog-open`
262
+ * stays hidden behind the UA `dialog:not([open])` for good. These rules used to ship inside
263
+ * `EDIT_PLACEHOLDER_CSS`, which is installed for design/edit alone, so Stylebook and a git-diff
264
+ * side de-linked without them. A read-only artboard must draw an overlay exactly as the design
265
+ * canvas does — a git-diff pair is read side by side, and the artboard-growth argument below is
266
+ * about a content-sized frame, not about editing.
267
+ */
268
+ /*
269
+ * NOTE: this is a template literal, so a BACKTICK anywhere inside — including in a CSS comment —
270
+ * ends the string and produces a syntax error several lines later. Quote property names bare.
271
+ */
272
+ export const CANVAS_OVERLAY_CSS = `
273
+ /* The UA rule a de-popovered element lost, re-supplied at UA-EQUIVALENT PRECEDENCE.
274
+
275
+ The cascade layer is the mechanism and it is the whole point. An unlayered author declaration
276
+ beats a layered one whatever its specificity, and every declaration applyStyle emits is an
277
+ unlayered rule in an adopted sheet. That is exactly how author origin beats UA origin on the
278
+ shipped page, so the canvas reproduces the real cascade rather than an approximation of it.
279
+
280
+ This used to read "a base declaration is written as an INLINE style", which was true and is the
281
+ reason the guarantee survived the move: inline beat layered harder still, and unlayered beats it
282
+ by the same rule one step down. Jx emits no layer of its own, which is what keeps that true.
283
+
284
+ Deliberately NOT forced with a priority flag. A popover whose base rule sets display is laid out
285
+ on every page whether open or not; that is a real defect, @jxsuite/schema/overlays reports it as
286
+ base-display, and the canvas's job is to SHOW it, not to hide it behind a stronger rule. */
287
+ @layer jx-canvas-ua {
288
+ [data-jx-popover]:not([data-jx-popover-open]) {
289
+ display: none;
290
+ }
291
+ dialog[data-jx-dialog-open] {
292
+ display: block;
293
+ }
294
+ }
295
+ /* SHOWN IN PLACE, and the position declaration is what makes that true.
296
+
297
+ Dropping the popover attribute drops the UA rule that made the panel fixed, so a panel that never
298
+ set position itself — every one in the fleet — lands in normal flow at its document position,
299
+ contributes to #jx-canvas-root's scrollHeight, and the host grows the artboard to fit it. That is
300
+ the whole geometry fix, and it is why an open panel is reachable at all.
301
+
302
+ The two alignment declarations are the other half, and they were found by measuring rather than
303
+ by reasoning. Every drawer in the fleet is declared inside its header's flex row, so in flow it
304
+ becomes a FLEX ITEM: align-items:center on the row centres a 904px panel on a 64px header and
305
+ half of it sits above the artboard at a negative offset, where it contributes nothing to the
306
+ scrollable overflow the host measures. Pinned to the start and refused any flex sizing, the same
307
+ panel hangs down from its own position and the artboard grows by its full height.
308
+
309
+ Forced, because a panel that DOES set position: fixed would otherwise keep it and be laid out
310
+ against the frame's own viewport — which in a de-linked mode is the document's full height, so a
311
+ drawer pinned with inset: 0 lands halfway down a long page and a short component frame clips it.
312
+ This is a presentation override for a canvas affordance, the same kind as the layout-region
313
+ dimming in EDIT_PLACEHOLDER_CSS, and it is NOT the same move as forcing display: that would hide
314
+ a real defect in the document (base-display), while this hides nothing — Preview renders the
315
+ panel natively, top layer and all. */
316
+ [data-jx-popover][data-jx-popover-open] {
317
+ position: relative !important;
318
+ inset: auto !important;
319
+ align-self: start !important;
320
+ flex: none !important;
321
+ outline: 1px dashed color-mix(in srgb, #808080 55%, transparent);
322
+ outline-offset: 2px;
323
+ }
324
+ [data-jx-popover][data-jx-popover-open]::before {
325
+ content: "POPOVER \\00B7 SHOWN IN PLACE";
326
+ position: absolute;
327
+ top: 0;
328
+ left: 0;
329
+ z-index: 2;
330
+ padding: 1px 5px;
331
+ border-radius: 0 0 3px 0;
332
+ background: color-mix(in srgb, #808080 78%, transparent);
333
+ color: #fff;
334
+ font: 700 9px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
335
+ letter-spacing: 0.08em;
336
+ pointer-events: none;
337
+ }
338
+ dialog[data-jx-dialog-open] {
339
+ position: relative !important;
340
+ inset: auto !important;
341
+ align-self: start !important;
342
+ flex: none !important;
343
+ outline: 1px dashed color-mix(in srgb, #808080 55%, transparent);
344
+ outline-offset: 2px;
345
+ }
346
+ dialog[data-jx-dialog-open]::before {
347
+ content: "DIALOG \\00B7 SHOWN IN PLACE";
348
+ position: absolute;
349
+ top: 0;
350
+ left: 0;
351
+ z-index: 2;
352
+ padding: 1px 5px;
353
+ border-radius: 0 0 3px 0;
354
+ background: color-mix(in srgb, #808080 78%, transparent);
355
+ color: #fff;
356
+ font: 700 9px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
357
+ letter-spacing: 0.08em;
358
+ pointer-events: none;
359
+ }
360
+ `;
361
+
362
+ /**
363
+ * Keep the overlay stylesheet in sync with the render mode: present (idempotently) wherever the
364
+ * render de-links its overlays, removed for preview — which keeps the real `popover`/`open`
365
+ * attributes, so the real UA rules and the real top layer apply there.
366
+ */
367
+ export function syncCanvasOverlayCss(doc: Document, mode: CanvasMode): void {
368
+ const existing = doc.head.querySelector(`#${CANVAS_OVERLAY_STYLE_ID}`);
369
+ if (mode === "preview") {
370
+ existing?.remove();
371
+ return;
372
+ }
373
+ if (existing) {
374
+ return;
375
+ }
376
+ const style = doc.createElement("style");
377
+ style.id = CANVAS_OVERLAY_STYLE_ID;
378
+ style.textContent = CANVAS_OVERLAY_CSS;
379
+ doc.head.append(style);
380
+ }
381
+
382
+ /** Id of the injected git-diff change-mark stylesheet. */
383
+ export const DIFF_MARK_STYLE_ID = "jx-canvas-diff-css";
384
+
385
+ /**
386
+ * Change marks for the diff artboards.
387
+ *
388
+ * **Colour is never the only encoding.** Each kind carries a distinct `border-left-style` and a
389
+ * distinct gutter glyph, so the three states stay apart for a reader with a red/green deficiency in
390
+ * the ordinary render, not only under forced colours. That is also what makes the forced-colours
391
+ * block below cheap: it drops the wash and keeps style and glyph, which were already carrying the
392
+ * meaning.
393
+ *
394
+ * **The forced-colours block has to live HERE, in the frame.** `styles/forced-colors.css` is chrome
395
+ * and never reaches this document. The artboard's own opt-out (`forced-color-adjust: none` on
396
+ * `iframe.canvas-iframe`) is about not repainting the AUTHOR'S palette, which is why these rules
397
+ * add editor chrome through an attribute selector rather than restyling content.
398
+ *
399
+ * Hexes rather than custom properties for the reason `EDIT_PLACEHOLDER_CSS` gives: this is a
400
+ * separate document where the parent's tokens do not exist. The pair is chosen against the white
401
+ * artboard (`.canvas-panel-viewport` pins `background: white; color-scheme: light` whatever the
402
+ * chrome theme is), so the dark chrome tints `--success`/`--danger` carry would be illegible here.
403
+ */
404
+ export const DIFF_MARK_CSS = `
405
+ [data-jx-diff] {
406
+ position: relative;
407
+ border-left-width: 3px;
408
+ border-left-color: currentColor;
409
+ }
410
+ [data-jx-diff]::before {
411
+ position: absolute;
412
+ top: 0;
413
+ left: -3px;
414
+ width: 3px;
415
+ content: "";
416
+ }
417
+ [data-jx-diff="added"] {
418
+ border-left-style: solid;
419
+ border-left-color: #0a7c42;
420
+ background: color-mix(in srgb, #0a7c42 12%, transparent);
421
+ }
422
+ [data-jx-diff="removed"] {
423
+ border-left-style: double;
424
+ border-left-color: #c9252d;
425
+ background: color-mix(in srgb, #c9252d 12%, transparent);
426
+ }
427
+ [data-jx-diff="modified-before"] {
428
+ border-left-style: dashed;
429
+ border-left-color: #c9252d;
430
+ background: color-mix(in srgb, #c9252d 8%, transparent);
431
+ }
432
+ [data-jx-diff="modified-after"] {
433
+ border-left-style: dashed;
434
+ border-left-color: #0a7c42;
435
+ background: color-mix(in srgb, #0a7c42 8%, transparent);
436
+ }
437
+ [data-jx-diff-within] {
438
+ border-left: 3px dotted color-mix(in srgb, #808080 60%, transparent);
439
+ }
440
+ @media (forced-colors: active) {
441
+ [data-jx-diff],
442
+ [data-jx-diff-within] {
443
+ background: none;
444
+ border-left-color: CanvasText;
445
+ }
446
+ }
447
+ `;
448
+
449
+ /**
450
+ * Keep the change-mark stylesheet in sync with whether this render carries marks.
451
+ *
452
+ * Gated on the MARKS, not on the mode. The decoration should depend on the payload the decoration
453
+ * needs: a git-diff artboard whose comparison is still loading has no marks to show, and a mode
454
+ * check would leave a stale sheet behind it.
455
+ */
456
+ export function syncDiffCss(doc: Document, on: boolean): void {
457
+ const existing = doc.head.querySelector(`#${DIFF_MARK_STYLE_ID}`);
458
+ if (!on) {
459
+ existing?.remove();
460
+ return;
461
+ }
462
+ if (existing) {
463
+ return;
464
+ }
465
+ const style = doc.createElement("style");
466
+ style.id = DIFF_MARK_STYLE_ID;
467
+ style.textContent = DIFF_MARK_CSS;
468
+ doc.head.append(style);
469
+ }
470
+
471
+ /**
472
+ * Stamp this artboard's change marks onto the rendered tree.
473
+ *
474
+ * Replaces the whole set: every previous `data-jx-diff` comes off first, so a render carrying no
475
+ * marks clears the last one's rather than layering on it.
476
+ *
477
+ * **An unresolvable mark climbs to the nearest stamped ancestor** and lands there as
478
+ * `data-jx-diff-within` — "something inside here changed that cannot be drawn here". Not every
479
+ * document path reaches an element: a component's internals are created by its own
480
+ * `connectedCallback` and never pass through {@link makeStamper}, and only the first expanded row
481
+ * of a repeater carries the template's collapsed path. Dropping those marks silently would make the
482
+ * artboard disagree with a count the header states out loud; climbing keeps the change locatable
483
+ * and honest about its resolution.
484
+ */
485
+ export function applyDiffMarks(container: HTMLElement, marks: WireDiffMarks | null): void {
486
+ for (const stale of container.querySelectorAll("[data-jx-diff], [data-jx-diff-within]")) {
487
+ if (stale instanceof HTMLElement) {
488
+ delete stale.dataset.jxDiff;
489
+ delete stale.dataset.jxDiffWithin;
490
+ }
491
+ }
492
+ if (!marks?.length) {
493
+ return;
494
+ }
495
+ for (const mark of marks) {
496
+ const exact = elementForPath(container, mark.path);
497
+ if (exact) {
498
+ exact.dataset.jxDiff = mark.kind;
499
+ continue;
500
+ }
501
+ /* Climb by whole `["children", i]` hops: a path's segments come in that pairing, so dropping
502
+ one at a time would ask for `[..., "children"]`, which is never an address. */
503
+ let { path } = mark;
504
+ while (path.length >= 2) {
505
+ path = path.slice(0, -2);
506
+ const ancestor = elementForPath(container, path);
507
+ if (ancestor) {
508
+ ancestor.dataset.jxDiffWithin = "";
509
+ break;
510
+ }
511
+ }
512
+ }
513
+ }
514
+
239
515
  /**
240
516
  * Flag the canvas document as a preview shell, so `canvas.html`'s preview rules apply.
241
517
  *
@@ -454,6 +730,71 @@ export function applyPreviewColorScheme(doc: Document, scheme: "light" | "dark"
454
730
  }
455
731
  }
456
732
 
733
+ /**
734
+ * Draw the popover at `path` open, and every other one closed.
735
+ *
736
+ * The canvas-only twin of `showPopover()` — one attribute, no top layer, no light dismiss, and
737
+ * idempotent, so the parent may post the same value as often as it likes. Re-applied after every
738
+ * render AND after every patch: an `attributes` op routes through `replaceSubtree`, which rebuilds
739
+ * the element and takes the attribute with it.
740
+ *
741
+ * A path naming a node that is not a de-popovered panel opens nothing rather than throwing — the
742
+ * frame's copy of a value the host may have computed against a document it has since changed.
743
+ *
744
+ * @param root The render container (`#jx-canvas-root`).
745
+ * @param path Serialized document path of the popover to open, or null to close them all.
746
+ * @docs studio/interface/canvas
747
+ */
748
+ export function applyCanvasPopoverOpen(root: ParentNode, path: string | null): void {
749
+ for (const el of root.querySelectorAll("[data-jx-popover-open]")) {
750
+ delete (el as HTMLElement).dataset.jxPopoverOpen;
751
+ }
752
+ if (path === null) {
753
+ return;
754
+ }
755
+ const target = root.querySelector(`[data-jx-popover]${jxPathSelector(path)}`);
756
+ if (target) {
757
+ (target as HTMLElement).dataset.jxPopoverOpen = "";
758
+ }
759
+ }
760
+
761
+ /**
762
+ * Mark the one `<dialog>` the canvas draws open — the dialog twin of
763
+ * {@link applyCanvasPopoverOpen}.
764
+ *
765
+ * @param root The render container (`#jx-canvas-root`).
766
+ * @param path Serialized document path of the dialog to open, or null to close them all.
767
+ * @docs studio/interface/canvas
768
+ */
769
+ /*
770
+ * The dialog rules in the sheet above, explained here rather than inside the template literal,
771
+ * because a comment inside the string ships in iframe-entry.js.
772
+ *
773
+ * `dialog[data-jx-dialog-open] { display: block }` in the `jx-canvas-ua` layer is the dialog's
774
+ * other half: its authored `open` is renamed away, so the browser's own
775
+ * `dialog:not([open]) { display: none }` keeps every dialog closed, and this is the one rule that
776
+ * shows the dialog the canvas opened. Layered for the reason the popover rule gives: an author
777
+ * `display` on the base rule beats it, and that defect (@jxsuite/schema/dialogs' base-display) has
778
+ * to show on the canvas rather than hide behind a stronger rule.
779
+ *
780
+ * The forced `position` block is the popover's, by the same move: the UA's absolute positioning
781
+ * and modal top layer are gone with the renamed attributes, so the open dialog lays out in normal
782
+ * flow at its document position and the artboard grows to fit it. Preview renders it modally,
783
+ * backdrop and all.
784
+ */
785
+ export function applyCanvasDialogOpen(root: ParentNode, path: string | null): void {
786
+ for (const el of root.querySelectorAll("[data-jx-dialog-open]")) {
787
+ delete (el as HTMLElement).dataset.jxDialogOpen;
788
+ }
789
+ if (path === null) {
790
+ return;
791
+ }
792
+ const target = root.querySelector(`dialog${jxPathSelector(path)}`);
793
+ if (target) {
794
+ (target as HTMLElement).dataset.jxDialogOpen = "";
795
+ }
796
+ }
797
+
457
798
  /** Inject the document's `$head` (link/meta/script) into the iframe's <head>, de-duped by href/src. */
458
799
  export function injectHead(doc: JxDocument, assets: AssetContext | null = null): void {
459
800
  const head = (doc as { $head?: HeadEntry[] }).$head;
@@ -500,7 +841,8 @@ export function injectHead(doc: JxDocument, assets: AssetContext | null = null):
500
841
 
501
842
  /**
502
843
  * Build the `onNodeCreated` hook that stamps `data-jx-path` (page content) or `data-jx-layout-path`
503
- * + `data-jx-layout-file` (layout-originated nodes) on rendered nodes.
844
+ *
845
+ * - `data-jx-layout-file` (layout-originated nodes) on rendered nodes.
504
846
  */
505
847
  export function makeStamper(ctx: PathMapCtx) {
506
848
  /*
@@ -650,6 +992,12 @@ export async function renderResolvedDocument(opts: {
650
992
  assets?: AssetContext | null;
651
993
  /** This render may fetch automatic `Request` entries even outside preview (Data-panel Refresh). */
652
994
  allowAutoRequests?: boolean;
995
+ /** Change marks for a git-diff artboard, in this side's own document coordinates. */
996
+ diffMarks?: WireDiffMarks | null;
997
+ /** Serialized path of the popover to draw open, or null/absent for none. */
998
+ popoverOpen?: string | null;
999
+ /** The dialog to draw open after this render, serialized; the twin of `popoverOpen`. */
1000
+ dialogOpen?: string | null;
653
1001
  }): Promise<RenderHandle> {
654
1002
  /* FIRST, before anything emits CSS or an attribute. The resolver is module-global in the runtime,
655
1003
  so it is set on every render — including to null — or a previous document's context would
@@ -672,6 +1020,15 @@ export async function renderResolvedDocument(opts: {
672
1020
  // De-link `<a href>` in design/edit so clicks select the anchor instead of navigating the iframe;
673
1021
  // Preview keeps real links live (mirrors the server-function gate above).
674
1022
  setCanvasDelinkAnchors(opts.mode !== "preview");
1023
+ /* De-popover in the same modes and for the same reason: an OPEN popover is in the top layer,
1024
+ whose containing block is the viewport — a fiction here, since the frame is sized to its own
1025
+ content — and which contributes to no ancestor's scrollable overflow, so the artboard could
1026
+ never grow to fit one. Preview keeps the real top layer, backdrop and all. */
1027
+ setCanvasDelinkPopovers(opts.mode !== "preview");
1028
+ /* And de-link invoker commands, `inert` and a dialog's `open` with them: a `show-modal` invoker
1029
+ would put its dialog in the top layer and make the rest of the page inert, which is the
1030
+ popover problem plus an unclickable document. The frame reports the click instead. */
1031
+ setCanvasDelinkCommands(opts.mode !== "preview");
675
1032
  // Stamp `data-jx-bound-prop` on component-internal invertible text bindings in design/edit only —
676
1033
  // The inline prop-edit affordance. Set every render so a preview/stylebook render in the same
677
1034
  // Iframe clears it (page-level templates are inert in design/edit via prepareForEditMode, so only
@@ -680,8 +1037,14 @@ export async function renderResolvedDocument(opts: {
680
1037
  applySiteStyle(opts.siteStyle, (opts.doc as { $media?: Record<string, string> }).$media ?? {});
681
1038
  injectHead(opts.doc, assets);
682
1039
  syncEditModeCss(opts.container.ownerDocument, opts.mode);
1040
+ // The substitute rules for the attributes the de-link above renamed. Same predicate as the
1041
+ // De-link, not the narrower design/edit one: a Stylebook specimen and a git-diff side rename
1042
+ // Their overlays too, and without this sheet a closed panel would lay out in flow and an open
1043
+ // Dialog could never be shown.
1044
+ syncCanvasOverlayCss(opts.container.ownerDocument, opts.mode);
683
1045
  syncPreviewShell(opts.container.ownerDocument, opts.mode);
684
1046
  syncStylebookCss(opts.container.ownerDocument, opts.mode);
1047
+ syncDiffCss(opts.container.ownerDocument, Boolean(opts.diffMarks?.length));
685
1048
  // Seed the runtime's root $media before buildScope so a COMPONENT with its own `@--name` blocks
686
1049
  // But no own `$media` resolves the breakpoint to its real query (the iframe path calls buildScope
687
1050
  // Directly and never the runtime's `Jx()` entry, which is the only other place _rootMedia is set).
@@ -704,9 +1067,14 @@ export async function renderResolvedDocument(opts: {
704
1067
  () => renderNode(opts.doc, $defs, { _path: [], onNodeCreated }) as HTMLElement,
705
1068
  );
706
1069
  opts.container.replaceChildren(el);
1070
+ applyCanvasPopoverOpen(opts.container, opts.popoverOpen ?? null);
1071
+ applyCanvasDialogOpen(opts.container, opts.dialogOpen ?? null);
707
1072
  // Claim (or release) the editing host AFTER the tree lands, so the browser computes editability
708
1073
  // Against the final DOM rather than an empty container.
709
1074
  syncEditableRoot(opts.container, opts.mode);
1075
+ // Marks last, and for the same reason: they are resolved by querying stamped attributes, which
1076
+ // Only exist once the tree the stamper walked is actually in the container.
1077
+ applyDiffMarks(opts.container, opts.diffMarks ?? null);
710
1078
  return {
711
1079
  ctx: { defs: $defs, docBase: opts.docBase, mapperCtx: opts.mapperCtx, mode: opts.mode },
712
1080
  dispose: stop,
@@ -1,8 +1,8 @@
1
1
  /// <reference lib="dom" />
2
2
  /**
3
3
  * Iframe-realm slash-menu bridge — the inline-edit engine detects "/" and drives a DI'd
4
- * {@link SlashController}; the real lit/Spectrum menu can't render in the slim iframe bundle, so
5
- * this controller proxies it across the postMessage bridge. It posts `slashShow` (with the edited
4
+ * {@link SlashController}; the real host-realm menu can't render in the slim iframe bundle, so this
5
+ * controller proxies it across the postMessage bridge. It posts `slashShow` (with the edited
6
6
  * element's IFRAME-VIEWPORT rect + the typed filter) / `slashDismiss`; the host shows the parent
7
7
  * menu and posts back `slashSelect` / `slashDismissed`.
8
8
  *
@@ -11,7 +11,7 @@
11
11
  * WeakMap, this stamps `data-jx-path` and tracks per-subtree effect scopes locally.
12
12
  */
13
13
 
14
- import { elementStyleTags, renderNode, runScoped } from "@jxsuite/runtime";
14
+ import { releaseElementStyles, renderNode, runScoped } from "@jxsuite/runtime";
15
15
  import { getNodeAtPath } from "../state";
16
16
  import { makeStamper } from "./iframe-render";
17
17
  import { prepareForEditMode } from "../utils/edit-display";
@@ -60,8 +60,9 @@ export function renderSubtreeIframe(
60
60
  }
61
61
 
62
62
  /**
63
- * Release a removed/replaced subtree: stop the effect scopes rooted inside it and drop the scoped
64
- * `<style>` tags the runtime emitted for its elements (otherwise they orphan in the iframe head).
63
+ * Release a removed/replaced subtree: stop the effect scopes rooted inside it and hand every
64
+ * element's rule set back to the runtime's stylesheet engine (otherwise its rules orphan in the
65
+ * iframe's adopted sheet).
65
66
  */
66
67
  export function disposeSubtree(el: Element): void {
67
68
  const targets: Element[] = [el, ...el.querySelectorAll("*")];
@@ -69,11 +70,7 @@ export function disposeSubtree(el: Element): void {
69
70
  if (!(t instanceof HTMLElement)) {
70
71
  continue;
71
72
  }
72
- const tag = elementStyleTags.get(t);
73
- if (tag) {
74
- tag.remove();
75
- elementStyleTags.delete(t);
76
- }
73
+ releaseElementStyles(t);
77
74
  const stop = elScope.get(t);
78
75
  if (stop) {
79
76
  stop();
@@ -13,6 +13,23 @@
13
13
 
14
14
  import type { JxPath } from "../state";
15
15
 
16
+ /**
17
+ * A `[data-jx-path='…']` selector for a serialized path.
18
+ *
19
+ * Single quotes because the serialized path is JSON and only ever uses double ones; the two escapes
20
+ * cover the characters that could still break out of an attribute-value selector. Shared because
21
+ * two callers need the same string — `measureHits` locating a node to measure, and
22
+ * `applyCanvasPopoverOpen` locating one to flip — and a second copy of an escaping rule is a second
23
+ * chance to get an escape wrong.
24
+ *
25
+ * @param serialized A path already through {@link serializeJxPath}.
26
+ * @returns The attribute selector, without a leading tag or combinator.
27
+ */
28
+ export function jxPathSelector(serialized: string): string {
29
+ const escaped = serialized.replaceAll("\\", String.raw`\\`).replaceAll("'", String.raw`\'`);
30
+ return `[data-jx-path='${escaped}']`;
31
+ }
32
+
16
33
  /** Context the mapper needs, computed once per render by whoever prepared the document. */
17
34
  export interface PathMapCtx {
18
35
  canvasMode: string;
@@ -102,9 +119,14 @@ export function classifyRenderNode(path: JxPath, def: unknown, ctx: PathMapCtx):
102
119
  }
103
120
  }
104
121
 
105
- // Collapse repeater-perimeter template hops: for an array document path P, a render path of
106
- // `[...P, "children", 0, ...rest]` maps to `[...P, "map", ...rest]`. Loop for nested repeaters.
107
- if ((ctx.canvasMode === "design" || ctx.canvasMode === "edit") && ctx.arrayPaths.size > 0) {
122
+ /* Collapse repeater-perimeter template hops: for an array document path P, a render path of
123
+ `[...P, "children", 0, ...rest]` maps to `[...P, "map", ...rest]`. Loop for nested repeaters.
124
+
125
+ `arrayPaths` IS the mode test, and naming modes here as well was a second copy of it that
126
+ disagreed. Only the caller knows whether `prepareForEditMode` ran, and it says so by filling
127
+ this set; a non-empty set in a mode this list did not name meant the perimeters existed and the
128
+ collapse did not, so the stamped path pointed at a node the document has no address for. */
129
+ if (ctx.arrayPaths.size > 0) {
108
130
  let changed = true;
109
131
  while (changed) {
110
132
  changed = false;
@@ -0,0 +1,106 @@
1
+ /// <reference lib="dom" />
2
+ /**
3
+ * Which popover a document path belongs to — the pure half of the open-on-selection rule.
4
+ *
5
+ * Separate from `popover-state.ts` because of who needs it. The command record in `canvas-utils.ts`
6
+ * has to answer "is there a popover here?" for its `enablement`, and `popover-state.ts` reaches
7
+ * `iframe-host.ts`, which reaches `canvas-utils.ts` — so importing the whole rule from the command
8
+ * file would close a cycle. Nothing here touches a frame, a channel or the DOM, which is also what
9
+ * makes it the easy half to test.
10
+ *
11
+ * @docs studio/interface/canvas
12
+ */
13
+
14
+ import { activeTab } from "../workspace/workspace";
15
+ import { getNodeAtPath } from "../state";
16
+ import { documentHasPopover, isPopover } from "@jxsuite/schema/overlays";
17
+ import { INVOKER_TAGS, POPOVER_TAGS } from "@jxsuite/ui";
18
+ import { primarySelection } from "../tabs/selection";
19
+ import type { JxMutableNode } from "@jxsuite/schema/types";
20
+ import type { JxPath } from "../state";
21
+ import type { Tab } from "../tabs/tab";
22
+
23
+ /**
24
+ * The nearest ancestor-or-self of `path` that declares `popover`, or null when there is none.
25
+ *
26
+ * Walks prefixes of the path rather than the tree, so it costs one `getNodeAtPath` per level and
27
+ * needs no parent pointers. A prefix that lands on a non-node (the `"children"` segment itself, an
28
+ * index into a repeater's `items`) simply is not a popover, so nothing here has to enumerate the
29
+ * segment vocabulary — which is what keeps it correct across `map` and `cases` hops for free.
30
+ *
31
+ * @param doc The document the path addresses.
32
+ * @param path The selected node's path.
33
+ * @returns The popover's path, or null.
34
+ */
35
+ /**
36
+ * The kit's overlay elements, so one of them on the canvas counts as the popover it is. Their
37
+ * `popover` attribute lives in the DEFINITION, which the edited document never carries, so without
38
+ * this an author who drops one on a page could not open it on the canvas at all.
39
+ */
40
+ const KIT_SCOPE = { invokerTags: INVOKER_TAGS, popoverTags: POPOVER_TAGS };
41
+
42
+ export function ancestorPopoverPath(
43
+ doc: JxMutableNode | null | undefined,
44
+ path: JxPath | null | undefined,
45
+ ): JxPath | null {
46
+ if (!doc || !path) {
47
+ return null;
48
+ }
49
+ for (let end = path.length; end >= 0; end -= 1) {
50
+ const prefix = path.slice(0, end);
51
+ const node = getNodeAtPath(doc, prefix);
52
+ if (node && typeof node === "object" && isPopover(node, KIT_SCOPE)) {
53
+ return prefix;
54
+ }
55
+ }
56
+ return null;
57
+ }
58
+
59
+ /**
60
+ * The popover a `canvas.setPopoverOpen` call means for `tab`, or null when there is none.
61
+ *
62
+ * An explicit `path` is taken at its word but still CHECKED — the palette, the assistant and the
63
+ * automation runner all reach the command, and a path that is not a popover must be refused rather
64
+ * than silently opening nothing. With no argument the answer is the popover the SELECTION is at or
65
+ * inside, which is what makes the command usable from a keystroke with nothing else stated.
66
+ *
67
+ * Takes the tab rather than reading the active one, so the command's `pane` argument decides which
68
+ * document is consulted: a split view can have a different popover open in each pane's tab.
69
+ *
70
+ * @param tab The tab to resolve against.
71
+ * @param explicit A path supplied by the caller, if any.
72
+ * @returns The popover's path, or null.
73
+ */
74
+ export function popoverPathFor(tab: Tab, explicit?: JxPath): JxPath | null {
75
+ const doc = tab.doc.document as JxMutableNode | undefined;
76
+ if (!doc) {
77
+ return null;
78
+ }
79
+ if (explicit) {
80
+ const node = getNodeAtPath(doc, explicit);
81
+ return node && typeof node === "object" && isPopover(node, KIT_SCOPE) ? explicit : null;
82
+ }
83
+ return ancestorPopoverPath(doc, primarySelection(tab.session.selection));
84
+ }
85
+
86
+ /**
87
+ * Whether the focused document has any popover — what the command's `enablement` asks.
88
+ *
89
+ * A fact about the DOCUMENT, not about the selection. Gating on the selection looked tighter and
90
+ * was wrong: the palette, the assistant and a screenshot step all name a popover by path, and an
91
+ * enablement predicate cannot see the arguments, so it would have refused every one of them before
92
+ * the path was read. The specific refusal — "that path is not a popover" — belongs to the run,
93
+ * where the argument exists.
94
+ *
95
+ * Reading the focused tab is correct here and only here: enablement describes what would happen if
96
+ * you pressed Enter now, and that is the pane you are in. The command's `run` resolves its tab
97
+ * through `contextTab` instead, so a `pane` argument still decides the write. That split is why
98
+ * this lives here rather than in `canvas-utils.ts`, whose stage-geometry functions must never read
99
+ * the focused tab (`check-pane-singletons.ts` enforces it).
100
+ *
101
+ * @returns True when the focused document declares at least one popover.
102
+ */
103
+ export function activeDocumentHasPopover(): boolean {
104
+ const doc = activeTab.value?.doc.document as JxMutableNode | undefined;
105
+ return doc ? documentHasPopover(doc, KIT_SCOPE) : false;
106
+ }