@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
@@ -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
+ }