@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
@@ -28,23 +28,46 @@
28
28
  *
29
29
  * **The card has no host of its own.** `#frontmatter-panel` — the grid row, the `hidden` div, the
30
30
  * `frontmatterPanelEl` ref and the 40vh cap that came with them — is deleted. The STAGE renders the
31
- * host now ({@link attachDocumentHeaderHost}, called from a `ref` in `canvas/canvas-render.ts`), so
32
- * the card sits inside the artefact rather than in a band above it. This module keeps only the
33
- * focus-aware scheduler and the reactive effect, because a field commit must still repaint the card
34
- * WITHOUT repainting the canvas — a full canvas render remounts the document iframe.
31
+ * host now ({@link attachDocumentHeaderHost}, called from the stage document's `onNodeCreated` in
32
+ * `canvas/canvas-render.ts`), so the card sits inside the artefact rather than in a band above it.
33
+ *
34
+ * **The card is a Jx document now** (`src/surfaces/doc-header.json`), and this module is the flow
35
+ * behind it: it decides which document a pane's card is drawn for, which rows that document has,
36
+ * what each row commits into and where a media or reference row gets its choices, then hands the
37
+ * surface a projection whose every field is already a string. Three things follow.
38
+ *
39
+ * **The focus-aware scheduler is gone, and nothing replaced it.** `panels/panel-scheduler.ts`
40
+ * existed to withhold a repaint while a text input in the card had focus, because a lit re-render
41
+ * rebuilt every control and truncated whatever was being typed into one. A document binds each
42
+ * value on its own runtime effect and skips a write equal to what the control already holds
43
+ * (specs/studio-ui-guidelines.md §9.3), so a projection that changed one field repaints one field
44
+ * and a projection that changed nothing repaints nothing. There is no window left to withhold.
45
+ *
46
+ * **ONE row vocabulary.** Title, the Layout picker and every schema-driven frontmatter field are
47
+ * the same row, told apart by which control they draw; what makes them different is what they
48
+ * WRITE, and that is this module's business rather than the document's. {@link RowCommit} is that
49
+ * table, rebuilt with each projection and reached by the row's `key` — which is why a row's key and
50
+ * its `data-prop` are two different strings: a collection whose schema declares a `layout` property
51
+ * would otherwise collide with the Layout picker.
52
+ *
53
+ * **Two widgets are borrowed as BEHAVIOUR rather than as markup.** The media browser
54
+ * (`ui/media-picker.ts`) renders into the popover layer rather than into the field, and the entry
55
+ * ids behind a `#/content/<type>` reference are a read (`ui/form-controls.ts`), so the card draws
56
+ * its own control for each and calls the owner for the part that is not markup. Neither module's
57
+ * lit template is interpolated here — a document and a lit template cannot share a container, and
58
+ * §9.4 forbids the mix outright.
59
+ *
60
+ * @docs studio/editing/frontmatter
35
61
  */
36
62
 
37
- import { html, nothing, render as litRender } from "lit-html";
38
63
  import { projectState } from "../store";
39
64
  import { workspace } from "../workspace/workspace";
40
65
  import { tabOfPane } from "../canvas/canvas-surface";
41
66
  import { paneRegion } from "../ui/regions";
42
67
  import { effect, effectScope } from "../reactivity";
43
- import { createPanelScheduler } from "./panel-scheduler";
44
- import { collectFmFields, renderFmField } from "./frontmatter-fields";
45
- import { renderFieldRow } from "../ui/field-row";
46
- import { spTextField } from "../ui/field-input";
47
- import { transact } from "../tabs/transact";
68
+ import { collectFmFields, projectFmField } from "./frontmatter-fields";
69
+ import { LIVE_PREVIEW } from "../ui/timing";
70
+ import { mutateUpdateFrontmatter, transact, transactDoc } from "../tabs/transact";
48
71
  import { pageRoute } from "./tab-strip";
