@jxsuite/studio 6.1.2 → 8.0.1

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 (522) hide show
  1. package/README.md +5 -5
  2. package/dist/chunks/2020-5wtzce5g.js +4 -0
  3. package/dist/chunks/adapter-5tphg7xe.js +4 -0
  4. package/dist/chunks/add-new-flow-psym14sn.js +4 -0
  5. package/dist/chunks/cf-account-picker-haqazq5r.js +4 -0
  6. package/dist/chunks/cf-settings-h2wrh6hq.js +4 -0
  7. package/dist/chunks/{color-area-0fj8a4ga.js → color-area-6rbra5dy.js} +2 -2
  8. package/dist/chunks/{color-field-xch8kx2g.js → color-field-gy56c94g.js} +3 -3
  9. package/dist/chunks/color-m7r1vk54.js +4 -0
  10. package/dist/chunks/{color-slider-jxd8v350.js → color-slider-sqqdyf6c.js} +2 -2
  11. package/dist/chunks/{combobox-p0efwjke.js → combobox-72h32ejk.js} +3 -3
  12. package/dist/chunks/components-r22xca09.js +4 -0
  13. package/dist/chunks/content-source-mmppmcyg.js +4 -0
  14. package/dist/chunks/convert-to-repeater-he70fc2b.js +4 -0
  15. package/dist/chunks/editor-ccd9jwsa.js +4 -0
  16. package/dist/chunks/entry-commands-6qxz9e8p.js +4 -0
  17. package/dist/chunks/extension-sections-0egb88dm.js +4 -0
  18. package/dist/chunks/file-ops-qnab36ek.js +4 -0
  19. package/dist/chunks/files-rcn56qvm.js +4 -0
  20. package/dist/chunks/format-host-yxz0kjjs.js +4 -0
  21. package/dist/chunks/fs-events-d1qxq4z4.js +4 -0
  22. package/dist/chunks/grid-open-4z1g062t.js +4 -0
  23. package/dist/chunks/{grid-view-d2wq9ezg.js → grid-view-qg1b5eqx.js} +25 -25
  24. package/dist/chunks/{icons-pnfz478q.js → icons-63bjtbs7.js} +2 -2
  25. package/dist/chunks/{iframe-entry-jdcktez8.js → iframe-entry-2zggzajv.js} +4 -4
  26. package/dist/chunks/{iframe-entry-zap785cw.js → iframe-entry-76j8vwwx.js} +4 -4
  27. package/dist/chunks/iframe-entry-99nr5zfb.js +5 -0
  28. package/dist/chunks/{iframe-entry-x0ygf2yr.js → iframe-entry-pkgmcgp0.js} +4 -4
  29. package/dist/chunks/iframe-host-ssv7kyj0.js +4 -0
  30. package/dist/chunks/{index-zpjmzqfk.js → index-32eqzv1k.js} +3 -3
  31. package/dist/chunks/index-f0jn7nnp.js +4 -0
  32. package/dist/chunks/{javascript-m54mnwp6.js → javascript-pjp734q4.js} +3 -3
  33. package/dist/chunks/jsonMode-njkvsa02.js +13 -0
  34. package/dist/chunks/jx-validate-ttasrbh7.js +4 -0
  35. package/dist/chunks/layers-bgyr11tr.js +4 -0
  36. package/dist/chunks/layers-panel-7ez66zeq.js +4 -0
  37. package/dist/chunks/{listbox-e74qjyq6.js → listbox-c421cf8c.js} +2 -2
  38. package/dist/chunks/lit-html-v9gh687x.js +4 -0
  39. package/dist/chunks/media-library-sq468wts.js +4 -0
  40. package/dist/chunks/media-picker-shfe59pt.js +4 -0
  41. package/dist/chunks/{menu-n6yzswmy.js → menu-bdpzbfvs.js} +2 -2
  42. package/dist/chunks/{monaco-binding-7gx83x3r.js → monaco-binding-vq1hgnn0.js} +3 -3
  43. package/dist/chunks/monaco-buffer-3afgwvbp.js +4 -0
  44. package/dist/chunks/monaco-setup-xrd2cbkp.js +4 -0
  45. package/dist/chunks/notify-kzrjr3fv.js +4 -0
  46. package/dist/chunks/parse-v77xtwv7.js +4 -0
  47. package/dist/chunks/platform-7v4rtd7m.js +4 -0
  48. package/dist/chunks/popover-b89sfej7.js +4 -0
  49. package/dist/chunks/publish-panel-2z9gw8ee.js +4 -0
  50. package/dist/chunks/publish-quick-qx2455cj.js +4 -0
  51. package/dist/chunks/publish-workspace-wrc1dgwx.js +4 -0
  52. package/dist/chunks/quick-search-n0t0eqxb.js +4 -0
  53. package/dist/chunks/references-9547rg5s.js +4 -0
  54. package/dist/chunks/right-panel-wsgnr9pn.js +4 -0
  55. package/dist/chunks/source-control-kwktr9y6.js +4 -0
  56. package/dist/chunks/{split-n2gfpm5m.js → split-c9zfdgxf.js} +2 -2
  57. package/dist/chunks/studio-03ct6yfv.js +9 -0
  58. package/dist/chunks/studio-07rcfje3.js +5 -0
  59. package/dist/chunks/studio-3e54qh39.js +7 -0
  60. package/dist/chunks/studio-3exxhz5g.js +5 -0
  61. package/dist/chunks/studio-3q40e3rj.js +5 -0
  62. package/dist/chunks/studio-3qeh9t0h.js +6 -0
  63. package/dist/chunks/studio-3qqjeep5.js +6 -0
  64. package/dist/chunks/studio-43sd2mam.js +4 -0
  65. package/dist/chunks/studio-4hp8pkyt.js +5 -0
  66. package/dist/chunks/studio-58xg6j2q.js +9 -0
  67. package/dist/chunks/studio-5g0rprdt.js +7 -0
  68. package/dist/chunks/{studio-2j1cb95x.js → studio-6m380wp7.js} +4 -4
  69. package/dist/chunks/studio-80jyh1xh.js +4 -0
  70. package/dist/chunks/studio-83mk4bgg.js +9 -0
  71. package/dist/chunks/{studio-v0jzqdjm.js → studio-881z1xrx.js} +4 -4
  72. package/dist/chunks/studio-8m0y2qe7.js +5 -0
  73. package/dist/chunks/{studio-d81xfh1g.js → studio-8tr6cfrc.js} +4 -4
  74. package/dist/chunks/studio-8vcvfs7r.js +6 -0
  75. package/dist/chunks/studio-9tmebf0s.js +5 -0
  76. package/dist/chunks/studio-a2bvaptz.js +5 -0
  77. package/dist/chunks/studio-b7y5mr30.js +5 -0
  78. package/dist/chunks/studio-bavb31r0.js +5 -0
  79. package/dist/chunks/{studio-dgz8d0c1.js → studio-bbe4c90a.js} +4 -4
  80. package/dist/chunks/studio-c05wdnt3.js +5 -0
  81. package/dist/chunks/studio-ccv2svc6.js +5 -0
  82. package/dist/chunks/studio-d5grtb9e.js +16 -0
  83. package/dist/chunks/studio-df0baea9.js +5 -0
  84. package/dist/chunks/studio-dgk4dqtx.js +7 -0
  85. package/dist/chunks/studio-e1g64q43.js +5 -0
  86. package/dist/chunks/studio-ed1hp6zf.js +6 -0
  87. package/dist/chunks/studio-ed3xx40h.js +7 -0
  88. package/dist/chunks/studio-f14e1zdj.js +5 -0
  89. package/dist/chunks/studio-fbhxxc5f.js +5 -0
  90. package/dist/chunks/studio-fyh0jc96.js +5 -0
  91. package/dist/chunks/studio-gcxk20n7.js +5 -0
  92. package/dist/chunks/studio-jaaz8ce9.js +5 -0
  93. package/dist/chunks/{studio-90pq9km1.js → studio-jcnsdaep.js} +4 -4
  94. package/dist/chunks/studio-jsgf2q5k.js +7 -0
  95. package/dist/chunks/{studio-0q988f9w.js → studio-k3xxbagr.js} +3 -3
  96. package/dist/chunks/{studio-5ve6bbep.js → studio-k7evxqh4.js} +4 -4
  97. package/dist/chunks/studio-kt3pjkwd.js +5 -0
  98. package/dist/chunks/studio-m8g16bgz.js +5 -0
  99. package/dist/chunks/studio-mn0476ag.js +5 -0
  100. package/dist/chunks/studio-mx1smyq0.js +5 -0
  101. package/dist/chunks/studio-n0c5h6mf.js +5 -0
  102. package/dist/chunks/{studio-485046te.js → studio-nh6d342n.js} +7 -7
  103. package/dist/chunks/studio-nqxqf8p2.js +4 -0
  104. package/dist/chunks/studio-p4nrkvqr.js +5 -0
  105. package/dist/chunks/{studio-1fxkaaea.js → studio-pbxa5fpc.js} +4 -4
  106. package/dist/chunks/studio-pgmvdv7z.js +7 -0
  107. package/dist/chunks/studio-ph84avkf.js +5 -0
  108. package/dist/chunks/{studio-tq0rtfwj.js → studio-q0mvbaq6.js} +4 -4
  109. package/dist/chunks/studio-q0tccd43.js +5 -0
  110. package/dist/chunks/studio-q7rtx6zq.js +14 -0
  111. package/dist/chunks/{studio-a3g98wjr.js → studio-qgh13jtz.js} +4 -4
  112. package/dist/chunks/studio-qmn1xc24.js +5 -0
  113. package/dist/chunks/studio-qwdqwkj0.js +5 -0
  114. package/dist/chunks/studio-r5qy30vx.js +5 -0
  115. package/dist/chunks/studio-rj9eamf8.js +5 -0
  116. package/dist/chunks/studio-rptgsk96.js +5 -0
  117. package/dist/chunks/studio-rthyg42z.js +928 -0
  118. package/dist/chunks/studio-s19tp1xr.js +5 -0
  119. package/dist/chunks/studio-s32578pn.js +5 -0
  120. package/dist/chunks/studio-svb7yyve.js +5 -0
  121. package/dist/chunks/studio-syreb644.js +5 -0
  122. package/dist/chunks/studio-tf9jryb1.js +10 -0
  123. package/dist/chunks/studio-tfkznv39.js +5 -0
  124. package/dist/chunks/studio-tft6r105.js +5 -0
  125. package/dist/chunks/studio-txf2kysx.js +5 -0
  126. package/dist/chunks/studio-v8867ddv.js +12 -0
  127. package/dist/chunks/studio-vxq7fcvv.js +5 -0
  128. package/dist/chunks/studio-w4rz9hdk.js +5 -0
  129. package/dist/chunks/studio-wbcjbhy1.js +9 -0
  130. package/dist/chunks/studio-wc649wss.js +5 -0
  131. package/dist/chunks/studio-wys23mxz.js +5 -0
  132. package/dist/chunks/studio-x1x167jh.js +5 -0
  133. package/dist/chunks/studio-x9hsze61.js +5 -0
  134. package/dist/chunks/studio-y0n7yxea.js +9 -0
  135. package/dist/chunks/studio-y3ahhdpx.js +5 -0
  136. package/dist/chunks/studio-z97c48fk.js +5 -0
  137. package/dist/chunks/studio-zat62m0c.js +5 -0
  138. package/dist/chunks/studio-zcd2e7yz.js +5 -0
  139. package/dist/chunks/studio-zz9qe3rw.js +64 -0
  140. package/dist/chunks/toast-1gv0h53h.js +4 -0
  141. package/dist/chunks/{toast-host-ke5z4ng7.js → toast-host-1yg5ar8d.js} +3 -3
  142. package/dist/chunks/{tooltip-5v8ngwj2.js → tooltip-yypz28gq.js} +3 -3
  143. package/dist/chunks/{tree-mdq3v5cw.js → tree-9xa4hm7n.js} +2 -2
  144. package/dist/chunks/tsMode-epyfz1hv.js +14 -0
  145. package/dist/chunks/workspace-dz7nxfhv.js +4 -0
  146. package/dist/chunks/workspace-home-xsd7hkjw.js +4 -0
  147. package/dist/chunks/{ws-client-nze0k6mv.js → ws-client-bv552sfx.js} +3 -3
  148. package/dist/iframe-entry.js +20 -16
  149. package/dist/manifest.json +77 -63
  150. package/dist/studio.js +103 -104
  151. package/dist/workers/ts.worker.js +3 -3
  152. package/package.json +12 -11
  153. package/src/browse/component-examples.ts +272 -0
  154. package/src/browse/library-commands.ts +64 -104
  155. package/src/browse/library-layouts.ts +263 -49
  156. package/src/browse/library-model.ts +181 -133
  157. package/src/browse/library-pane.ts +738 -446
  158. package/src/browse/library-preview.ts +149 -19
  159. package/src/browse/library-source.ts +308 -41
  160. package/src/browse/preview-root.ts +78 -0
  161. package/src/browse/preview-style.ts +155 -0
  162. package/src/canvas/asset-refs.ts +16 -1
  163. package/src/canvas/canvas-live-render.ts +222 -30
  164. package/src/canvas/canvas-origin.ts +37 -12
  165. package/src/canvas/canvas-page-context.ts +112 -0
  166. package/src/canvas/canvas-render.ts +113 -107
  167. package/src/canvas/canvas-utils.ts +57 -12
  168. package/src/canvas/iframe-drop.ts +107 -0
  169. package/src/canvas/iframe-entry.ts +24 -12
  170. package/src/canvas/iframe-host.ts +134 -62
  171. package/src/canvas/iframe-keys.ts +11 -0
  172. package/src/canvas/iframe-patch.ts +8 -7
  173. package/src/canvas/iframe-protocol.ts +24 -5
  174. package/src/canvas/iframe-render.ts +48 -1
  175. package/src/canvas/iframe-subtree.ts +5 -2
  176. package/src/canvas/npm-modules.ts +22 -0
  177. package/src/canvas/preview-navigate.ts +16 -0
  178. package/src/canvas/repeater-sources.ts +200 -0
  179. package/src/canvas/stage-scrollbar.ts +5 -1
  180. package/src/canvas/surface-registry.ts +6 -0
  181. package/src/commands/app-commands.ts +16 -6
  182. package/src/commands/budget.ts +10 -8
  183. package/src/commands/context.ts +35 -10
  184. package/src/commands/defaults.ts +69 -45
  185. package/src/commands/keymap.ts +9 -0
  186. package/src/commands/levels.ts +11 -5
  187. package/src/commands/live-context.ts +2 -0
  188. package/src/commands/registry.ts +10 -1
  189. package/src/content/collection-match.ts +6 -5
  190. package/src/content/collection-pages.ts +412 -0
  191. package/src/content/content-collections.ts +270 -0
  192. package/src/content/entry-commands.ts +51 -60
  193. package/src/content/entry-editor.ts +111 -18
  194. package/src/content/entry-fields.ts +13 -1
  195. package/src/content/entry-inspector.ts +225 -0
  196. package/src/content/entry-model.ts +17 -3
  197. package/src/content/entry-open.ts +80 -0
  198. package/src/content/entry-summaries.ts +440 -0
  199. package/src/content/file-record-ids.ts +80 -0
  200. package/src/content/file-records.ts +125 -0
  201. package/src/content/page-summaries.ts +129 -0
  202. package/src/content/record-entry.ts +201 -0
  203. package/src/content/writing-banner.ts +177 -0
  204. package/src/create/add-new-commands.ts +185 -0
  205. package/src/create/add-new-flow.ts +1116 -0
  206. package/src/create/add-new.ts +1014 -0
  207. package/src/create/element-name.ts +121 -0
  208. package/src/editor/convert-to-component.ts +67 -43
  209. package/src/editor/file-drop-action.ts +9 -1
  210. package/src/editor/inline-edit-apply.ts +44 -0
  211. package/src/editor/inline-edit.ts +10 -0
  212. package/src/editor/insert-component.ts +91 -0
  213. package/src/editor/insert-zone-action.ts +13 -4
  214. package/src/editor/shortcuts.ts +44 -22
  215. package/src/editor/slash-menu.ts +51 -8
  216. package/src/files/components.ts +74 -10
  217. package/src/files/file-ops.ts +15 -5
  218. package/src/files/files.ts +164 -150
  219. package/src/files/fs-events.ts +18 -7
  220. package/src/files/media-meta.ts +95 -1
  221. package/src/files/media-types.ts +55 -0
  222. package/src/files/media-upload.ts +209 -52
  223. package/src/files/new-file-name.ts +156 -0
  224. package/src/files/serialize-document.ts +13 -3
  225. package/src/format/format-host.ts +15 -0
  226. package/src/github/github-auth.ts +2 -2
  227. package/src/github/github-publish.ts +4 -3
  228. package/src/grid/grid-controller.ts +5 -1
  229. package/src/grid/grid-open.ts +0 -27
  230. package/src/grid/grid-panel.ts +54 -2
  231. package/src/grid/grid-reveal.ts +34 -0
  232. package/src/grid/grid-source.ts +3 -25
  233. package/src/grid/grid-view.ts +31 -6
  234. package/src/grid/sources/content-source.ts +75 -7
  235. package/src/grid/tabulator-tables.d.ts +6 -0
  236. package/src/hosting/document.ts +2 -2
  237. package/src/i18n/i18n-commands.ts +2 -1
  238. package/src/media/media-commands.ts +181 -0
  239. package/src/media/media-library-model.ts +232 -0
  240. package/src/media/media-library.ts +1283 -0
  241. package/src/media/media-pane.ts +47 -19
  242. package/src/packages/pull-package-sync.ts +1 -1
  243. package/src/panels/activity-panel.ts +21 -5
  244. package/src/panels/ai-chat/chat-markdown.ts +8 -8
  245. package/src/panels/block-action-bar.ts +11 -11
  246. package/src/panels/canvas-dnd-bridge.ts +5 -2
  247. package/src/panels/chat-panel.ts +6 -6
  248. package/src/panels/collections-panel.ts +306 -0
  249. package/src/panels/component-preview.ts +35 -13
  250. package/src/panels/data-explorer.ts +1 -1
  251. package/src/panels/data-grid.ts +4 -0
  252. package/src/panels/dnd.ts +17 -30
  253. package/src/panels/elements-panel.ts +9 -2
  254. package/src/panels/empty-state.ts +2 -2
  255. package/src/panels/formula-workspace.ts +2 -3
  256. package/src/panels/frontmatter-fields.ts +9 -9
  257. package/src/panels/git-diff-open.ts +9 -5
  258. package/src/panels/head-panel.ts +369 -103
  259. package/src/panels/i18n-panel.ts +11 -10
  260. package/src/panels/imports-panel.ts +2 -0
  261. package/src/panels/jump-bar.ts +27 -2
  262. package/src/panels/layers-panel.ts +36 -30
  263. package/src/panels/left-panel.ts +22 -8
  264. package/src/panels/navigator-panels.ts +23 -11
  265. package/src/panels/pane-context.ts +111 -29
  266. package/src/panels/pane-grid.ts +6 -6
  267. package/src/panels/panel-registry.ts +14 -9
  268. package/src/panels/properties-panel.ts +7 -3
  269. package/src/panels/right-panel.ts +199 -43
  270. package/src/panels/seo-modal.ts +41 -108
  271. package/src/panels/settings-menu.ts +52 -50
  272. package/src/panels/settings-pane.ts +268 -162
  273. package/src/panels/signals-panel.ts +3 -4
  274. package/src/panels/site-model.ts +367 -0
  275. package/src/panels/site-panel.ts +638 -0
  276. package/src/panels/style-panel.ts +37 -3
  277. package/src/panels/stylebook-doc.ts +142 -10
  278. package/src/panels/stylebook-layers-panel.ts +39 -80
  279. package/src/panels/stylebook-panel.ts +47 -10
  280. package/src/panels/styles-panel.ts +327 -0
  281. package/src/panels/tab-strip.ts +109 -25
  282. package/src/platform.ts +1 -1
  283. package/src/platforms/cloud.ts +28 -1
  284. package/src/platforms/default-platform.ts +4 -4
  285. package/src/platforms/devserver.ts +51 -25
  286. package/src/publish/deploy-checklist.ts +12 -7
  287. package/src/publish/pages-service.ts +38 -14
  288. package/src/publish/publish-changes.ts +491 -0
  289. package/src/publish/publish-commands.ts +83 -18
  290. package/src/publish/publish-ids.ts +49 -0
  291. package/src/publish/publish-quick.ts +148 -0
  292. package/src/publish/publish-workspace.ts +926 -0
  293. package/src/publish/source-control.ts +907 -0
  294. package/src/services/automation.ts +16 -20
  295. package/src/services/cf-settings.ts +4 -4
  296. package/src/services/connection.ts +2 -2
  297. package/src/services/directory-picker.ts +2 -2
  298. package/src/services/references.ts +0 -0
  299. package/src/services/render-critic.ts +21 -2
  300. package/src/services/settings/definitions.ts +25 -14
  301. package/src/settings/contributed-section.ts +49 -10
  302. package/src/settings/css-vars-editor.ts +93 -27
  303. package/src/settings/developer-panels.ts +43 -0
  304. package/src/settings/extension-sections.ts +20 -2
  305. package/src/settings/general-settings.ts +91 -21
  306. package/src/settings/preferences-dialog.ts +8 -1
  307. package/src/settings/project-sections.ts +27 -10
  308. package/src/settings/schema-field-ui.ts +3 -1
  309. package/src/settings/section-registry.ts +116 -11
  310. package/src/settings/settings-dialog.ts +192 -0
  311. package/src/settings/settings-document.ts +182 -162
  312. package/src/shell.ts +467 -431
  313. package/src/site-context.ts +64 -10
  314. package/src/state.ts +28 -9
  315. package/src/studio.ts +194 -41
  316. package/src/style/project-styles.ts +34 -0
  317. package/src/style/stylebook-specimens.ts +89 -0
  318. package/src/style/stylebook-target.ts +26 -0
  319. package/src/style/tag-inheritance.ts +105 -0
  320. package/src/style/variables-focus.ts +61 -0
  321. package/src/style/variables-view.ts +235 -0
  322. package/src/surfaces/add-new.json +1482 -0
  323. package/src/surfaces/add-new.ts +325 -0
  324. package/src/surfaces/ai-chat.ts +2 -2
  325. package/src/surfaces/boot-failure.json +4 -3
  326. package/src/surfaces/bottom-dock.json +1 -1
  327. package/src/surfaces/canvas-stage.json +30 -19
  328. package/src/surfaces/canvas-stage.ts +5 -5
  329. package/src/surfaces/collections-panel.json +615 -0
  330. package/src/surfaces/collections-panel.ts +168 -0
  331. package/src/surfaces/commandbar.json +241 -96
  332. package/src/surfaces/commandbar.ts +134 -99
  333. package/src/surfaces/empty-state.json +1 -1
  334. package/src/surfaces/entry-editor.json +136 -0
  335. package/src/surfaces/entry-editor.ts +21 -0
  336. package/src/surfaces/entry-inspector.json +371 -0
  337. package/src/surfaces/entry-inspector.ts +143 -0
  338. package/src/surfaces/inspector-dock.json +162 -14
  339. package/src/surfaces/inspector-dock.ts +46 -19
  340. package/src/surfaces/library-pane.json +670 -437
  341. package/src/surfaces/library-pane.ts +94 -59
  342. package/src/surfaces/media-browser.json +62 -6
  343. package/src/surfaces/media-browser.ts +15 -1
  344. package/src/surfaces/media-field.json +1 -1
  345. package/src/surfaces/media-library.json +1586 -0
  346. package/src/surfaces/media-library.ts +282 -0
  347. package/src/surfaces/media-pick-dialog.json +71 -0
  348. package/src/surfaces/media-pick-dialog.ts +132 -0
  349. package/src/surfaces/menu.ts +18 -0
  350. package/src/surfaces/pane-context.json +144 -77
  351. package/src/surfaces/pane-context.ts +24 -0
  352. package/src/surfaces/panel-deploy-checklist.json +12 -6
  353. package/src/surfaces/panel-deploy-checklist.ts +3 -1
  354. package/src/surfaces/panel-page.json +72 -2
  355. package/src/surfaces/panel-page.ts +38 -7
  356. package/src/surfaces/panel-signals.json +1 -1
  357. package/src/surfaces/panel-signals.ts +5 -5
  358. package/src/surfaces/panel-stylebook-layers.json +71 -204
  359. package/src/surfaces/panel-stylebook-layers.ts +0 -21
  360. package/src/surfaces/preferences.json +120 -48
  361. package/src/surfaces/preferences.ts +8 -0
  362. package/src/surfaces/publish-quick.json +428 -0
  363. package/src/surfaces/publish-quick.ts +165 -0
  364. package/src/surfaces/publish-workspace.json +2892 -0
  365. package/src/surfaces/publish-workspace.ts +391 -0
  366. package/src/surfaces/rail.json +1 -159
  367. package/src/surfaces/rail.ts +18 -75
  368. package/src/surfaces/schema-form.json +10 -2
  369. package/src/surfaces/schema-form.ts +2 -0
  370. package/src/surfaces/seo.json +1 -1
  371. package/src/surfaces/settings-contributed.json +5 -5
  372. package/src/surfaces/settings-contributed.ts +18 -2
  373. package/src/surfaces/settings-css-vars.json +44 -36
  374. package/src/surfaces/settings-css-vars.ts +77 -4
  375. package/src/surfaces/settings-deploy.json +2 -15
  376. package/src/surfaces/settings-deploy.ts +1 -1
  377. package/src/surfaces/settings-dialog.json +51 -0
  378. package/src/surfaces/settings-dialog.ts +169 -0
  379. package/src/surfaces/settings-overview.json +3 -3
  380. package/src/surfaces/settings-overview.ts +9 -2
  381. package/src/surfaces/settings-pane.json +269 -46
  382. package/src/surfaces/settings-pane.ts +85 -24
  383. package/src/surfaces/shell.json +2 -1
  384. package/src/surfaces/site-panel.json +1029 -0
  385. package/src/surfaces/site-panel.ts +233 -0
  386. package/src/surfaces/slash-menu.json +63 -21
  387. package/src/surfaces/slash-menu.ts +23 -0
  388. package/src/surfaces/statusbar.ts +39 -15
  389. package/src/surfaces/styles-panel.json +735 -0
  390. package/src/surfaces/styles-panel.ts +195 -0
  391. package/src/surfaces/styles-variables.json +149 -0
  392. package/src/surfaces/styles-variables.ts +145 -0
  393. package/src/surfaces/tab-strip.json +4 -4
  394. package/src/surfaces/welcome.ts +1 -1
  395. package/src/surfaces/writing-banner.json +94 -0
  396. package/src/surfaces/writing-banner.ts +80 -0
  397. package/src/tabs/bare-repeaters.ts +101 -0
  398. package/src/tabs/tab.ts +47 -7
  399. package/src/types.ts +21 -26
  400. package/src/ui/form-controls.ts +47 -8
  401. package/src/ui/layers.ts +13 -0
  402. package/src/ui/media-picker.ts +284 -40
  403. package/src/ui/regions.ts +7 -6
  404. package/src/ui/schema-form.ts +259 -27
  405. package/src/ui/virtual-window.ts +26 -1
  406. package/src/utils/base64.ts +3 -3
  407. package/src/utils/edit-display.ts +322 -67
  408. package/src/utils/local-day.ts +18 -0
  409. package/src/workspace/pane-derive.ts +4 -4
  410. package/src/workspace/session.ts +28 -7
  411. package/src/workspace/tab-workspace.ts +137 -0
  412. package/src/workspace/workspace-back.ts +189 -0
  413. package/src/workspace/workspace-home.ts +133 -0
  414. package/src/workspace/workspace-ids.ts +129 -0
  415. package/src/workspace/workspace-switch.ts +372 -0
  416. package/src/workspace/workspace.ts +125 -11
  417. package/styles/shell-frame.css +30 -7
  418. package/styles/shell-frame.json +15 -3
  419. package/dist/chunks/2020-f2gkpy8d.js +0 -4
  420. package/dist/chunks/adapter-4912g339.js +0 -4
  421. package/dist/chunks/cf-account-picker-wccaenh6.js +0 -4
  422. package/dist/chunks/cf-settings-br6105tr.js +0 -4
  423. package/dist/chunks/color-28ksk2qm.js +0 -4
  424. package/dist/chunks/components-db68vsn7.js +0 -4
  425. package/dist/chunks/content-source-1449qesq.js +0 -4
  426. package/dist/chunks/convert-to-repeater-5h457egj.js +0 -4
  427. package/dist/chunks/editor-x9jn86a7.js +0 -4
  428. package/dist/chunks/entry-commands-may24cpt.js +0 -4
  429. package/dist/chunks/extension-sections-pvr14vxr.js +0 -4
  430. package/dist/chunks/file-ops-x9v54pcj.js +0 -4
  431. package/dist/chunks/files-j3knqf0s.js +0 -4
  432. package/dist/chunks/format-host-tyre2trb.js +0 -4
  433. package/dist/chunks/fs-events-64ma9htc.js +0 -4
  434. package/dist/chunks/git-panel-84ps5dex.js +0 -4
  435. package/dist/chunks/iframe-entry-fvbk89gm.js +0 -5
  436. package/dist/chunks/iframe-host-ck6vejt5.js +0 -4
  437. package/dist/chunks/index-b8xw3vy9.js +0 -4
  438. package/dist/chunks/jsonMode-w15cmrcm.js +0 -13
  439. package/dist/chunks/jx-validate-at3zn8wa.js +0 -4
  440. package/dist/chunks/layers-h4xp1pss.js +0 -4
  441. package/dist/chunks/layers-panel-m5hw1d7g.js +0 -4
  442. package/dist/chunks/lit-html-8t8t3dnr.js +0 -4
  443. package/dist/chunks/media-picker-2cweexc5.js +0 -4
  444. package/dist/chunks/menu-ynw13y0s.js +0 -4
  445. package/dist/chunks/monaco-buffer-qzxn567r.js +0 -4
  446. package/dist/chunks/monaco-setup-gxf6656g.js +0 -4
  447. package/dist/chunks/notify-cj5zbrs2.js +0 -4
  448. package/dist/chunks/parse-b7ddhr8m.js +0 -4
  449. package/dist/chunks/platform-3dfws9js.js +0 -4
  450. package/dist/chunks/popover-jt2gxmzs.js +0 -4
  451. package/dist/chunks/publish-panel-1gvxbj21.js +0 -4
  452. package/dist/chunks/quick-search-0dajrwqg.js +0 -4
  453. package/dist/chunks/references-49qgt404.js +0 -4
  454. package/dist/chunks/right-panel-jm1bwe53.js +0 -4
  455. package/dist/chunks/section-registry-6kwc4fky.js +0 -4
  456. package/dist/chunks/studio-0rt52vpq.js +0 -5
  457. package/dist/chunks/studio-18vgbhs5.js +0 -5
  458. package/dist/chunks/studio-24mccdrj.js +0 -5
  459. package/dist/chunks/studio-2eh507zk.js +0 -9
  460. package/dist/chunks/studio-2hcbccfv.js +0 -5
  461. package/dist/chunks/studio-2jbs3rxv.js +0 -5
  462. package/dist/chunks/studio-4h8k4dw9.js +0 -5
  463. package/dist/chunks/studio-5f6n18kx.js +0 -5
  464. package/dist/chunks/studio-5gthpky6.js +0 -5
  465. package/dist/chunks/studio-6ep3js6b.js +0 -9
  466. package/dist/chunks/studio-6wkmf3wz.js +0 -9
  467. package/dist/chunks/studio-841rf3cg.js +0 -5
  468. package/dist/chunks/studio-8gmwgheg.js +0 -5
  469. package/dist/chunks/studio-8qm0ep50.js +0 -5
  470. package/dist/chunks/studio-924p1tm1.js +0 -6
  471. package/dist/chunks/studio-a3shpz9y.js +0 -5
  472. package/dist/chunks/studio-amsbaaar.js +0 -6
  473. package/dist/chunks/studio-at5spjbc.js +0 -7
  474. package/dist/chunks/studio-b2xv945e.js +0 -5
  475. package/dist/chunks/studio-bn5nkafw.js +0 -5
  476. package/dist/chunks/studio-bw6q5xjf.js +0 -9
  477. package/dist/chunks/studio-c7q0t9x1.js +0 -5
  478. package/dist/chunks/studio-camnhc96.js +0 -6
  479. package/dist/chunks/studio-cffqvnzd.js +0 -5
  480. package/dist/chunks/studio-efvfrsf8.js +0 -5
  481. package/dist/chunks/studio-er30bpcx.js +0 -5
  482. package/dist/chunks/studio-ezscgr64.js +0 -5
  483. package/dist/chunks/studio-ffrrncvz.js +0 -5
  484. package/dist/chunks/studio-fpphgn8y.js +0 -10
  485. package/dist/chunks/studio-j1kvhdjk.js +0 -4
  486. package/dist/chunks/studio-j430n16g.js +0 -7
  487. package/dist/chunks/studio-jjkg84nj.js +0 -5
  488. package/dist/chunks/studio-m0q32ars.js +0 -4
  489. package/dist/chunks/studio-m6bdst11.js +0 -5
  490. package/dist/chunks/studio-nv2cptd4.js +0 -64
  491. package/dist/chunks/studio-nwfrc98n.js +0 -5
  492. package/dist/chunks/studio-p1wgvp7r.js +0 -5
  493. package/dist/chunks/studio-pag2wncm.js +0 -5
  494. package/dist/chunks/studio-qvanvzfd.js +0 -5
  495. package/dist/chunks/studio-qwa64kt7.js +0 -5
  496. package/dist/chunks/studio-rk1zc4x3.js +0 -928
  497. package/dist/chunks/studio-se2r3ptc.js +0 -5
  498. package/dist/chunks/studio-sg2a0dh0.js +0 -5
  499. package/dist/chunks/studio-tjzjhe68.js +0 -5
  500. package/dist/chunks/studio-v2w7q8r1.js +0 -7
  501. package/dist/chunks/studio-v5vjq1h0.js +0 -4
  502. package/dist/chunks/studio-v8aga3vp.js +0 -14
  503. package/dist/chunks/studio-vfcdrfmv.js +0 -5
  504. package/dist/chunks/studio-vmfkgdjf.js +0 -5
  505. package/dist/chunks/studio-wt1816jj.js +0 -5
  506. package/dist/chunks/studio-wz73q5kq.js +0 -4
  507. package/dist/chunks/studio-y04qtxvp.js +0 -7
  508. package/dist/chunks/studio-y21g8zzg.js +0 -5
  509. package/dist/chunks/studio-y64hrt8p.js +0 -12
  510. package/dist/chunks/studio-ynn90f58.js +0 -16
  511. package/dist/chunks/studio-yxz682sx.js +0 -5
  512. package/dist/chunks/studio-zp7snvkq.js +0 -5
  513. package/dist/chunks/studio-zq4z6tm6.js +0 -5
  514. package/dist/chunks/toast-c9fn075p.js +0 -4
  515. package/dist/chunks/tsMode-sf49ee9x.js +0 -14
  516. package/dist/chunks/workspace-jtfahpee.js +0 -4
  517. package/src/panels/frontmatter-panel.ts +0 -711
  518. package/src/panels/git-panel.ts +0 -1108
  519. package/src/surfaces/doc-header.json +0 -776
  520. package/src/surfaces/doc-header.ts +0 -222
  521. package/src/surfaces/git-panel.json +0 -1880
  522. package/src/surfaces/git-panel.ts +0 -264
