@jxsuite/studio 6.1.2 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
@@ -0,0 +1,80 @@
1
+ /// <reference lib="dom" />
2
+ /**
3
+ * The writing-mode banner surface: one line over the canvas while a collection entry is written
4
+ * inside its template's layout (`specs/studio.md` §22.5).
5
+ *
6
+ * This is the adapter. `content/writing-banner.ts` decides what the line says and where its button
7
+ * goes; this module writes the projection into a scope and mounts `writing-banner.json`.
8
+ *
9
+ * @docs studio/projects/content-types
10
+ */
11
+
12
+ import { reactive } from "../reactivity";
13
+ import { mountSurface, registerSurface } from "../ui/surface";
14
+ import writingBannerDoc from "./writing-banner.json";
15
+ import type { SurfaceHandle } from "../ui/surface";
16
+
17
+ registerSurface("writing-banner", writingBannerDoc);
18
+
19
+ /** What the banner draws. */
20
+ export interface WritingBannerView {
21
+ /** The region id stamped on the root: `pane.<id>/writing-banner`. */
22
+ region: string;
23
+ text: string;
24
+ /** "Design the post page", or "Edit the Base template in Design" when the entry has no page. */
25
+ actionLabel: string;
26
+ /** Whether there is a template to open at all. */
27
+ actionState: "hidden" | "shown";
28
+ }
29
+
30
+ export interface WritingBannerActions {
31
+ /** Open the entry's template in Design. */
32
+ openTemplate: () => void;
33
+ }
34
+
35
+ export interface WritingBannerHandle {
36
+ readonly ready: Promise<unknown>;
37
+ update: (view: WritingBannerView) => void;
38
+ dispose: () => void;
39
+ }
40
+
41
+ interface WritingBannerScope extends WritingBannerView, WritingBannerActions {}
42
+
43
+ /**
44
+ * Mount the banner into `host`, the empty node the canvas stage drew for it.
45
+ *
46
+ * @param host The stage's `writing-banner` host.
47
+ * @param view What it says.
48
+ * @param actions What its button does.
49
+ */
50
+ export function mountWritingBannerSurface(
51
+ host: HTMLElement,
52
+ view: WritingBannerView,
53
+ actions: WritingBannerActions,
54
+ ): WritingBannerHandle {
55
+ const scope: WritingBannerScope = reactive<WritingBannerScope>({ ...view, ...actions });
56
+ let mounted: SurfaceHandle | null = null;
57
+ let disposed = false;
58
+ const ready = mountSurface("writing-banner", scope, host).then((surface) => {
59
+ if (disposed) {
60
+ surface.dispose();
61
+ } else {
62
+ mounted = surface;
63
+ }
64
+ return surface;
65
+ });
66
+ return {
67
+ dispose() {
68
+ disposed = true;
69
+ mounted?.dispose();
70
+ mounted = null;
71
+ },
72
+ ready,
73
+ update(next) {
74
+ scope.region = next.region;
75
+ scope.text = next.text;
76
+ scope.actionLabel = next.actionLabel;
77
+ scope.actionState = next.actionState;
78
+ },
79
+ };
80
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * The repeaters a file wrote as a node's whole `children`, so a save can write them back that way.
3
+ *
4
+ * A node may hold a repeater two ways: as a member of its children array (`children: [{ $prototype:
5
+ * "Array", … }]`) or as the whole children slot (`children: { $prototype: "Array", … }`). The two
6
+ * render the same, and the editor works in the first form only: `normalizeArrayChildren` rewrites
7
+ * every document a tab loads, because every mutation, path and history checkpoint assumes a
8
+ * children array. Without a record of what the file said, a save then rewrote every bare repeater
9
+ * in it into the member form, so one inserted paragraph produced a diff in a list nobody touched,
10
+ * and undoing every edit still left the file different from the one on disk.
11
+ *
12
+ * **Two keys, either of which is enough.** The pointer of the node that held the repeater survives
13
+ * an edit inside the repeater (a restyled template); the repeater's own JSON survives an edit that
14
+ * moves it (a sibling inserted above an ancestor shifts every pointer below it). A repeater both
15
+ * moved and changed loses both and is written in the member form, which renders the same. Only a
16
+ * children array whose ONLY member is a repeater is unwrapped: a node that has gained a sibling of
17
+ * its repeater can no longer be written the bare way at all.
18
+ *
19
+ * @docs studio/interface/tabs
20
+ */
21
+
22
+ import { isJsonObject, isMappedArray } from "@jxsuite/schema/guards";
23
+ import type { JsonObject, JsonValue, JxMutableNode } from "@jxsuite/schema/types";
24
+ import { normalizeArrayChildren, pointerToken } from "../state";
25
+
26
+ /** What a file said about its bare repeaters, in the normalized document's coordinates. */
27
+ export interface BareRepeaters {
28
+ /** The JSON Pointer of each node whose `children` WAS the repeater. */
29
+ readonly pointers: ReadonlySet<string>;
30
+ /** `JSON.stringify` of each such repeater as it was read. */
31
+ readonly fingerprints: ReadonlySet<string>;
32
+ }
33
+
34
+ /**
35
+ * Normalize a freshly read document in place (`normalizeArrayChildren`) and return what it rewrote,
36
+ * or null when the file held no bare repeater — the common case, which needs no record.
37
+ */
38
+ export function normalizeDocument(document: JxMutableNode): BareRepeaters | null {
39
+ const pointers = new Set<string>();
40
+ const fingerprints = new Set<string>();
41
+ normalizeArrayChildren(document, (pointer, repeater) => {
42
+ pointers.add(pointer);
43
+ fingerprints.add(JSON.stringify(repeater));
44
+ });
45
+ return pointers.size === 0 ? null : { fingerprints, pointers };
46
+ }
47
+
48
+ /**
49
+ * The document as the file should hold it: every single-member children array whose repeater the
50
+ * record names, written back as the node's whole `children`. Copy-on-write, so the tab's document
51
+ * (a reactive tree the editor owns) is never touched, and a document with nothing to restore is
52
+ * returned as it is.
53
+ */
54
+ export function restoreBareRepeaters(
55
+ document: JxMutableNode,
56
+ record: BareRepeaters | null,
57
+ ): JxMutableNode {
58
+ if (!record) {
59
+ return document;
60
+ }
61
+ /* SAFETY: a Jx document is JSON (it is read from, and written to, a JSON file); the restored copy
62
+ is the same node with some `children` slots holding their sole repeater directly — the bare
63
+ form the node's schema allows. */
64
+ return restoreValue(document as JsonValue, "", record) as JxMutableNode;
65
+ }
66
+
67
+ /** Restore inside one value of the tree, returning it unchanged when nothing below it moved. */
68
+ function restoreValue(value: JsonValue, pointer: string, record: BareRepeaters): JsonValue {
69
+ if (Array.isArray(value)) {
70
+ let copy: JsonValue[] | null = null;
71
+ for (const [i, item] of value.entries()) {
72
+ const next = restoreValue(item, `${pointer}/${i}`, record);
73
+ if (next !== item) {
74
+ copy ??= [...value];
75
+ copy[i] = next;
76
+ }
77
+ }
78
+ return copy ?? value;
79
+ }
80
+ if (!isJsonObject(value)) {
81
+ return value;
82
+ }
83
+ let copy: JsonObject | null = null;
84
+ for (const [key, child] of Object.entries(value)) {
85
+ let next = restoreValue(child, `${pointer}/${pointerToken(key)}`, record);
86
+ if (key === "children" && Array.isArray(next) && next.length === 1) {
87
+ const [only] = next;
88
+ if (
89
+ isMappedArray(only) &&
90
+ (record.pointers.has(pointer) || record.fingerprints.has(JSON.stringify(only)))
91
+ ) {
92
+ next = only;
93
+ }
94
+ }
95
+ if (next !== child) {
96
+ copy ??= { ...value };
97
+ copy[key] = next;
98
+ }
99
+ }
100
+ return copy ?? value;
101
+ }
package/src/tabs/tab.ts CHANGED
@@ -2,7 +2,8 @@
2
2
  import { effectScope, reactive } from "../reactivity";
3
3
  import { formatByName, formatForPath } from "../format/format-host";
4
4
  import type { FormatFrontmatter } from "../format/format-host";
5
- import { normalizeArrayChildren } from "../state";
5
+ import { normalizeDocument } from "./bare-repeaters";
6
+ import type { BareRepeaters } from "./bare-repeaters";
6
7
  import type { JxPath } from "../state";
7
8
  import type { FormulaEditDef, FunctionEditDef, InlineEditDef, JsonValue } from "../types";
8
9
  import { editorKindForMode } from "../commands/context";
@@ -15,13 +16,20 @@ import type { JsonLayout } from "@jxsuite/schema/json-layout";
15
16
  * The project's configuration file, project-relative.
16
17
  *
17
18
  * Named here rather than at the four places that used to spell it, because it is a DOCUMENT PATH —
18
- * `inferModes` answers `["stylebook", "source"]` for it (editor kind `config`), and
19
- * `tabs/project-config.ts` binds the tab at this path as the configuration document.
19
+ * `inferModes` answers `["stylebook", "tokens", "source"]` for it (the Styles workspace's Elements,
20
+ * Variables and Code views; editor kind `config`), and `tabs/project-config.ts` binds the tab at
21
+ * this path as the configuration document.
20
22
  */
21
23
  export const PROJECT_CONFIG_PATH = "project.json";
22
24
 
23
25
  export interface TabUi {
26
+ /** The Inspector leaf showing (`shell.ts`'s `INSPECTOR_TAB_IDS`); Page on a fresh tab (§6). */
24
27
  rightTab: string;
28
+ /**
29
+ * The Element leaf last shown — Content, Style or Logic — so the selection-driven switch from
30
+ * Page returns to the view the author was using rather than always to Content (§6).
31
+ */
32
+ elementTab: string;
25
33
  canvasMode: string;
26
34
  /** Preview toggle — composes with an edit/design canvasMode; the effective mode becomes "preview". */
27
35
  preview: boolean;
@@ -198,6 +206,22 @@ export interface Tab {
198
206
  * file.
199
207
  */
200
208
  layout: JsonLayout | null;
209
+ /**
210
+ * The text a format-class file (Markdown) was read as, or null for a JSON document, a new
211
+ * document or a stub. The format's serializer is handed it on save
212
+ * (`files/serialize-document.ts`) so every frontmatter key the author did not edit keeps its
213
+ * own spelling and the body keeps its markers (`@jxsuite/parser` `source-style.ts`). The
214
+ * lifetime is `layout`'s: an edit leaves it alone, and a read from disk, a source-view commit,
215
+ * a peer's text or a save replaces it with the text just read or written.
216
+ */
217
+ source: string | null;
218
+ /**
219
+ * The repeaters the file wrote as a node's whole `children`, which loading rewrote into the
220
+ * member form the editor works in (`tabs/bare-repeaters.ts`); null when it wrote none. A fact
221
+ * about the file with the lifetime `layout` has: a reload or a source-view commit replaces it,
222
+ * an edit or an undo leaves it alone, and a save writes those repeaters back as they were.
223
+ */
224
+ bareRepeaters: BareRepeaters | null;
201
225
  };
202
226
  session: {
203
227
  /**
@@ -267,7 +291,8 @@ function createDefaultUi(canvasMode: string, preview = false): TabUi {
267
291
  previewLocale: null,
268
292
  previewParams: {},
269
293
  previewProps: null,
270
- rightTab: "properties",
294
+ rightTab: "page",
295
+ elementTab: "properties",
271
296
  showLayout: true,
272
297
  styleFilter: "",
273
298
  styleSections: {},
@@ -302,6 +327,7 @@ const ALL_MODES = ["edit", "design", "preview", "source", "stylebook", "git-diff
302
327
  * openedFrom?: TabOrigin | null;
303
328
  * preview?: boolean;
304
329
  * layout?: JsonLayout | null;
330
+ * source?: string | null;
305
331
  * }} opts
306
332
  * @returns {Tab}
307
333
  */
@@ -316,6 +342,8 @@ export function createTab({
316
342
  openedFrom = null,
317
343
  preview: previewTab = false,
318
344
  layout = null,
345
+ source = null,
346
+ canvasMode,
319
347
  }: {
320
348
  id: string;
321
349
  documentPath?: string | null;
@@ -327,28 +355,37 @@ export function createTab({
327
355
  openedFrom?: TabOrigin | null;
328
356
  preview?: boolean;
329
357
  layout?: JsonLayout | null;
358
+ /** The text a format-class file was read as (`doc.source`). */
359
+ source?: string | null;
360
+ /**
361
+ * The mode to open in instead of the first declared one: a comparison opens straight into
362
+ * `git-diff`, so the tab is never active in an edit mode that belongs to another workspace.
363
+ */
364
+ canvasMode?: string | undefined;
330
365
  }) {
331
366
  const scope = effectScope();
332
367
 
333
368
  // Normalize legacy whole-children repeaters to the canonical array-member form before the doc
334
369
  // (and its first history checkpoint) are stored.
335
- normalizeArrayChildren(document);
370
+ const bareRepeaters = normalizeDocument(document);
336
371
 
337
372
  const resolvedModes = capabilities?.modes ?? inferModes(documentPath, sourceFormat);
338
373
  // A tab opens in its first allowed mode — never one the toolbar would disable.
339
374
  // Formats author mode order so the default comes first (edit, stylebook, etc.).
340
375
  // "preview" is a per-tab toggle rather than a base mode: a preview-first format opens
341
376
  // In its first non-preview mode with the toggle already on.
342
- const initialCanvasMode = resolvedModes.find((m) => m !== "preview") ?? "edit";
377
+ const initialCanvasMode = canvasMode ?? resolvedModes.find((m) => m !== "preview") ?? "edit";
343
378
  const initialPreview = resolvedModes[0] === "preview";
344
379
 
345
380
  const doc: Tab["doc"] = {
381
+ bareRepeaters,
346
382
  content: { frontmatter: frontmatter || {} },
347
383
  dirty: false,
348
384
  document,
349
385
  handlersSource: null,
350
386
  layout,
351
387
  mode: inferDocumentMode(documentPath, sourceFormat),
388
+ source,
352
389
  sourceFormat,
353
390
  };
354
391
  const history: Tab["history"] = {
@@ -404,7 +441,10 @@ export function createTab({
404
441
  */
405
442
  function inferModes(documentPath: string | null | undefined, sourceFormat: string | null) {
406
443
  if (documentPath === PROJECT_CONFIG_PATH) {
407
- return ["stylebook", "source"];
444
+ /* Elements, Variables and Code: the Styles workspace's three views. Spelled here rather than
445
+ imported from `style/project-styles` (`STYLES_TAB_MODES`), whose imports reach `store`,
446
+ which imports this module; `tests/project-config.test.ts` holds the two lists equal. */
447
+ return ["stylebook", "tokens", "source"];
408
448
  }
409
449
  const format = formatByName(sourceFormat) ?? formatForPath(documentPath);
410
450
  if (format) {
package/src/types.ts CHANGED
@@ -311,25 +311,9 @@ export interface StudioPlatform {
311
311
  /**
312
312
  * URL of the canvas iframe document. Optional: when set (chromium serves it from the project
313
313
  * server under the studio namespace), the iframe host uses it; otherwise the host falls back to
314
- * the default dev-server path. Not keyed on `id` — chromium and electrobun both report `id:
315
- * "desktop"`, but only chromium sets this.
314
+ * the default dev-server path. Not keyed on `id`.
316
315
  */
317
316
  canvasUrl?: string;
318
- /**
319
- * True when this platform resolves {@link canvasUrl} ASYNCHRONOUSLY.
320
- *
321
- * Electrobun does: the url is this window's loopback port, fetched over RPC inside `activate()`.
322
- * Until the canvas fallback was anchored to the bundle it did not matter — the old literal
323
- * `/packages/studio/canvas.html` resolved to nothing servable under `views://`, so an early frame
324
- * simply failed and the host rebuilt when the real url landed. Now the fallback RESOLVES:
325
- * `views://studio/canvas.html` is a document electrobun really stages. An early frame would boot
326
- * the whole canvas bundle inside the SHELL's app-privileged origin, in a CEF instance running
327
- * `disable-site-isolation-trials` — and the cross-origin loopback canvas exists precisely so that
328
- * cannot happen. Declaring it makes the host wait instead.
329
- *
330
- * Not keyed on `id`: chromium and electrobun both report `"desktop"`, and only electrobun defers.
331
- */
332
- canvasUrlDeferred?: boolean;
333
317
  /**
334
318
  * Base URL the canvas fetches PROJECT FILES from — component `$ref`s, `$src` modules, images.
335
319
  *
@@ -408,12 +392,11 @@ export interface StudioPlatform {
408
392
  * image. So a caller that must DECODE a project file — the image editor — needs this instead.
409
393
  *
410
394
  * **The `<img>` route is not an alternative, on the one platform that matters most.** A preview
411
- * `src` from `files/media-paths.ts` is a cross-origin loopback URL on the electrobun desktop
412
- * while the shell sits on `views://`, so drawing it into a canvas taints the canvas and
413
- * `toBlob()` throws; and `server.md` §4.2 bans CORS outright, because the whole loopback security
414
- * model rests on the browser refusing cross-origin reads. Bytes through the PAL become a
415
- * same-origin `Blob`, so tainting stops being a thing that can happen rather than a thing that is
416
- * worked around.
395
+ * `src` from `files/media-paths.ts` can be a cross-origin loopback URL while the shell sits on
396
+ * another origin, so drawing it into a canvas taints the canvas and `toBlob()` throws; and
397
+ * `server.md` §4.2 bans CORS outright, because the whole loopback security model rests on the
398
+ * browser refusing cross-origin reads. Bytes through the PAL become a same-origin `Blob`, so
399
+ * tainting stops being a thing that can happen rather than a thing that is worked around.
417
400
  *
418
401
  * Optional, and feature-detected like every optional member: a backend without it simply does not
419
402
  * offer the image editor, which is the honest degradation `specs/desktop.md` §3.1 asks for.
@@ -595,6 +578,16 @@ export interface StudioPlatform {
595
578
  gitDiscard: (files: string[]) => Promise<void>;
596
579
  gitClone?: (url: string) => Promise<{ ok: boolean; root: string }>;
597
580
  gitInit: () => Promise<void>;
581
+ /**
582
+ * True on a host whose git is the remote's: a commit lands on GitHub as it is made, a push is
583
+ * only a check that it did, and each branch is a session of its own, so a checkout reloads the
584
+ * window into that branch's session (the cloud). Absent means an ordinary working tree, where a
585
+ * commit is local until it is pushed and a checkout swaps files in place.
586
+ *
587
+ * A declaration rather than a method, because what differs is the meaning of the verbs every host
588
+ * has: the Publish workspace reads it to say what a verb will do (studio.md §22.8).
589
+ */
590
+ gitHosted?: boolean;
598
591
  /**
599
592
  * Build the site to its output directory, so a reader opens what the author is looking at.
600
593
  *
@@ -864,9 +857,9 @@ export interface StudioPlatform {
864
857
  * Watch the backend connection, on platforms whose transport can lose one.
865
858
  *
866
859
  * Only a socket-based launcher implements this: the dev server talks over `fetch` and has nothing
867
- * to lose, and the electrobun launcher's RPC times a request out rather than dropping it. The
868
- * chromium launcher holds one long-lived WebSocket, and when it died every call after it hung
869
- * with no error anywhere — so the transport reports the state, and the studio says it out loud.
860
+ * to lose, The chromium launcher holds one long-lived WebSocket, and when it died every call
861
+ * after it hung with no error anywhere — so the transport reports the state, and the studio says
862
+ * it out loud.
870
863
  *
871
864
  * The handler is called on each transition, never repeatedly for one state.
872
865
  */
@@ -886,6 +879,8 @@ export interface ContentSectionEntry {
886
879
  source?: string;
887
880
  format?: string;
888
881
  schema?: ContentTypeSchema;
882
+ /** The field a file-backed collection's records are identified by (the loader's `idField`). */
883
+ idField?: string;
889
884
  $elements?: unknown[];
890
885
  }
891
886
 
@@ -34,6 +34,7 @@ import {
34
34
  import { mountSchemaBuilderSurface } from "../surfaces/schema-builder";
35
35
  import { mountReferenceFieldSurface } from "../surfaces/reference-field";
36
36
  import { mountSecretFieldSurface } from "../surfaces/secret-field";
37
+ import { mountMediaPicker, unmountMediaPicker } from "./media-picker";
37
38
  import { camelToLabel, toCamelCase } from "../utils/studio-utils";
38
39
 
39
40
  import type { JsonObject, JsonValue } from "@jxsuite/schema/types";
@@ -710,6 +711,44 @@ export const secretControl: SchemaFormMountedControl = { mount: mountSecret };
710
711
 
711
712
  registerFormControl("secret", secretControl);
712
713
 
714
+ // ─── Media control ───────────────────────────────────────────────────────────
715
+
716
+ /**
717
+ * An image field as the media field: the path, its thumbnail, **Upload media** and **Browse
718
+ * media**, exactly as the page's Icon and OG Image rows draw them. The flow is
719
+ * `ui/media-picker.ts`'s, which already makes a repeat mount into the same host an update, so this
720
+ * control only keeps the latest commit hook and clears the field to absent rather than to "".
721
+ *
722
+ * @param {HTMLElement} host - The field's `[part="control-host"]`
723
+ * @param {SchemaFormControlArgs} args - The value and the commit hook
724
+ * @returns {SchemaFormControlHandle}
725
+ */
726
+ function mountMedia(host: HTMLElement, args: SchemaFormControlArgs): SchemaFormControlHandle {
727
+ let latest = args;
728
+ const draw = (): void => {
729
+ mountMediaPicker(
730
+ host,
731
+ latest.key,
732
+ isString(latest.value) ? latest.value : "",
733
+ (value: string) => latest.onChange(value === "" ? undefined : value),
734
+ { images: true },
735
+ );
736
+ };
737
+ draw();
738
+ return {
739
+ dispose: () => unmountMediaPicker(host),
740
+ update(next) {
741
+ latest = next;
742
+ draw();
743
+ },
744
+ };
745
+ }
746
+
747
+ /** The media control as the registry holds it, named so a test can drive it as the engine does. */
748
+ export const mediaControl: SchemaFormMountedControl = { mount: mountMedia };
749
+
750
+ registerFormControl("media", mediaControl);
751
+
713
752
  // ─── Reference control ───────────────────────────────────────────────────────
714
753
 
715
754
  /**
@@ -804,13 +843,13 @@ function entryIdsFor(collection: string): Promise<string[]> {
804
843
  * `null` means the read is still in flight, and `onSettled` is called when it lands — which is the
805
844
  * second frame a document has to be GIVEN, since nothing repaints it on the read's behalf.
806
845
  *
807
- * Exported because two surfaces draw the choices and neither is this control: the Document Header
808
- * card and the Navigator's Page panel project a `$ref` field as a picker row of their own
809
- * (`panels/frontmatter-fields.ts`), and what they need is not the widget but the answer behind it.
810
- * Taking it from here rather than calling `listCollectionEntryIds` directly is what keeps ONE cache
811
- * and ONE invalidation: a collection listed for the Document Header card is not listed again for
812
- * the entry editor, and {@link invalidateReferenceEntries} after an entry is created is still the
813
- * single event that forgets it.
846
+ * Exported because a surface draws the choices and is not this control: the Inspector's Page tab
847
+ * projects a `$ref` field as a picker row of its own (`panels/frontmatter-fields.ts`), and what it
848
+ * needs is not the widget but the answer behind it. Taking it from here rather than calling
849
+ * `listCollectionEntryIds` directly is what keeps ONE cache and ONE invalidation: a collection
850
+ * listed for the Page tab is not listed again for the entry editor, and
851
+ * {@link invalidateReferenceEntries} after an entry is created is still the single event that
852
+ * forgets it.
814
853
  *
815
854
  * @param {string} collection
816
855
  * @param {() => void} [onSettled]
@@ -990,4 +1029,4 @@ export const referenceControl: SchemaFormMountedControl = { mount: mountReferenc
990
1029
  registerFormControl("reference", referenceControl);
991
1030
 
992
1031
  // Keep an explicit export so hosts can assert the built-ins module loaded
993
- export const builtinFormControls = ["schema-builder", "secret", "reference"] as const;
1032
+ export const builtinFormControls = ["schema-builder", "secret", "reference", "media"] as const;
package/src/ui/layers.ts CHANGED
@@ -99,6 +99,19 @@ export function isModalOpen(): boolean {
99
99
  return Boolean(_dialogLayer?.querySelector(UNDERLAID) || _modalLayer?.querySelector(UNDERLAID));
100
100
  }
101
101
 
102
+ /**
103
+ * Whether a popover is showing anywhere in the chrome: a kit `jx-popover`, a menu, any element in
104
+ * the platform's popover top layer.
105
+ *
106
+ * A popover leaves Escape to the platform, which closes it as the keystroke's default action, so an
107
+ * app chord on bare Escape has to stand down while one is up: cancelling the keystroke would cancel
108
+ * the close as well, and whatever the chord did would happen under a popover that stays open. Asked
109
+ * of the live DOM for the reason {@link isModalOpen} is.
110
+ */
111
+ export function isPopoverOpen(): boolean {
112
+ return document.querySelector(":popover-open") !== null;
113
+ }
114
+
102
115
  /*
103
116
  * `showDialog` was here, with `openOverlaySlot`, `focusOverlay`, `focusablesIn` and
104
117
  * `BODY_FOCUSABLE` under it — and all five are GONE together.