49
72
  import {
50
73
  RESERVED_FM_KEYS,
@@ -54,25 +77,58 @@ import {
54
77
  entryValue,
55
78
  isPageDocument,
56
79
  isManagedEntry,
57
- renderLayoutPickerRow,
80
+ layoutPickerEntries,
58
81
  } from "./head-panel";
59
82
  import { isGoogleFontEntry, isGoogleFontPreconnect } from "../utils/google-fonts";
60
83
  import { activeRegistry } from "../commands/active-registry";
84
+ import { invalidateLayoutCache } from "../site-context";
85
+ import { mountDocHeaderSurface } from "../surfaces/doc-header";
61
86
 
62
87
  import type { JxHeadEntry, JxMutableNode } from "@jxsuite/schema/types";
63
88
  import type { Tab } from "../tabs/tab";
64
- import type { PanelScheduler } from "./panel-scheduler";
65
- import type { TemplateResult } from "lit-html";
89
+ import type { FmSchemaEntry } from "./frontmatter-fields";
90
+ import type * as MediaPickerModule from "../ui/media-picker";
91
+ import type {
92
+ DocHeaderActions,
93
+ DocHeaderChoice,
94
+ DocHeaderRawEntry,
95
+ DocHeaderRow,
96
+ DocHeaderSurface,
97
+ DocHeaderView,
98
+ } from "../surfaces/doc-header";
99
+
100
+ /** What one row of the card writes. The document knows the control; this knows the document. */
101
+ interface RowCommit {
102
+ /** Remove the value this row shows. */
103
+ clear: () => void;
104
+ /** Commit what the control settled on — a string from every kind but the checkbox. */
105
+ commit: (raw: string | boolean) => void;
106
+ }
66
107
 
67
108
  /**
68
- * The card's host and scheduler, per PANE.
109
+ * One pane's card: the node the stage gave it, the mounted surface, its commit table and the
110
+ * actions the surface was handed.
69
111
  *
70
112
  * A single `_host` was the same singleton every other stage-content module had, and it produced a
71
113
  * subtler failure than most: the stage hands the host over from a lit `ref`, so with two stages
72
114
  * drawing a header the second `ref` to fire silently took the card away from the first, leaving one
73
115
  * pane's Document Header frozen on the frontmatter of the moment it lost the handle.
74
116
  */
75
- const _hosts = new Map<string, { el: HTMLElement; scheduler: PanelScheduler }>();
117
+ interface HeaderCard {
118
+ /** The pane this card is drawn for. Held rather than looked up: an action has to answer it. */
119
+ paneId: string;
120
+ el: HTMLElement;
121
+ /** `null` until the pane's document first has a header to draw. */
122
+ surface: DocHeaderSurface | null;
123
+ /** This pane's rows, by key. Rebuilt with each projection. */
124
+ commits: Map<string, RowCommit>;
125
+ /** Handed to the surface once, when it is mounted; every one of them reads `commits`. */
126
+ actions: DocHeaderActions;
127
+ /** Text edits waiting out {@link LIVE_PREVIEW}, one per row key. */
128
+ pending: Map<string, { commit: () => void; timer: ReturnType<typeof setTimeout> }>;
129
+ }
130
+
131
+ const _hosts = new Map<string, HeaderCard>();
76
132
  let _scope: { stop: () => void; run: <T>(fn: () => T) => T | undefined } | null = null;
77
133
 
78
134
  /** Per-document disclosure state, keyed by tab id — not stored on the document. */
@@ -82,9 +138,7 @@ const _rawOpen = new Set<string>();
82
138
  * The element the stage has made available for the card, or `null` while no stage hosts it.
83
139
  *
84
140
  * Called from a lit `ref` in `canvas/canvas-render.ts`, so the host's lifetime is the stage's: the
85
- * canvas creates it when it draws a document that has a header and drops it otherwise. The
86
- * scheduler is re-created per host because it binds `focusin`/`focusout` to that exact node, and
87
- * the focus guard is the whole reason this module still owns a render path of its own.
141
+ * canvas creates it when it draws a document that has a header and drops it otherwise.
88
142
  *
89
143
  * @param {HTMLElement | null} el
90
144
  */
@@ -93,24 +147,33 @@ export function attachDocumentHeaderHost(paneId: string, el: HTMLElement | null)
93
147
  if (held?.el === el) {
94
148
  return;
95
149
  }
96
- held?.scheduler.unbind();
97
- _hosts.delete(paneId);
150
+ if (held) {
151
+ takeDown(held);
152
+ _hosts.delete(paneId);
153
+ }
98
154
  if (!el) {
99
155
  return;
100
156
  }
101
- const scheduler = createPanelScheduler({ render: () => _doRender(paneId), root: el });
102
- _hosts.set(paneId, { el, scheduler });
103
- scheduler.bindFocus();
104
- scheduler.schedule();
157
+ const card: HeaderCard = {
158
+ actions: {} as DocHeaderActions,
159
+ commits: new Map(),
160
+ el,
161
+ paneId,
162
+ pending: new Map(),
163
+ surface: null,
164
+ };
165
+ card.actions = makeActions(card);
166
+ _hosts.set(paneId, card);
167
+ paint(paneId, card);
105
168
  }