@@ -23,6 +23,7 @@ import { disposeSubtree, renderSubtreeIframe } from "./iframe-subtree";
23
23
  import {
24
24
  computeEmptyPlaceholderClass,
25
25
  EMPTY_PLACEHOLDER_CLASSES,
26
+ isRowBound,
26
27
  templateToEditDisplay,
27
28
  } from "../utils/edit-display";
28
29
  import type { IframeRenderCtx } from "./iframe-render";
@@ -342,7 +343,7 @@ function patchStyle(
342
343
  /* SAFETY: buildScope writes `$media` only from the document's own `$media` map or the runtime's
343
344
  * root media map, both Record<string, string>, and the host scope may not supply the key. */
344
345
  const mediaQueries = (ctx?.defs["$media"] as Record<string, string> | undefined) ?? {};
345
- reapplyStyle(el, editModeStyle(node?.style), mediaQueries);
346
+ reapplyStyle(el, editModeStyle(node?.style), mediaQueries, ctx?.defs ?? {});
346
347
  }
347
348
 
348
349
  /** Write the node's display text, syncing the empty-placeholder class like the full render. */
@@ -371,8 +372,9 @@ function findElement(container: HTMLElement, path: (string | number)[]): HTMLEle
371
372
  }
372
373
 
373
374
  /**
374
- * Edit-mode style transform — mirrors prepareForEditMode (and the legacy patcher's editModeStyle):
375
- * top-level template-string values are blanked so no reactive bindings are created for them.
375
+ * Edit-mode style transform — mirrors prepareForEditMode: bindings are live and resolve against the
376
+ * render's scope, except a declaration that reads the repeater row, which the perimeter cannot
377
+ * supply and so is omitted.
376
378
  */
