@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,121 @@
1
+ /**
2
+ * What makes a component's tag name usable, decided once.
3
+ *
4
+ * Two rules used to answer this and they disagreed: Convert to Component accepted
5
+ * `/^[a-z][a-z0-9]*(-[a-z0-9]+)+$/`, which refused `x-card.v2` and `my_el-x`, and the Library's
6
+ * component seed accepted `/^[a-z][\d.a-z]*-[\d.a-z-]*$/`, which allowed a trailing hyphen the
7
+ * first refused. Neither was the rule the browser applies, which is the only one that matters: a
8
+ * definition whose tag `customElements.define` rejects is a component nothing can register. So this
9
+ * module states HTML's own production — the [valid custom element
10
+ * name](https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name) —
11
+ * and every creation flow asks it: Add new (`create/add-new.ts`) and Convert to Component
12
+ * (`editor/convert-to-component.ts`).
13
+ *
14
+ * The DERIVER is the other half. A name the reader typed as a title ("Pricing table") becomes a tag
15
+ * (`pricing-table`), and a name with no hyphen gains a `site-` prefix rather than being refused,
16
+ * because "Hero" is a perfectly good thing to call a component and a refusal there teaches nothing
17
+ * but the syntax of a rule the app can satisfy itself (`specs/studio.md` §9.1.1).
18
+ *
19
+ * DOM-free, so the command records and the pure derivation can import it in a bare Bun process.
20
+ *
21
+ * @docs studio/design/components
22
+ */
23
+
24
+ import { slugify } from "../files/new-file-name";
25
+
26
+ /** The prefix a hyphenless slug gains, so the derived tag is a valid custom element name. */
27
+ export const TAG_PREFIX = "site-";
28
+
29
+ /**
30
+ * Names HTML reserves: they match the production but are SVG and MathML elements that predate it,
31
+ * so `customElements.define` refuses each one.
32
+ */
33
+ export const RESERVED_ELEMENT_NAMES: ReadonlySet<string> = new Set([
34
+ "annotation-xml",
35
+ "color-profile",
36
+ "font-face",
37
+ "font-face-format",
38
+ "font-face-name",
39
+ "font-face-src",
40
+ "font-face-uri",
41
+ "missing-glyph",
42
+ ]);
43
+
44
+ /**
45
+ * HTML's `PCENChar`: the characters a potential custom element name may hold after its first
46
+ * letter. ASCII upper case is deliberately absent, which is why a tag name is lower case.
47
+ */
48
+ const PCEN_RANGES: readonly (readonly [number, number])[] = [
49
+ [0xb7, 0xb7],
50
+ [0xc0, 0xd6],
51
+ [0xd8, 0xf6],
52
+ [0xf8, 0x3_7d],
53
+ [0x3_7f, 0x1f_ff],
54
+ [0x20_0c, 0x20_0d],
55
+ [0x20_3f, 0x20_40],
56
+ [0x20_70, 0x21_8f],
57
+ [0x2c_00, 0x2f_ef],
58
+ [0x30_01, 0xd7_ff],
59
+ [0xf9_00, 0xfd_cf],
60
+ [0xfd_f0, 0xff_fd],
61
+ [0x1_00_00, 0xe_ff_ff],
62
+ ];
63
+
64
+ /** A code point as a `u`-flag regular expression escape, so no invisible character is in source. */
65
+ function escapeCodePoint(cp: number): string {
66
+ return `${String.fromCodePoint(92)}u{${cp.toString(16)}}`;
67
+ }
68
+
69
+ /** The whole class body: ASCII first, then each range by its escaped ends. */
70
+ const PCEN_CHAR = [
71
+ String.raw`\-.0-9_a-z`,
72
+ ...PCEN_RANGES.map(([from, to]) => `${escapeCodePoint(from)}-${escapeCodePoint(to)}`),
73
+ ].join("");
74
+
75
+ /** One character that may appear after the first letter. */
76
+ const ONE_PCEN_CHAR = new RegExp(`^[${PCEN_CHAR}]$`, "u");
77
+
78
+ /**
79
+ * The sentence a tag name earns, or `""` when it is valid.
80
+ *
81
+ * Each sentence names the one thing to change, in the order a reader fixes them: say something,
82
+ * start with a letter, lose the character the browser refuses, add the hyphen, avoid a reserved
83
+ * name.
84
+ */
85
+ export function customElementNameProblem(name: string): string {
86
+ if (name === "") {
87
+ return "Enter a tag name.";
88
+ }
89
+ if (!/^[a-z]/.test(name)) {
90
+ return "A tag name starts with a lower-case letter.";
91
+ }
92
+ for (const char of name.slice(1)) {
93
+ if (!ONE_PCEN_CHAR.test(char)) {
94
+ return /[A-Z]/.test(char)
95
+ ? "A tag name is lower case."
96
+ : `“${char}” cannot appear in a tag name.`;
97
+ }
98
+ }
99
+ if (!name.includes("-")) {
100
+ return "A tag name needs a hyphen, such as my-card.";
101
+ }
102
+ if (RESERVED_ELEMENT_NAMES.has(name)) {
103
+ return `<${name}> is reserved by HTML.`;
104
+ }
105
+ return "";
106
+ }
107
+
108
+ /**
109
+ * The tag a display name derives to: slugified, its hyphens tidied, and prefixed with
110
+ * {@link TAG_PREFIX} when it would otherwise have no hyphen or would start with a digit.
111
+ *
112
+ * `"Pricing table"` → `pricing-table`, `"Hero"` → `site-hero`, `"2 columns"` → `site-2-columns`,
113
+ * and a name with nothing usable in it → `""`, which {@link customElementNameProblem} refuses.
114
+ */
115
+ export function deriveTagName(input: string): string {
116
+ const slug = slugify(input).replaceAll(/-+/g, "-").replaceAll(/^-|-$/g, "");
117
+ if (slug === "") {
118
+ return "";
119
+ }
120
+ return /^[a-z]/.test(slug) && slug.includes("-") ? slug : `${TAG_PREFIX}${slug}`;
121
+ }
@@ -12,8 +12,11 @@
12
12
  * was fixed.