106
169
 
107
- /**
108
- * The host the stage last handed over, or `null`. The stage reads it back to settle Lit's
109
- * order-independent detach report; nothing else needs to know where the card lives.
110
- */
111
- export function documentHeaderHost(paneId: string): HTMLElement | null {
112
- return _hosts.get(paneId)?.el ?? null;
113
- }
170
+ /* There is no `documentHeaderHost` getter any more, and its absence is the point.
171
+ It existed for ONE caller: the stage's lit `ref`, which was told about a removal WITHOUT being
172
+ told which node had gone, so it had to read the host back and ask whether it was still connected
173
+ before deciding the report was about the host it held. `surfaces/canvas-stage.json` states both
174
+ placements as a view, so a frame that draws no card simply announces none and `renderCanvasImpl`
175
+ releases the slot in one place — there is nothing left to settle after the fact, and a getter
176
+ whose only reader is gone is a fact about the module nobody is entitled to. */
114
177
 
115
178
  /**
116
179
  * Mount the Document Header card: subscribe to the tab / document / frontmatter reads it renders
@@ -151,13 +214,24 @@ export function mount() {
151
214
  export function unmount() {
152
215
  _scope?.stop();
153
216
  _scope = null;
154
- for (const { scheduler } of _hosts.values()) {
155
- scheduler.unbind();
217
+ for (const card of _hosts.values()) {
218
+ takeDown(card);
156
219
  }
157
220
  _hosts.clear();
158
221
  _rawOpen.clear();
159
222
  }
160
223
 
224
+ /** Take one pane's card down: cancel its waiting edits and dispose the document it mounted. */
225
+ function takeDown(card: HeaderCard): void {
226
+ for (const { timer } of card.pending.values()) {
227
+ clearTimeout(timer);
228
+ }
229
+ card.pending.clear();
230
+ card.surface?.dispose();
231
+ card.surface = null;
232
+ card.commits.clear();
233
+ }
234
+
161
235
  /** Each pane's tab, deduplicated — the set of documents a header could be drawn for. */