377
379
  function editModeStyle(style: JxStyle | undefined): JxStyle | undefined {
378
380
  if (!style || !isNonNullObject(style)) {
@@ -380,10 +382,9 @@ function editModeStyle(style: JxStyle | undefined): JxStyle | undefined {
380
382
  }
381
383
  const out: JxStyle = {};
382
384
  for (const [k, v] of Object.entries(style)) {
383
- /* OMITTED, not blanked. It used to be written as `""`, which was a silent no-op while a
384
- declaration went to `el.style` and is a malformed declaration now that it goes into a rule.
385
- Dropping the key is what blanking always meant: the property falls back to the cascade. */
386
- if (isString(v) && v.includes("${")) {
385
+ /* OMITTED, not blanked: `color: ` is a malformed declaration in a rule, and an absent one lets
386
+ the cascade answer, which is what blanking always meant. */
387
+ if (isRowBound(v)) {
387
388
  continue;
388
389
  }
389
390
  out[k] = v;
@@ -77,6 +77,12 @@ export interface WireDiffMark {
77
77
  /** An artboard's whole mark set. Each side receives only its own; `[]` clears. */
78
78
  export type WireDiffMarks = readonly WireDiffMark[];
79
79
 
80
+ /** A language as the frame applies it: the BCP 47 tag for `lang`, and its direction for `dir`. */
81
+ export interface WireLocale {
82
+ locale: string;
83
+ dir: "ltr" | "rtl";
84
+ }
85
+
80
86
  /** Messages the editor (parent) sends into the canvas iframe. */
81
87
  export type ParentToIframe =
82
88
  | { kind: "init"; gen: number }
@@ -135,6 +141,13 @@ export type ParentToIframe =
135
141
  * every re-render would close the panel the author is editing.
136
142
  */
137
143
  popoverOpen?: (string | number)[] | null;
144
+ /**
145
+ * The language the artboard is drawn in, carried on `render` for `colorScheme`'s reason: a
146
+ * frame that boots after the pane's locale was posted (a new host, a reload) would otherwise
147
+ * draw an Arabic document left to right until the locale next CHANGED. Absent or null clears
148
+ * `lang` and `dir`.
149
+ */
150
+ locale?: WireLocale | null;
138
151
  /** The dialog to draw open after this render, for the same reason. */
139
152
  dialogOpen?: (string | number)[] | null;
140
153
  gen: number;
@@ -188,8 +201,10 @@ export type ParentToIframe =
188
201
  * the frame — `localeDirection` is CLDR's answer via `Intl.Locale`, and the frame has no reason
189
202
  * to hold a second copy of the RTL script list.
190
203
  *
191
- * `null` is "the document's own language": both attributes come off, and the frame falls back to
192
- * whatever the rendered document declares.
204
+ * The host posts the language the pane DRAWS, not only an override: the preview choice, else the
205
+ * file's own, else the site default (`canvasLocaleOf`). `null` is a project that declares no
206
+ * locales: both attributes come off, and the frame falls back to whatever the rendered document
207
+ * declares.
193
208
  */
194
209
  | { kind: "setLocale"; locale: string | null; dir: "ltr" | "rtl" }
195
210
  /**
@@ -377,10 +392,14 @@ export type DropInstructionType = "reorder-above" | "reorder-below" | "make-chil
377
392
 
378
393
  /**
379
394
  * The kind of thing being dragged. `tree-node` carries the source's existing document path (a
380
- * move); `block` carries nothing on the wire — its full `fragment` (a {@link JxMutableNode}) is
381
- * retained PARENT-SIDE keyed by `dragSeq` and never crosses the boundary.
395
+ * move); `block` carries only its fragment's tag on the wire — its full `fragment` (a
396
+ * {@link JxMutableNode}) is retained PARENT-SIDE keyed by `dragSeq` and never crosses the boundary.
397
+ * The tag is what lets the iframe keep a block out of a paragraph (`iframe-drop.ts`); an OS file
398
+ * drag has none, and is placed as a block.
382
399
  */
383
- export type DragSrcKind = { type: "tree-node"; path: (string | number)[] } | { type: "block" };
400
+ export type DragSrcKind =
401
+ | { type: "tree-node"; path: (string | number)[] }
402
+ | { type: "block"; tag?: string };
384
403
 
385
404
  /**
386
405
  * A display-only drop preview the iframe posts on `dragOver`: where the drop indicator should draw
@@ -37,7 +37,7 @@ import { classifyRenderNode, jxPathSelector, serializeJxPath } from "./path-mapp
37
37
  import { elementForPath } from "./iframe-position";
38
38
  import type { AssetContext } from "./asset-resolve";
39
39
  import { SITE_STYLE_ID, buildSiteStyleCSS } from "@jxsuite/site/site-style";
40
- import type { CanvasMode, WireDiffMarks } from "./iframe-protocol";
40
+ import type { CanvasMode, WireDiffMarks, WireLocale } from "./iframe-protocol";
41
41
  import { isNonNullObject, isString } from "@jxsuite/schema/guards";
42
42
  import type { JxDocument, JxElement, JxStyle } from "@jxsuite/schema/types";
43
43
  import type { PathMapCtx } from "./path-mapping";
@@ -631,6 +631,10 @@ export const STYLEBOOK_CSS = `
631
631
  .element-card-preview > * {
632
632
  max-width: 100%;
633
633
  margin: 0;
634
+ }
635
+ /* A list keeps its inline-start padding: its markers are drawn in it, and with none they are
636
+ clipped by the card ("1." shows as "."). Every other specimen sits flush in its card. */
637
+ .element-card-preview > :not(ol, ul, menu) {
634
638
  padding: 0;
635
639
  }
636
640
  .element-card-preview > hr {
@@ -728,6 +732,49 @@ export function applyPreviewColorScheme(doc: Document, scheme: "light" | "dark"
728
732
  }
729
733
  }
730
734
 
735
+ /** The `lang` and `dir` a frame's root booted with, which a frame with no language goes back to. */
736
+ export interface LocaleBaseline {
737
+ lang: string | null;
738
+ dir: string | null;
739
+ }
740
+
741
+ /** Read the root's `lang` and `dir` as the frame booted them (`canvas.html` is `lang="en"`). */
742
+ export function captureLocaleBaseline(doc: Document): LocaleBaseline {
743
+ const root = doc.documentElement;
744
+ return { dir: root.getAttribute("dir"), lang: root.getAttribute("lang") };
745
+ }
746
+
747
+ /** Write one root attribute, or remove it for null. */
748
+ function setRootAttribute(root: Element, name: string, value: string | null): void {
749
+ if (value === null) {
750
+ root.removeAttribute(name);
751
+ } else {
752
+ root.setAttribute(name, value);
753
+ }
754
+ }
755
+
756
+ /**
757
+ * Set the artboard's language (studio.md §20.2): `lang` and `dir` on the frame's root, the only
758
+ * writer of either. `dir` is what makes an RTL document actually mirror, and `lang` is what CSS's
759
+ * `:lang()` and the font stack select on. The root survives a render, which replaces only the
760
+ * container's children, so the value holds until the next one arrives.
761
+ *
762
+ * `null`, a project that declares no locales, restores the language the frame booted with rather
763
+ * than removing it, so applying the value on every render does not strip `canvas.html`'s
764
+ * `lang="en"` from every canvas of a monolingual project. A left-to-right language writes no
765
+ * `dir="ltr"`, as the built page never does (`site-architecture.md` §13.4): it restores the booted
766
+ * `dir`.
767
+ */
768
+ export function applyPreviewLocale(
769
+ doc: Document,
770
+ locale: WireLocale | null,
771
+ baseline: LocaleBaseline,
772
+ ): void {
773
+ const root = doc.documentElement;
774
+ setRootAttribute(root, "lang", locale?.locale ?? baseline.lang);
775
+ setRootAttribute(root, "dir", locale?.dir === "rtl" ? "rtl" : baseline.dir);
776
+ }
777
+
731
778
  /**
732
779
  * Draw the popover at `path` open, and every other one closed.
733
780
  *
@@ -15,7 +15,7 @@ import { releaseElementStyles, renderNode, runScoped } from "@jxsuite/runtime";
15
15
  import { isNumber, isString } from "@jxsuite/schema/guards";
16
16
  import { getNodeAtPath } from "../state";
17
17
  import { makeStamper } from "./iframe-render";
18
- import { prepareForEditMode } from "../utils/edit-display";
18
+ import { prepareForEditMode, sampleRowAt } from "../utils/edit-display";
19
19
  import { stripEventHandlers } from "../utils/strip-events";
20
20
  import type { IframeRenderCtx } from "./iframe-render";
21
21
  import type { JxPath } from "../state";
@@ -47,7 +47,10 @@ export function renderSubtreeIframe(
47
47
  if (node === undefined) {
48
48
  throw new Error(`iframe-patch-node-not-found:${docPath.join("/")}`);
49
49
  }
50
- const def = isString(node) ? node : prepareForEditMode(stripEventHandlers(node));
50
+ // Inside a repeater, the template is drawn with the sample row the full render used.
51
+ const def = isString(node)
52
+ ? node
53
+ : prepareForEditMode(stripEventHandlers(node), sampleRowAt(shadowDoc, docPath));
51
54
  const { result: rendered, stop } = runScoped(() =>
52
55
  renderNode(def, ctx.defs, {
53
56
  _path: docPathToRenderPath(docPath, ctx),
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Where a live render imports an `npm:` package from (spec.md §5.3, 4c).
3
+ *
4
+ * The dev server answers `/node_modules/<package>/<subpath>` by resolving the package's `exports`
5
+ * and bundling the entry on demand (`@jxsuite/server`), the same URL space a bare `$elements`
6
+ * specifier and a bare `$head` asset are already fetched from. Both realms that render a project
7
+ * live install it into the runtime (`setNpmModuleResolver`): the canvas frame against its own
8
+ * address, and the editor window, which draws the Site panel's and the Content home's previews,
9
+ * against the project's asset origin.
10
+ *
11
+ * Dependency-free so it rides in the slim canvas bundle.
12
+ */
13
+
14
+ /**
15
+ * The URL a package specifier is imported from, under `base`.
16
+ *
17
+ * @param specifier - `@scope/pkg/subpath`, as the document wrote it after `npm:`
18
+ * @param base - The origin (or any URL on it) that serves the project
19
+ */
20
+ export function npmModuleUrl(specifier: string, base: string): string {
21
+ return new URL(`/node_modules/${specifier}`, base).href;
22
+ }
@@ -24,3 +24,19 @@ export function setPreviewNavigateHandler(next: PreviewNavigateHandler | null):
24
24
  export function getPreviewNavigateHandler(): PreviewNavigateHandler | null {
25
25
  return handler;
26
26
  }
27
+
28
+ /**
29
+ * Hand a URL to the user's real browser.
30
+ *
31
+ * Reuses the seam the desktop launchers already register for Preview link clicks, which routes
32
+ * through the OS rather than navigating a webview with no address bar; the browser build falls back
33
+ * to a new tab. One function, because Open in Browser and the Publish workspace's Visit site and
34
+ * deployment links all mean the same thing by "open this outside Studio".
35
+ */
36
+ export function openUrlExternally(url: string): void {
37
+ if (handler) {
38
+ handler(url);
39
+ return;
40
+ }
41
+ window.open(url, "_blank", "noopener,noreferrer");
42
+ }
@@ -0,0 +1,200 @@
1
+ /**
2
+ * The data a repeater is drawn from in Edit and Design, resolved before the canvas renders.
3
+ *
4
+ * Edit mode draws a repeater as one instance of its row template, with the repeater's first item as
5
+ * that instance's sample row (`utils/edit-display.ts`). A collection is a class-backed state entry
6
+ * (`ContentCollection`) the canvas runtime resolves through the dev proxy AFTER its first render,
7
+ * so the instance connected with an empty row: a component that formats a date or a locale threw,
8
+ * was reported as not rendered, and drew itself only when the data arrived. Resolving the entry
9
+ * here, the way `page-params.ts` bakes a param-bound entry for Preview, puts the row in the state
10
+ * the canvas builds its scope from, so the first render is a render of the data.
11
+ *
12
+ * The same resolution serves every mode for an entry that names an enabled extension's class by a
13
+ * bare `$prototype` ({@link extensionClassKeys}), which the build also resolves before rendering.
14
+ *
15
+ * Only an entry whose configuration holds no template is baked: one that does is re-resolved by the
16
+ * runtime whenever what it reads changes, and a baked copy would freeze it. Answers are cached by
17
+ * the entry's configuration until the project's content changes ({@link
18
+ * invalidateRepeaterSources}).
19
+ *
20
+ * @docs studio/interface/canvas
21
+ */
22
+
23
+ import { isJsonObject, isNodeObject, isRef, isString } from "@jxsuite/schema/guards";
24
+ import { getPlatform } from "../platform";
25
+ import type {
26
+ JsonObject,
27
+ JsonValue,
28
+ JxDocumentValue,
29
+ JxMutableNode,
30
+ JxStateDefinition,
31
+ } from "@jxsuite/schema/types";
32
+
33
+ /** What the backend answered for one entry, or `null` when it could not resolve it. */
34
+ type Resolved = JsonValue | null;
35
+
36
+ /** Resolved entries by their request body, so a re-render does not ask the backend again. */
37
+ let _cache = new Map<string, Promise<Resolved>>();
38
+
39
+ /** Forget every resolved entry; the next render asks the backend again. */
40
+ export function invalidateRepeaterSources(): void {
41
+ _cache = new Map();
42
+ }
43
+
44
+ /** The top-level state key a `#/state/<key>/…` pointer reads, or null for any other pointer. */
45
+ function stateKeyOf(ref: string): string | null {
46
+ if (!ref.startsWith("#/state/")) {
47
+ return null;
48
+ }
49
+ const [key] = ref.slice("#/state/".length).split("/");
50
+ return key ? key.replaceAll("~1", "/").replaceAll("~0", "~") : null;
51
+ }
52
+
53
+ /** Whether a configuration value holds a `${…}` template anywhere inside it. */
54
+ function holdsTemplate(value: JsonValue | undefined): boolean {
55
+ if (isString(value)) {
56
+ return value.includes("${");
57
+ }
58
+ if (Array.isArray(value)) {
59
+ return value.some((member) => holdsTemplate(member));
60
+ }
61
+ return isJsonObject(value) && Object.values(value).some((member) => holdsTemplate(member));
62
+ }
63
+
64
+ /** Extension class names to their descriptor paths, as `getEffectiveImports` merges them. */
65
+ export type ClassPaths = Readonly<Record<string, string>>;
66
+
67
+ /**
68
+ * The `$src` a class-backed state entry resolves from: its own, else the enabled extension class
69
+ * its bare `$prototype` names (spec.md §12.5), else null.
70
+ */
71
+ function classSourceOf(entry: JsonObject, classes: ClassPaths): string | null {
72
+ if (isString(entry.$src)) {
73
+ return entry.$src;
74
+ }
75
+ return isString(entry.$prototype) && Object.hasOwn(classes, entry.$prototype)
76
+ ? (classes[entry.$prototype] ?? null)
77
+ : null;
78
+ }
79
+
80
+ /** Whether a state entry is a class-backed source the backend can resolve once, for good. */
81
+ function isStaticClassEntry(
82
+ entry: JxStateDefinition | undefined,
83
+ classes: ClassPaths = {},
84
+ ): entry is JsonObject {
85
+ return (
86
+ isJsonObject(entry) &&
87
+ isString(entry.$prototype) &&
88
+ classSourceOf(entry, classes) !== null &&
89
+ !holdsTemplate(entry)
90
+ );
91
+ }
92
+
93
+ /**
94
+ * The state keys of `doc` that name an enabled extension's class by a bare `$prototype` (a layout's
95
+ * `{ "$prototype": "ContentEntry", ... }`) and can be resolved once.
96
+ *
97
+ * The build resolves these before any template runs (`compiler/src/site/prototype-resolver.ts`).
98
+ * Left to the canvas runtime, the class's descriptor path, a file on disk, was first fetched as a
99
+ * URL of the canvas, answered 404, and only then asked of the dev proxy: one failed request per
100
+ * entry per render, on every page of a site whose layout reads its navigation this way.
101
+ */
102
+ export function extensionClassKeys(doc: JxMutableNode, classes: ClassPaths): string[] {
103
+ return Object.entries(doc.state ?? {})
104
+ .filter(
105
+ ([, entry]) =>
106
+ isJsonObject(entry) && !isString(entry.$src) && isStaticClassEntry(entry, classes),
107
+ )
108
+ .map(([key]) => key);
109
+ }
110
+
111
+ /**
112
+ * The state keys `doc`'s repeaters read their items from, among them only the class-backed entries
113
+ * {@link bakeRepeaterSources} can resolve.
114
+ */
115
+ export function repeaterSourceKeys(doc: JxMutableNode): string[] {
116
+ const keys = new Set<string>();
117
+ const walk = (value: JxDocumentValue | undefined): void => {
118
+ if (Array.isArray(value)) {
119
+ for (const member of value) {
120
+ walk(member);
121
+ }
122
+ return;
123
+ }
124
+ if (!isNodeObject(value)) {
125
+ return;
126
+ }
127
+ if (value.$prototype === "Array" && isRef(value.items)) {
128
+ const key = stateKeyOf(value.items.$ref);
129
+ if (key) {
130
+ keys.add(key);
131
+ }
132
+ }
133
+ walk(value.map);
134
+ walk(value.children);
135
+ for (const branch of Object.values(value.cases ?? {})) {
136
+ walk(branch);
137
+ }
138
+ };
139
+ walk(doc.children);
140
+ const state = doc.state ?? {};
141
+ return [...keys].filter((key) => isStaticClassEntry(state[key]));
142
+ }
143
+
144
+ /** Say why an entry could not be resolved, and answer that it was not. */
145
+ function unresolved(key: string, error: Error | string): Resolved {
146
+ console.warn(`canvas: could not resolve state.${key} before the render:`, error);
147
+ return null;
148
+ }
149
+
150
+ /**
151
+ * `renderDoc` with each of `keys` replaced by its resolved value, or `renderDoc` itself when there
152
+ * is nothing to resolve. A rebuild, never a write: the render document shares its `state` with the
153
+ * tab's source document. An entry the backend cannot resolve is left as written, and the runtime's
154
+ * own resolution remains the fallback.
155
+ */
156
+ export async function bakeRepeaterSources(
157
+ renderDoc: JxMutableNode,
158
+ keys: readonly string[],
159
+ docBase?: string,
160
+ classes: ClassPaths = {},
161
+ ): Promise<JxMutableNode> {
162
+ const platform = getPlatform();
163
+ const { resolveClass } = platform;
164
+ const source = renderDoc.state;
165
+ if (keys.length === 0 || !resolveClass || !source) {
166
+ return renderDoc;
167
+ }
168
+ const state = { ...source };
169
+ await Promise.all(
170
+ keys.map(async (key) => {
171
+ const entry = state[key];
172
+ if (!isStaticClassEntry(entry, classes)) {
173
+ return;
174
+ }
175
+ const body: JsonObject = { ...entry, $src: classSourceOf(entry, classes) };
176
+ if (docBase) {
177
+ body.$base = docBase;
178
+ }
179
+ const id = JSON.stringify(body);
180
+ let pending = _cache.get(id);
181
+ if (!pending) {
182
+ pending = resolveClass.call(platform, body).then(
183
+ (value): Resolved => value ?? null,
184
+ (error: Error | string) => {
185
+ _cache.delete(id);
186
+ return unresolved(key, error);
187
+ },
188
+ );
189
+ _cache.set(id, pending);
190
+ }
191
+ const value = await pending;
192
+ if (value !== null) {
193
+ /* SAFETY: the backend answers with the class's resolved JSON value (no `$prototype`), which
194
+ the runtime reads as a naked-value state entry, so it is a JxStateDefinition. */
195
+ state[key] = value as JxStateDefinition;
196
+ }
197
+ }),
198
+ );
199
+ return { ...renderDoc, state };
200
+ }
@@ -92,10 +92,14 @@ export function trackStageScrollbar(surface: CanvasSurface, scroller: HTMLElemen
92
92
  const write = () => {
93
93
  cell.style.setProperty(STAGE_SCROLLBAR_VAR, `${scrollbarWidthOf(scroller)}px`);
94
94
  };
95
+ /* No measurement here: observing an element delivers its first notification after the frame's
96
+ layout, so the observer's own first call is the initial write. Reading `offsetWidth` here, on a
97
+ stage the render has just rebuilt, forced a style recalculation of the whole document (24 ms on
98
+ each return to Design in the perf trace); the cell keeps the `0px` the release wrote until
99
+ then, which is what a stage with no scrollbar yet has. */
95
100
  const observer = new ResizeObserver(write);
96
101
  observer.observe(scroller);
97
102
  surface.stageScrollbar = { observer, scroller };
98
- write();
99
103
  }
100
104
 
101
105
  /**
@@ -101,6 +101,12 @@ export interface CanvasSurface {
101
101
  */
102
102
  prevStylebookFilter: string | null;
103
103
  prevStylebookCustomizedOnly: boolean | null;
104
+ /**
105
+ * The breakpoint this stage's catalogue was drawn at. The sheet is ONE artboard at the active
106
+ * breakpoint, so a size switch changes which board exists and must rebuild it, exactly as a
107
+ * filter change does. `undefined` is "never drawn"; `null` is the base.
108
+ */
109
+ prevStylebookMedia?: string | null | undefined;
104
110
  /** Teardown for this stage's source-mode collab binding (unbind + release the lock). */
105
111
  sourceCollabCleanup: (() => void) | null;
106
112
  /**
@@ -54,12 +54,14 @@ import { collabCommands } from "../collab/collab-commands";
54
54
  import { assistantCommands } from "../panels/ai-panel";
55
55
  import { preferencesCommands } from "../settings/preferences-dialog";
56
56
  import { aboutCommands } from "../about/about-modal";
57
- import { libraryCommands } from "../browse/library-commands";
58
- import { sourceControlCommands } from "../panels/git-panel";
57
+ import { contentViewCommands } from "../browse/library-commands";
58
+ import { sourceControlCommands } from "../publish/source-control";
59
59
  import { publishCommands } from "../publish/publish-commands";
60
60
  import { gridViewCommands } from "../grid/grid-panel";
61
61
  import { redirectsCommands } from "../grid/redirects-grid";
62
62
  import { contentCommands } from "../content/entry-commands";
63
+ import { addNewCommands } from "../create/add-new-commands";
64
+ import { mediaCommands } from "../media/media-commands";
63
65
  import { i18nCommands } from "../i18n/i18n-commands";
64
66
  import { newProjectCommands } from "../new-project/new-project-modal";
65
67
  import { canvasCommands } from "../editor/shortcuts";
@@ -67,6 +69,7 @@ import { formatCommands, registerSelectionCommands } from "../panels/block-actio
67
69
  import { fileFormatCommands } from "../format/convert-file";
68
70
  import { registerTabCommands } from "../workspace/workspace";
69
71
  import { derivationCommands, noopDerivationDeps } from "../workspace/pane-derive";
72
+ import { workspaceBackCommands } from "../workspace/workspace-back";
70
73
  import type { AnyCommand } from "./registry";
71
74
 
72
75
  /** A verb set that does nothing — the checks read declarations, never behaviour. */
@@ -111,13 +114,15 @@ function viaRegistration(): AnyCommand[] {
111
114
  */
112
115
  export function appCommandSet(): AnyCommand[] {
113
116
  return [
114
- // The panel roster is the one dependency the checks must supply for real: the ⌘1–8 records are
115
- // Generated from it, so an empty one would hide eight commands (and their chords) from the
116
- // Level check, the chrome budget and the generated keyboard sheet alike.
117
+ // The panel roster is the one dependency the checks must supply for real: the `panel.focus.*`
118
+ // Records are generated from it, so an empty one would hide a command per panel from the level
119
+ // Check, the chrome budget and the generated command sheet alike.
117
120
  ...defaultCommands({ ...noopCommandDeps(), panelRoster: panelFocusRoster() }),
118
121
  ...viaRegistration(),
119
122
  ...derivationCommands(noopDerivationDeps()),
120
123
  ...shellViewCommands({ inspectorTab: () => DEFAULT_INSPECTOR_TAB, setInspectorTab: NO_OP }),
124
+ // Back to a strip-less workspace's home: the pane context bar's Back control, and Escape.
125
+ ...workspaceBackCommands({ closeTab: NO_OP, setCanvasMode: NO_OP }),
121
126
  ...canvasViewCommands({
122
127
  getCanvasMode: () => "design",
123
128
  renderPane: NO_OP,
@@ -150,8 +155,13 @@ export function appCommandSet(): AnyCommand[] {
150
155
  ...assistantCommands(),
151
156
  ...preferencesCommands(),
152
157
  ...aboutCommands(),
153
- ...libraryCommands(),
158
+ // The Content home's verbs: which collection it shows, and that collection's own view.
159
+ ...contentViewCommands(),
154
160
  ...contentCommands(),
161
+ // Add new (`file.new`): the one creation dialog, and its no-dialog form for a named kind.
162
+ ...addNewCommands(),
163
+ // The Media workspace's four verbs: browse, upload, replace and delete (studio.md §22.6).
164
+ ...mediaCommands(),
155
165
  // The four translation verbs. `i18n.switchLocale` is deliberately not among them: it is a
156
166
  // Rendering context, defined in `canvas/canvas-utils.ts` beside the per-tab UI state it writes.
157
167
  ...i18nCommands(),
@@ -15,8 +15,8 @@
15
15
  * region table, because `registerPanel()` did not exist. It does now, so the two `rail/*` rows are
16
16
  * a QUERY over `railDeclarations()` ({@link dockTabs}) and adding a ninth rail panel fails this
17
17
  * check without anyone remembering to update a list. The two rows that remain declared are the two
18
- * docks whose tab sets this module cannot see from a bare Bun process: the Inspector's four come
19
- * from `commands/defaults.ts`'s `INSPECTOR_TABS` (a query in all but name), and the Bottom dock's
18
+ * docks whose tab sets this module cannot see from a bare Bun process: the Inspector's three come
19
+ * from `commands/defaults.ts`'s `INSPECTOR_STRIP` (a query in all but name), and the Bottom dock's
20
20
  * four are records in `panels/bottom-dock.ts`, which renders lit templates and therefore cannot be
21
21
  * imported here. `tests/bottom-dock.test.ts` asserts the declared row and the registered set are
22
22
  * the same four titles, so the copy cannot drift — the same bargain `tests/right-panel.test.ts`
@@ -24,7 +24,7 @@
24
24
  * joins them the way it already joins the rail.
25
25
  */
26
26
 
27
- import { INSPECTOR_TABS } from "./defaults";
27
+ import { INSPECTOR_STRIP } from "./defaults";
28
28
 
29
29
  /** The caps. Raising either one is a design decision, so it happens here, in one place. */
30
30
  export const CHROME_BUDGET = {
@@ -65,10 +65,12 @@ export interface DockDeclaration {
65
65
  /**
66
66
  * The tabbed regions that are still WRITTEN DOWN rather than observed.
67
67
  *
68
- * The Inspector's row is a query in all but name — `INSPECTOR_TABS` is the list `right-panel.ts`
69
- * renders and `⌘⇧1–4` address, so a fifth inspector tab fails this check in the commit that adds
70
- * it. The Bottom dock's row is the same shape: the four ids live in `shell.ts`'s `BOTTOM_TAB_IDS`
71
- * (so `view.setBottomTab`'s enum can be read in a bare Bun process) and the four RECORDS live in
68
+ * The Inspector's row is a query in all but name — `INSPECTOR_STRIP` is the strip `right-panel.ts`
69
+ * renders (Page · Element · Assistant), so a fifth strip tab fails this check in the commit that
70
+ * adds it. Element's Content · Style · Logic sub-switch lives inside a tab's body and is not
71
+ * counted: it is three views of one target, not three places (`studio-ui-guidelines.md` §12.2). The
72
+ * Bottom dock's row is the same shape: the four ids live in `shell.ts`'s `BOTTOM_TAB_IDS` (so
73
+ * `view.setBottomTab`'s enum can be read in a bare Bun process) and the four RECORDS live in
72
74
  * `panels/bottom-dock.ts`. It inherited this cap before it existed, which is why Deploy folded into
73
75
  * Activity rather than becoming a fifth tab.
74
76
  *
@@ -78,7 +80,7 @@ export interface DockDeclaration {
78
80
  * those could disagree.
79
81
  */
80
82
  export const DECLARED_DOCK_TABS: readonly DockDeclaration[] = [
81
- { dock: "inspector", tabs: INSPECTOR_TABS.map((tab) => tab.title) },
83
+ { dock: "inspector", tabs: INSPECTOR_STRIP.map((tab) => tab.title) },
82
84
  { dock: "bottom", tabs: ["Problems", "Logic", "Activity"] },
83
85
  ];
84
86