13
13
  *
14
14
  * What is left is the flow, which is all this module ever owned that mattered: what the default
15
- * name is, what makes a name usable, whether the document actually took the reference, and what is
16
- * written to disk once it has.
15
+ * name is, whether the document actually took the reference, and what is written to disk once it
16
+ * has. What makes a name usable is no longer this module's: it is HTML's valid custom element name,
17
+ * stated once in `create/element-name.ts` and asked by Add new's Component kind too, which runs
18
+ * {@link convertSelectionToComponent} for **Start from: Current selection** (`specs/studio.md`
19
+ * §9.1.1).
17
20
  *
18
21
  * @docs studio/design/components
19
22
  */
@@ -31,33 +34,62 @@ import { serializeJson } from "@jxsuite/schema/json-layout";
31
34
  import { notify } from "../services/notify";
32
35
  import { showPromptDialog } from "../ui/layers";
33
36
  import { validateComponentSlots } from "../services/cem-export";
37
+ import { customElementNameProblem, deriveTagName } from "../create/element-name";
34
38
 
35
- import type { JxMutableNode } from "@jxsuite/schema/types";
39
+ import type { JxMutableNode, JxPath } from "@jxsuite/schema/types";
36
40
 
37
- const VALID_NAME = /^[a-z][a-z0-9]*(-[a-z0-9]+)+$/;
38
-
39
- /** Convert the currently selected element into a reusable component. */
40
- export async function convertToComponent() {
41
+ /**
42
+ * The selected element a conversion would lift out, or `null` when there is none to convert.
43
+ *
44
+ * The root is not convertible (it is the document, not an element in it), and neither is a
45
+ * selection the document cannot resolve to a node with a tag. Exported so Add new can say WHY its
46
+ * Current selection choice is disabled rather than offering a button that does nothing.
47
+ */
48
+ export function convertibleSelection(): { path: JxPath; node: JxMutableNode } | null {
41
49
  const tab = activeTab.value;
42
50
  const selected = primarySelection(tab?.session.selection);
43
51
  if (!tab || !selected || selected.length < 2) {
44
- return;
52
+ return null;
45
53
  }
46
-
47
54
  const node = getNodeAtPath(tab.doc.document, selected);
48
55
  if (!node || !node.tagName) {
49
- return;
56
+ return null;
50
57
  }
58
+ return { node, path: selected };
59
+ }
51
60
 
52
- const defaultName = deriveDefaultName(node);
53
- const chosen = await promptComponentName(defaultName);
61
+ /** Convert the currently selected element into a reusable component. */
62
+ export async function convertToComponent() {
63
+ const subject = convertibleSelection();
64
+ if (!subject) {
65
+ return;
66
+ }
67
+ const chosen = await promptComponentName(deriveDefaultName(subject.node));
54
68
  if (!chosen) {
55
69
  return;
56
70
  }
57
71
  /* The prompt resolves the TRIMMED value; the case fold is this module's, because it is the case
58
72
  fold `validateName` already judged the name in. A tag name is lower-case by the same rule that
59
73
  made `Hero-Block` default to `hero-block`. */
60
- const name = chosen.toLowerCase();
74
+ await convertSelectionToComponent(chosen.toLowerCase());
75
+ }
76
+
77
+ /**
78
+ * Replace the selected element with an instance of a new component named `name`, and write the
79
+ * component's definition to `components/<name>.json`.
80
+ *
81
+ * The caller has validated `name` (`create/element-name.ts`). The reference goes into the document
82
+ * FIRST, in one transaction, and the file is written only if that transaction landed — see below.
83
+ *
84
+ * @returns The component file's path, or `null` when nothing was converted or the write failed.
85
+ */
86
+ export async function convertSelectionToComponent(name: string): Promise<string | null> {
87
+ const tab = activeTab.value;
88
+ const subject = convertibleSelection();
89
+ if (!tab || !subject) {
90
+ return null;
91
+ }
92
+ const { node, path: selected } = subject;
61
93
 
62
94
  // Extract component definition
63
95
  const componentDef = extractComponentDef(node);
@@ -107,7 +139,7 @@ export async function convertToComponent() {
107
139
  document the author was told had been changed. The gate raises its own toast saying why the
108
140
  edit is paused; the honest thing to add to it is nothing at all. */
109
141
  if (!converted) {
110
- return;
142
+ return null;
111
143
  }
112
144
 
113
145
  // Write component file and refresh registry
@@ -130,27 +162,31 @@ export async function convertToComponent() {
130
162
  tier: "problem",
131
163
  });
132
164
  }
165
+ return componentFile;
133
166
  } catch (error) {
134
167
  notify.error(`Could not save the <${name}> component.`, {
135
168
  detail: errorMessage(error),
136
169
  path: componentFile,
137
170
  source: "Components",
138
171
  });
172
+ return null;
139
173
  }