162
236
  function paneTabs(): Tab[] {
163
237
  const tabs: Tab[] = [];
@@ -171,12 +245,15 @@ function paneTabs(): Tab[] {
171
245
  }
172
246
 
173
247
  /**
174
- * Request a render. Coalesced and deferred while a text input in the card is focused, so re-renders
175
- * never clobber a field mid-edit.
248
+ * Repaint every card.
249
+ *
250
+ * Coalescing is the runtime's now: a projection assigns to a standing scope and each binding
251
+ * re-runs only where the value it reads has changed, so calling this twice in one tick costs one
252
+ * comparison per binding rather than two renders.
176
253
  */
177
254
  export function render() {
178
- for (const { scheduler } of _hosts.values()) {
179
- scheduler.schedule();
255
+ for (const [paneId, card] of _hosts) {
256
+ paint(paneId, card);
180
257
  }
181
258
  }
182
259
 
@@ -207,204 +284,351 @@ export function hasDocumentHeader(tab: Tab): boolean {
207
284
  return isPageDocument(tab);
208
285
  }
209
286
 
210
- function _doRender(paneId: string) {
211
- const host = _hosts.get(paneId)?.el;
212
- if (!host) {
213
- return;
214
- }
287
+ /**
288
+ * Draw (or take down) one pane's card.
289
+ *
290
+ * The surface is only ever mounted ONCE per host and assigned to afterwards. Rebuilding it on a
291
+ * repaint would take a field out from under a reader mid-edit, which is the failure the scheduler
292
+ * this conversion deletes was built to prevent; `connected()` answers the one case assignment
293
+ * cannot, a stage that redrew and took the card's root away.
294
+ */
295
+ function paint(paneId: string, card: HeaderCard): void {
215
296
  const tab = tabOfPane(paneId);
216
297
  if (!tab || !hasDocumentHeader(tab)) {
217
- // The stage decides whether the host exists; this only covers the window between a document
218
- // Losing its header and the canvas noticing. Lit leaves comment markers, so `:empty` cannot be
219
- // The signal and the host is hidden explicitly.
220
- litRender(nothing, host);
221
- host.hidden = true;
298
+ /* The stage decides whether the host exists; this only covers the window between a document
299
+ losing its header and the canvas noticing. */
300
+ takeDown(card);
301
+ card.el.replaceChildren();
302
+ card.el.hidden = true;
303
+ return;
304
+ }
305
+ const view = viewFor(tab, paneId, card);
306
+ card.el.hidden = false;
307
+ if (card.surface?.connected()) {
308
+ card.surface.update(view);
222
309
  return;
223
310
  }
224
- litRender(documentHeaderTemplate(tab, paneId), host);
225
- host.hidden = false;
311
+ card.surface?.dispose();
312
+ card.surface = mountDocHeaderSurface(card.el, view, card.actions);
226
313
  }
227
314
 
315
+ // ─── The projection ──────────────────────────────────────────────────────────
316
+
228
317
  /**
229
- * The card.
318
+ * Everything the card says about one document, and the commit table behind it.
230
319
  *
231
- * Exported as a template because the STAGE hosts it: `canvas-render.ts` emits the host div and this
232
- * module paints into it. The region id (`pane.primary/frontmatter`) rides on the `<section>` rather
233
- * than on a shell host, which is what made the move a layout change instead of a rename — the id
234
- * names the card's PLACE in the pane, and the pane still has one.
320
+ * `tab`, not `activeTab` — the card is drawn for the pane that mounted it. Both branches of the
321
+ * commit path resolved through FOCUS once, so with two panes the visible card's controls edited
322
+ * whichever document happened to be focused: click "Clear title" on the card in the left pane and
323
+ * the field disappeared from the right pane's document instead.
235
324
  *
236
325
  * @param {Tab} tab
237
- * @returns {TemplateResult}
326
+ * @returns {DocHeaderView}
238
327
  */
239
- export function documentHeaderTemplate(tab: Tab, paneId: string): TemplateResult {
328
+ function viewFor(tab: Tab, paneId: string, card: HeaderCard): DocHeaderView {
240
329
  const isContent = tab.doc.mode === "content";
241
330
  const fm = (tab.doc.content?.frontmatter ?? {}) as Record<string, unknown>;
242
331
  // ONE view of the document's head material, whichever realm it lives in: frontmatter keys for a
243
332
  // Markdown page, root properties for a JSON one.
244
333
  const headDoc = isContent ? buildHeadDoc(tab.doc.document, fm) : tab.doc.document;
245
- /*
246
- * `tab`, not `activeTab` — the card is drawn for the pane that mounted it, and every other line
247
- * in this template already reads that. Both branches resolved through FOCUS, so with two panes
248
- * the visible card's controls edited whichever document happened to be focused: click "Clear
249
- * title" on the card in the left pane and the field disappears from the right pane's document
250
- * instead. Invisible with one stage, which is why it survived the pane keying.
251
- *
252
- * **The first fix reached the JSON branch only, and the comment here said it was done.** A
253
- * markdown page takes the CONTENT branch, where `applyContentMutation` resolved the focus for
254
- * itself one call deeper — and `renderFmField` below did the same at each of its seven widgets.
255
- * Both take their tab now, so the whole card commits into one document: the one it is showing.
256
- */
257
334
  const applyMutation = isContent
258
335
  ? (fn: (doc: JxMutableNode) => void) => applyContentMutation(tab, render, fn)
259
336
  : (fn: (doc: JxMutableNode) => void) => {
260
337
  transact(tab, fn);
261
338
  };
262
339
 
340
+ const commits = new Map<string, RowCommit>();
341
+ const rows: DocHeaderRow[] = [titleRow(headDoc, applyMutation, commits)];
342
+ const layout = layoutRow(tab, headDoc, applyMutation, commits);
343
+ if (layout) {
344
+ rows.push(layout);
345
+ }
346
+
263
347
  // ONE call. The old pair of surfaces made two, with two different reserved-key policies.
264
348
  const { collection, fields, requiredFields } = collectFmFields(
265
349
  tab,
266
350
  projectState?.projectConfig,
267
351
  RESERVED_FM_KEYS,
268
352
  );
353
+ for (const f of fields) {
354
+ rows.push(fieldRow(tab, f.field, f.entry, f.value, requiredFields, commits));
355
+ }
356
+ card.commits = commits;
269
357
 
270
358
  const route = tab.documentPath ? pageRoute(tab.documentPath) : null;
271
- const title = typeof headDoc.title === "string" ? headDoc.title : "";
272
- const head = headDoc.$head ?? [];
359
+ const raw = rawEntries(headDoc.$head ?? []);
360
+ return {
361
+ collection: collection ? collection.name : "Document",
362
+ hasRoute: route !== null,
363
+ /* The stage's two placements, read off the host it handed over. In Edit the card is a block of
364
+ the document's own column; in Design it is pinned above a pan/zoom surface at 1:1, and only
365
+ that one wants the band's edge rather than the card's. `surfaces/canvas-stage.json` says the
366
+ same thing from the STAGE's side, which a document's own scoped style block cannot answer —
367
+ and it says it on `data-placement` rather than on a class, because the stage emits none. */
368
+ placement: card.el.dataset["placement"] === "pinned" ? "pinned" : "in-column",
369
+ rawEntries: raw,
370
+ rawOpen: _rawOpen.has(tab.id),
371
+ rawState: raw.length === 0 ? "empty" : "list",
372
+ region: paneRegion(paneId, "frontmatter"),
373
+ route: route ?? "",
374
+ rows,
375
+ };
376
+ }
377
+
378
+ /** A blank row, so every kind carries every field the document's bindings read. */
379
+ function blankRow(key: string, prop: string, label: string): DocHeaderRow {
380
+ return {
381
+ checked: false,
382
+ clearLabel: `Clear ${prop}`,
383
+ hasNote: false,
384
+ hasThumb: false,
385
+ isSet: false,
386
+ key,
387
+ kind: "text",
388
+ label,
389
+ note: "",
390
+ options: [],
391
+ placeholder: "",
392
+ prop,
393
+ thumb: "",
394
+ value: "",
395
+ };
396
+ }
273
397
 
274
- return html`
275
- <section
276
- class="doc-header"
277
- aria-label="Document header"
278
- data-jx-region=${paneRegion(paneId, "frontmatter")}
279
- >
280
- <header class="doc-header-bar">
281
- <span class="doc-header-title">${collection ? collection.name : "Document"}</span>
282
- ${
283
- route === null
284
- ? nothing
285
- : html`<code class="doc-header-route" title="This page's route">${route}</code>`
398
+ /** The one head value you type while writing, in both realms. */
399
+ function titleRow(
400
+ headDoc: JxMutableNode,
401
+ applyMutation: (fn: (doc: JxMutableNode) => void) => void,
402
+ commits: Map<string, RowCommit>,
403
+ ): DocHeaderRow {
404
+ const title = typeof headDoc.title === "string" ? headDoc.title : "";
405
+ commits.set("title", {
406
+ clear: () =>
407
+ applyMutation((d) => {
408
+ delete d.title;
409
+ }),
410
+ commit: (raw) =>
411
+ applyMutation((d) => {
412
+ const val = String(raw).trim();
413
+ if (val) {
414
+ d.title = val;
415
+ } else {
416
+ delete d.title;
286
417
  }
287
- </header>
288
- <div class="doc-header-body">
289
- ${renderFieldRow({
290
- hasValue: Boolean(title),
291
- label: "Title",
292
- onClear: () =>
293
- applyMutation((d) => {
294
- delete d.title;
295
- }),
296
- prop: "title",
297
- widget: spTextField(
298
- "doc-header:title",
299
- title,
300
- (v: string) =>
301
- applyMutation((d) => {
302
- const val = v.trim();
303
- if (val) {
304
- d.title = val;
305
- } else {
306
- delete d.title;
307
- }
308
- }),
309
- { placeholder: "Untitled" },
310
- ),
311
- })}
312
- ${isPageDocument(tab) ? renderLayoutPickerRow(headDoc, applyMutation) : nothing}
313
- ${fields.map((f) => renderFmField(tab, f.field, f.entry, f.value, requiredFields))}
314
- </div>
315
- ${seoButtonRow()} ${disclosure(tab.id, _rawOpen, "Raw head tags", rawHeadBody(head))}
316
- </section>
317
- `;
418
+ }),
419
+ });
420
+ return {
421
+ ...blankRow("title", "title", "Title"),
422
+ clearLabel: "Clear title",
423
+ isSet: Boolean(title),
424
+ placeholder: "Untitled",
425
+ value: title,
426
+ };
318
427
  }
319
428
 
320
429
  /**
321
- * The door to Search appearance (`panels/seo-modal.ts`).
430
+ * The layout picker, or `null` when this document takes none — a component has no layout, and a
431
+ * page whose layouts directory is still being listed has nothing to offer yet.
322
432
  *
323
- * A row rather than a disclosure, because what is behind it is a surface and not more of this card.
324
- * It runs the COMMAND rather than calling `openSeoModal` — the Page panel offers the same door and
325
- * neither of them should own it.
433
+ * The listing is `head-panel.ts`'s, asked for as data: the Page panel draws the same choices from
434
+ * the same cache, so creating a layout invalidates one thing rather than two.
326
435
  */
327
- function seoButtonRow(): TemplateResult {
328
- return html`
329
- <div class="doc-header-seo">
330
- <sp-action-button
331
- quiet
332
- size="s"
333
- class="doc-header-seo-btn"
334
- @click=${() => {
335
- void activeRegistry()?.run("document.openSeo");
336
- }}
337
- >
338
- <sp-icon-search slot="icon"></sp-icon-search>
339
- Search appearance…
340
- </sp-action-button>
341
- </div>
342
- `;
436
+ function layoutRow(
437
+ tab: Tab,
438
+ headDoc: JxMutableNode,
439
+ applyMutation: (fn: (doc: JxMutableNode) => void) => void,
440
+ commits: Map<string, RowCommit>,
441
+ ): DocHeaderRow | null {
442
+ if (!isPageDocument(tab)) {
443
+ return null;
444
+ }
445
+ const entries = layoutPickerEntries();
446
+ if (entries === null) {
447
+ return null;
448
+ }
449
+ const current = headDoc.$layout;
450
+ const defaultPath = projectState?.projectConfig?.defaults?.layout;
451
+ const defaultLabel = defaultPath ? layoutName(defaultPath) : "";
452
+ const options: DocHeaderChoice[] = [
453
+ { label: defaultLabel ? `Default (${defaultLabel})` : "Default", value: "__default__" },
454
+ { label: "None", value: "__none__" },
455
+ ...entries.map((l) => ({ label: l.name, value: l.path })),
456
+ ];
457
+ commits.set("__layout", {
458
+ clear: () =>
459
+ applyMutation((d) => {
460
+ delete d.$layout;
461
+ }),
462
+ commit: (raw) => {
463
+ const val = String(raw);
464
+ applyMutation((d) => {
465
+ if (val === "__default__") {
466
+ delete d.$layout;
467
+ } else if (val === "__none__") {
468
+ d.$layout = false;
469
+ } else {
470
+ d.$layout = val;
471
+ }
472
+ });
473
+ invalidateLayoutCache();
474
+ },
475
+ });
476
+ return {
477
+ ...blankRow("__layout", "layout", "Layout"),
478
+ isSet: current !== undefined,
479
+ kind: "select",
480
+ options,
481
+ value: current === false ? "__none__" : current || "__default__",
482
+ };
483
+ }
484
+
485
+ /** `./layouts/blog-post.json` → `Blog Post`. */
486
+ function layoutName(path: string): string {
487
+ return path
488
+ .replace(/^\.\/layouts\//, "")
489
+ .replace(/\.json$/, "")
490
+ .replaceAll(/[-_]+/g, " ")
491
+ .replaceAll(/\b\w/g, (c: string) => c.toUpperCase());
343
492
  }
344
493
 
345
494
  /**
346
- * A collapsible block whose open state is per tab and lives in this module.
495
+ * One schema-driven frontmatter field, as the row that draws it and the write that commits it.
347
496
  *
348
- * Deliberately NOT on `tab.session.ui`: P3's first workstream is hoisting view flags OUT of the tab
349
- * record, and adding two more would be moving in the wrong direction for a disclosure nobody needs
350
- * restored across a relaunch.
497
+ * **What the row LOOKS like is `panels/frontmatter-fields.ts`'s answer, not this module's.** The
498
+ * Navigator's Page panel draws the same field set from the same schemas, and the two surfaces
499
+ * deciding independently that a `$ref` is a picker, that `"uri-reference"` is a media format or
500
+ * that an array is a comma-separated line is exactly how they came to disagree about `title`. What
501
+ * stays here is what the card COMMITS INTO — this tab's frontmatter, through its transaction log.
502
+ *
503
+ * `tab` is a parameter because this used to commit to `activeTab.value` at each of seven widgets,
504
+ * which is right for the Navigator's Document panel and wrong for a card drawn once per pane: a
505
+ * collection field edited on the card in one pane wrote into whichever document had the keyboard.
351
506
  */
352
- function disclosure(
353
- tabId: string,
354
- open: Set<string>,
355
- label: string,
356
- body: TemplateResult,
357
- ): TemplateResult {
358
- const isOpen = open.has(tabId);
359
- return html`
360
- <details
361
- class="doc-header-disclosure"
362
- ?open=${isOpen}
363
- @toggle=${(e: Event) => {
364
- const el = e.target as HTMLDetailsElement;
365
- if (el.open) {
366
- open.add(tabId);
367
- } else {
368
- open.delete(tabId);
369
- }
370
- }}
371
- >
372
- <summary>${label}</summary>
373
- <div class="doc-header-body">${body}</div>
374
- </details>
375
- `;
507
+ function fieldRow(
508
+ tab: Tab,
509
+ field: string,
510
+ entry: FmSchemaEntry,
511
+ value: unknown,
512
+ requiredFields: Set<string>,
513
+ commits: Map<string, RowCommit>,
514
+ ): DocHeaderRow {
515
+ const key = `fm:${field}`;
516
+ const { parse, row } = projectFmField(field, entry, value, requiredFields, { rerender: render });
517
+ commits.set(key, {
518
+ clear: () => transactDoc(tab, (t) => mutateUpdateFrontmatter(t, field)),
519
+ commit: (raw) => transactDoc(tab, (t) => mutateUpdateFrontmatter(t, field, parse(raw))),
520
+ });
521
+ return { ...blankRow(key, field, row.label), ...row, clearLabel: `Clear ${field}` };
376
522
  }
377
523
 
378
- /* THE SEO BLOCK IS A MODAL — `panels/seo-modal.ts`.
379
- Two rendered previews, a resolved-field list, a warning list, the page and Open Graph meta rows
380
- and an icon picker, all disclosed inside a card whose job is the four or five fields you fill in
381
- while writing. A previewed SERP result is not a field; it is a picture you study, and studying it
382
- in a strip above the canvas made the card taller than the document it describes. The card keeps
383
- Title — the one head value you type while writing — and offers the door. */
384
-
385
524
  /**
386
525
  * The `$head` entries no structured control owns, listed read-only.
387
526
  *
388
527
  * Read-only on purpose: the card discloses what is there so the author is never surprised by a tag
389
528
  * they cannot see, and the Page panel remains the surface that adds and removes them.
390
529
  */
391
- function rawHeadBody(head: JxHeadEntry[]): TemplateResult {
392
- const custom = head.filter(
393
- (e: JxHeadEntry) => !isManagedEntry(e) && !isGoogleFontEntry(e) && !isGoogleFontPreconnect(e),
394
- );
395
- if (custom.length === 0) {
396
- return html`<p class="doc-header-empty">No custom head tags on this document.</p>`;
397
- }
398
- return html`
399
- <ul class="doc-header-raw">
400
- ${custom.map(
401
- (entry) => html`
402
- <li>
403
- <code>${entryLabel(entry)}</code>
404
- <span title=${entryValue(entry)}>${entryValue(entry)}</span>
405
- </li>
406
- `,
407
- )}
408
- </ul>
409
- `;
530
+ function rawEntries(head: JxHeadEntry[]): DocHeaderRawEntry[] {
531
+ return head
532
+ .filter(
533
+ (e: JxHeadEntry) => !isManagedEntry(e) && !isGoogleFontEntry(e) && !isGoogleFontPreconnect(e),
534
+ )
535
+ .map((entry, index) => ({
536
+ key: `raw:${index}`,
537
+ label: entryLabel(entry),
538
+ value: entryValue(entry),
539
+ }));
540
+ }
541
+
542
+ // ─── The actions ─────────────────────────────────────────────────────────────
543
+
544
+ /**
545
+ * What the reader can do on one pane's card.
546
+ *
547
+ * Made once per host and handed to the surface at mount, so every one of them reads the CURRENT
548
+ * commit table rather than closing over the projection that was standing when the card was drawn.
549
+ */
550
+ function makeActions(card: HeaderCard): DocHeaderActions {
551
+ const flush = (key: string): void => {
552
+ const waiting = card.pending.get(key);
553
+ if (waiting) {
554
+ clearTimeout(waiting.timer);
555
+ card.pending.delete(key);
556
+ }
557
+ };
558
+ const now = (key: string, raw: string | boolean): void => {
559
+ flush(key);
560
+ card.commits.get(key)?.commit(raw);
561
+ };
562
+ return {
563
+ browse: (key, anchor) => {
564
+ flush(key);
565
+ void mediaPicker().then((m) => {
566
+ if (anchor instanceof HTMLElement) {
567
+ m.showMediaPickerPopover(anchor, (val: string) => {
568
+ card.commits.get(key)?.commit(val);
569
+ });
570
+ }
571
+ });
572
+ },
573
+ clear: (key) => {
574
+ flush(key);
575
+ card.commits.get(key)?.clear();
576
+ },
577
+ commitText: (key, value) => now(key, value),
578
+ /* Debounced, so the canvas follows the typing without a document write per keystroke. The
579
+ control is NOT reset in the meantime: the projection this commit causes resolves to the text
580
+ already in the field, and a document binding skips a write equal to what the element holds. */
581
+ editText: (key, value) => {
582
+ flush(key);
583
+ const commit = () => {
584
+ card.pending.delete(key);
585
+ card.commits.get(key)?.commit(value);
586
+ };
587
+ card.pending.set(key, { commit, timer: setTimeout(commit, LIVE_PREVIEW) });
588
+ },
589
+ openSeo: () => {
590
+ // The COMMAND, not a local open() — the Page panel offers the same door and neither of them
591
+ // Owns it.
592
+ void activeRegistry()?.run("document.openSeo");
593
+ },
594
+ setBoolean: (key, checked) => now(key, checked),
595
+ setChoice: (key, value) => now(key, value),
596
+ setNumber: (key, value) => now(key, value),
597
+ setRawOpen: (open) => {
598
+ // Per DOCUMENT rather than per pane: the same file open in two stages discloses the same
599
+ // Tags, and a flag on the pane would make the two cards disagree about one document.
600
+ const tab = tabOfPane(card.paneId);
601
+ if (!tab) {
602
+ return;
603
+ }
604
+ if (open) {
605
+ _rawOpen.add(tab.id);
606
+ } else {
607
+ _rawOpen.delete(tab.id);
608
+ }
609
+ },
610
+ upload: (key) => {
611
+ flush(key);
612
+ void mediaPicker().then((m) => {
613
+ m.pickAndUpload((val: string) => {
614
+ card.commits.get(key)?.commit(val);
615
+ });
616
+ });
617
+ },
618
+ };
619
+ }
620
+
621
+ /**
622
+ * The media picker's two behaviours, imported on the first press.
623
+ *
624
+ * ONE cached promise, deliberately: two dynamic imports of a module in flight at once is the shape
625
+ * that loses its coverage record (see the note in the repository's agent guide), and the file also
626
+ * pulls in the upload pipeline and the media metadata cache — neither of which a card that draws no
627
+ * media field should ever load.
628
+ */
629
+ let _mediaPicker: Promise<typeof MediaPickerModule> | null = null;
630
+
631
+ function mediaPicker(): Promise<typeof MediaPickerModule> {
632
+ _mediaPicker ??= import("../ui/media-picker");
633
+ return _mediaPicker;
410
634
  }