140
174
  }
141
175
 
142
176
  /**
143
- * Derive a default tag name from a node.
144
- *
145
- * @param {JxMutableNode} node
146
- * @returns {string}
177
+ * The default tag name for a node: its `$id` when it has one, else its tag, through the shared
178
+ * deriver — so `$id: "Hero"` proposes `site-hero` and a `<section>` proposes `site-section`, the
179
+ * same answer Add new gives a component typed as "Hero".
147
180
  */
148
181
  function deriveDefaultName(node: JxMutableNode) {
149
- if (node.$id && node.$id.includes("-")) {
150
- return node.$id.toLowerCase();
182
+ if (node.$id) {
183
+ const fromId = deriveTagName(node.$id);
184
+ if (fromId !== "") {
185
+ return fromId;
186
+ }
151
187
  }
152
188
  const tag = (displayTagName(node.tagName) || "div").toLowerCase();
153
- return tag.includes("-") ? tag : `jx-${tag}`;
189
+ return tag.includes("-") ? tag : deriveTagName(tag);
154
190
  }
155
191
 
156
192
  /**
@@ -170,30 +206,18 @@ function extractComponentDef(node: JxMutableNode) {
170
206
  }
171
207
 
172
208
  /**
173
- * Validate a component name against naming rules and existing registry.
174
- *
175
- * @param {string} val
176
- * @returns {{ valid: boolean; error: string }}
209
+ * The refusal a component name earns, or `""`: HTML's valid custom element name, then uniqueness
210
+ * against the registry.
177
211
  */
178
- function validateName(val: string) {
212
+ function validateName(val: string): string {
179
213
  const name = val.trim().toLowerCase();
180
- if (!name.includes("-")) {
181
- return {
182
- error: "Name must contain a hyphen (e.g. my-component)",
183
- valid: false,
184
- };
185
- }
186
- if (!VALID_NAME.test(name)) {
187
- return {
188
- error: "Lowercase letters, digits, and hyphens only",
189
- valid: false,
190
- };
191
- }
192
- const exists = componentRegistry.some((c) => c.tagName === name);
193
- if (exists) {
194
- return { error: `Component <${name}> already exists`, valid: false };
214
+ const problem = customElementNameProblem(name);
215
+ if (problem !== "") {
216
+ return problem;
195
217
  }
196
- return { error: "", valid: true };
218
+ return componentRegistry.some((c) => c.tagName === name)
219
+ ? `Component <${name}> already exists`
220
+ : "";
197
221
  }
198
222
 
199
223
  /**
@@ -212,7 +236,7 @@ function promptComponentName(defaultName: string): Promise<string | null> {
212
236
  confirmLabel: "Convert",
213
237
  message: "Enter a hyphenated tag name for the new component.",
214
238
  placeholder: "my-component",
215
- validate: (value) => validateName(value).error,
239
+ validate: validateName,
216
240
  value: defaultName,
217
241
  });
218
242
  }
@@ -83,11 +83,19 @@ export function resolveFileDropTarget(
83
83
  * The node an uploaded asset becomes when inserted. Images get a plain `<img>` (the build's
84
84
  * optimizer picks it up); playable media gets native controls so it is usable straight away;
85
85
  * anything else becomes a download link, which is the only sensible generic rendering.
86
+ *
87
+ * **An image arrives with no `alt` at all, never an empty one.** `alt=""` is a claim (the image is
88
+ * decorative, and a screen reader skips it), and a drop cannot know it: stamping it made every
89
+ * dropped picture invisible to assistive technology and silenced the accessibility report's
90
+ * `img-alt-missing`, the one check that would have asked. Leaving it out is what gets the image
91
+ * flagged, and the drop selects the new element (`applyDropInstruction`), so the Inspector is
92
+ * already on the field the author has to fill. Neither is the file name a description: `IMG_0412`
93
+ * read aloud is noise, not an alternative.
86
94
  */
87
95
  export function elementForAsset(asset: UploadedAsset): JxMutableNode {
88
96
  switch (asset.kind) {
89
97
  case "image": {
90
- return { attributes: { alt: "", src: asset.ref }, tagName: "img" };
98
+ return { attributes: { src: asset.ref }, tagName: "img" };
91
99
  }
92
100
  case "video": {
93
101
  return { attributes: { controls: "", src: asset.ref }, tagName: "video" };
@@ -20,6 +20,7 @@ import {
20
20
  transactDoc,
21
21
  } from "../tabs/transact";
22
22
  import { defaultDef } from "../panels/shared";
23
+ import { componentInstance, mutateImportComponent } from "./insert-component";
23
24
 
24
25
  import { contentLength, contentOf, spliceAcross, toStored } from "./content-slice";
25
26
  import type { JxContentResult, SlashCommand } from "./inline-edit";
@@ -276,6 +277,45 @@ export function applyInlineSplit(
276
277
  return newPath;
277
278
  }
278
279
 
280
+ /**
281
+ * A slash pick from the Components group (§5.7): the empty block the "/" was typed in is REPLACED
282
+ * by an instance of the component, and a block with content keeps it and gets the instance after
283
+ * it. Either way the component is imported, as a drop of the same component would import it. An
284
+ * instance is a whole element with its own `$props` and slot content, so swapping a tag in place —
285
+ * what an HTML pick does — would leave a paragraph's leftovers inside a component.
286
+ */
287
+ function insertComponentBlock(
288
+ tab: Tab | null,
289
+ path: JxPath,
290
+ instance: JxMutableNode,
291
+ commitData: JxContentResult | undefined,
292
+ ): JxPath {
293
+ const { parentPath, index: idx } = blockSlot(path);
294
+ const replace = isEmptyContent(commitData);
295
+ const at = replace ? idx : idx + 1;
296
+ const newPath = [...parentPath, "children", at];
297
+ if (!tab) {
298
+ return newPath;
299
+ }
300
+ transactDoc(tab, (t) => {
301
+ if (replace) {
302
+ mutateRemoveNode(t, path);
303
+ } else if (commitData?.children) {
304
+ mutateUpdateProperty(t, path, "textContent");
305
+ mutateUpdateProperty(t, path, "children", commitData.children);
306
+ } else if (commitData?.textContent != null) {
307
+ mutateUpdateProperty(t, path, "children");
308
+ mutateUpdateProperty(t, path, "textContent", commitData.textContent);
309
+ }
310
+ mutateInsertNode(t, parentPath, at, instance);
311
+ mutateImportComponent(t, instance.tagName);
312
+ if (isTabActive(tab)) {
313
+ t.session.selection = [newPath];
314
+ }
315
+ });
316
+ return newPath;
317
+ }
318
+
279
319
  /**
280
320
  * Apply a slash-insert at `path` of `tab`'s document: swap the (empty) node's tag in place, or
281
321
  * commit pending content and insert a new element after it. Returns the path to edit next (the
@@ -287,6 +327,10 @@ export function applyInlineInsert(
287
327
  cmd: SlashCommand,
288
328
  commitData: JxContentResult | undefined,
289
329
  ): JxPath {
330
+ const instance = cmd.component === true ? componentInstance(cmd.tag) : null;
331
+ if (instance) {
332
+ return insertComponentBlock(tab, path, instance, commitData);
333
+ }
290
334
  if (isEmptyContent(commitData)) {
291
335
  if (!tab) {
292
336
  return path;
@@ -81,6 +81,16 @@ export interface SlashCommand {
81
81
  label: string;
82
82
  tag: string;
83
83
  description: string;
84
+ /**
85
+ * The heading the offer is listed under. Absent for the HTML elements, which are the menu's
86
+ * first, unheaded group; `"Components"` for the project's components (§5.7).
87
+ */
88
+ group?: string;
89
+ /**
90
+ * Whether `tag` names a project component: the pick inserts `buildComponentInstance` and imports
91
+ * the component (`editor/insert-component.ts`) rather than an element's default definition.
92
+ */
93
+ component?: boolean;
84
94
  }
85
95
 
86
96
  export interface JxContentResult {
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Inserting a component has one meaning everywhere (`specs/studio.md` §5.7, §9.1.3).
3
+ *
4
+ * A component instance is only a usable element in a document that imports it: the canvas drop did
5
+ * that, and the Insert panel's click, the slash menu and the canvas `+` did not, so a component
6
+ * clicked in rendered as an element the page could not resolve. This module is the one rule the
7
+ * four doors share — the instance comes from `buildComponentInstance`, and the import from the one
8
+ * `$elements` service, `files/elements.ts`'s `enableElement`.
9
+ *
10
+ * @docs studio/design/components
11
+ */
12
+
13
+ import { isString } from "@jxsuite/schema/guards";
14
+ import { buildComponentInstance, componentRegistry } from "../files/components";
15
+ import { enableElement } from "../files/elements";
16
+ import { mutateUpdateProperty } from "../tabs/transact";
17
+ import type { JxNodeValue } from "../tabs/transact";
18
+ import type { ElementsEntry } from "../files/elements";
19
+ import type { JxMutableNode } from "@jxsuite/schema/types";
20
+ import type { Tab } from "../tabs/tab";
21
+ import type { SlashCommand } from "./inline-edit";
22
+
23
+ /** The slash menu's group heading for the project's components. */
24
+ export const COMPONENTS_GROUP = "Components";
25
+
26
+ /**
27
+ * Import the component a node is an instance of into `tab`'s document, when it is one and is not
28
+ * imported yet. A node whose tag has no hyphen is an HTML element and is left alone, and a
29
+ * component the registry cannot name (an npm entry with no package, a local one with no path)
30
+ * changes nothing, so a document that had no `$elements` is not handed an empty one.
31
+ *
32
+ * A computed tag (`{ $expression }`) names no registered component, so it is left alone too.
33
+ *
34
+ * This is a MUTATION: call it inside the transaction that inserts the instance, so the instance and
35
+ * its import are one undo step. As a transaction of its own after the insert, the first ⌘Z took
36
+ * back only the import and left a document using a component it no longer imported, which saves as
37
+ * an instance the page cannot resolve.
38
+ *
39
+ * @param tab The document the node was inserted into, inside a `transactDoc`.
40
+ * @param tagName The inserted node's tag.
41
+ */
42
+ export function mutateImportComponent(tab: Tab, tagName: JxMutableNode["tagName"]): void {
43
+ const after = elementsImporting(tab, tagName);
44
+ if (after) {
45
+ /* Through the recorded mutator, not a bare assignment: a transaction that records ops is
46
+ undone by replaying their inverses, so an unrecorded write beside the insert would survive
47
+ the ⌘Z that took the instance back, and never reach a co-editor.
48
+ SAFETY: `$elements` entries are JSON — module specifiers, `{ $ref }` imports and inline
49
+ element definitions — which is what a node key holds. */
50
+ mutateUpdateProperty(tab, [], "$elements", after as JxNodeValue);
51
+ }
52
+ }
53
+
54
+ /** The document's `$elements` with the component imported, or null when nothing would change. */
55
+ function elementsImporting(tab: Tab, tagName: JxMutableNode["tagName"]): ElementsEntry[] | null {
56
+ if (!isString(tagName) || !tagName.includes("-")) {
57
+ return null;
58
+ }
59
+ const comp = componentRegistry.find((entry) => entry.tagName === tagName);
60
+ if (!comp) {
61
+ return null;
62
+ }
63
+ /* SAFETY: `$elements` holds module specifiers and `{ $ref }` imports; the document type and
64
+ files/elements.ts spell an inline entry as JxMutableNode and JxElement respectively, two editor
65
+ views of the same JSON object that enableElement only compares by `$ref`. */
66
+ const before = (tab.doc.document.$elements ?? []) as ElementsEntry[];
67
+ const after = enableElement(before, comp, tab.documentPath ?? null);
68
+ return after.length === before.length ? null : after;
69
+ }
70
+
71
+ /** A fresh instance of a registered component, or null for a tag the registry does not know. */
72
+ export function componentInstance(tagName: string): JxMutableNode | null {
73
+ const comp = componentRegistry.find((entry) => entry.tagName === tagName);
74
+ return comp ? buildComponentInstance(comp) : null;
75
+ }
76
+
77
+ /**
78
+ * Every registered component as a slash-menu offer, under the Components heading: the project's
79
+ * own, described by their definition file, and the ones installed packages provide, described by
80
+ * their package. A package component needs no prior enabling, because inserting one imports its
81
+ * package ({@link mutateImportComponent}), exactly as the canvas drop does.
82
+ */
83
+ export function componentSlashCommands(): SlashCommand[] {
84
+ return componentRegistry.map((comp) => ({
85
+ component: true,
86
+ description: (comp.source === "npm" ? comp.package : comp.path) ?? "",
87
+ group: COMPONENTS_GROUP,
88
+ label: `<${comp.tagName}>`,
89
+ tag: comp.tagName,
90
+ }));
91
+ }
@@ -7,11 +7,13 @@
7
7
  * name via `setInsertZoneClickHandler(runInsertZoneAction)` in studio.ts.
8
8
  *
9
9
  * The cross-origin insertion "+" (drawn by the host from posted zones) runs the parent-realm
10
- * slash-menu → mutateInsertNode flow on click. The host captured the zone (parentPath + index); the
11
- * new node is selected at its inserted path. Salvaged from the orphaned insertion-helper.ts.
10
+ * slash-menu → mutateInsertNode flow on click. The menu offers the HTML elements and, under a
11
+ * Components heading, the project's components. The host captured the zone (parentPath + index);
12
+ * the new node is selected at its inserted path. Salvaged from the orphaned insertion-helper.ts.
12
13
  */
13
14
 
14
15
  import { showSlashMenu } from "./slash-menu";
16
+ import { componentInstance, mutateImportComponent } from "./insert-component";
15
17
  import { defaultDef } from "../panels/shared";
16
18
  import { mutateInsertNode, transactDoc } from "../tabs/transact";
17
19
  import { activeTab } from "../workspace/workspace";
@@ -21,12 +23,19 @@ import type { JxPath } from "../state";
21
23
  export function runInsertZoneAction(btn: HTMLElement, zone: InsertZone): void {
22
24
  showSlashMenu(btn, "", {
23
25
  onSelect: (cmd) => {
24
- const def = defaultDef(cmd.tag);
26
+ /* A pick from the Components group inserts an instance and imports the component, as a drop
27
+ of the same component does (§5.7); every other pick inserts the element's default. */
28
+ const instance = cmd.component === true ? componentInstance(cmd.tag) : null;
29
+ const def = instance ?? defaultDef(cmd.tag);
25
30
  const insertPath: JxPath = zone.insertParentPath;
26
31
  const idx = zone.index;
27
32
  const newPath = [...insertPath, "children", idx];
28
- transactDoc(activeTab.value, (t) => {
33
+ const tab = activeTab.value;
34
+ transactDoc(tab, (t) => {
29
35
  mutateInsertNode(t, insertPath, idx, def);
36
+ if (instance) {
37
+ mutateImportComponent(t, instance.tagName);
38
+ }
30
39
  t.session.selection = [newPath];
31
40
  });
32
41
  },