@jxsuite/studio 4.1.0 → 5.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 (607) hide show
  1. package/README.md +1 -1
  2. package/data/elements-meta.json +5 -15
  3. package/data/html-meta.json +51 -6
  4. package/dist/chunks/{2020-n6tfwnda.js → 2020-g79n01v9.js} +14 -14
  5. package/dist/chunks/action-group-sp6tna8v.js +118 -0
  6. package/dist/chunks/adapter-nj7tnjfn.js +13 -0
  7. package/dist/chunks/cf-account-picker-fyf63pmy.js +495 -0
  8. package/dist/chunks/cf-settings-71bv5zn9.js +20 -0
  9. package/dist/chunks/color-area-v17df7b8.js +154 -0
  10. package/dist/chunks/color-field-anwybnkn.js +208 -0
  11. package/dist/chunks/color-slider-44ez96fc.js +79 -0
  12. package/dist/chunks/color-xgg3kfw7.js +35 -0
  13. package/dist/chunks/combobox-5e7jev23.js +236 -0
  14. package/dist/chunks/components-2w1f35qs.js +18 -0
  15. package/dist/chunks/content-source-3wqeas45.js +46 -0
  16. package/dist/chunks/convert-to-repeater-d0cgnb6d.js +639 -0
  17. package/dist/chunks/dialog-s890b918.js +85 -0
  18. package/dist/chunks/editor-3qhbg51x.js +35 -0
  19. package/dist/chunks/entry-commands-d6g50j7s.js +42 -0
  20. package/dist/chunks/extension-sections-fy7q4fe9.js +651 -0
  21. package/dist/chunks/file-ops-3btj6st0.js +47 -0
  22. package/dist/chunks/files-ef8fgs3w.js +72 -0
  23. package/dist/chunks/format-host-3c33nwr8.js +40 -0
  24. package/dist/chunks/fs-events-13tj6sex.js +18 -0
  25. package/dist/chunks/git-panel-4wp2rn9b.js +49 -0
  26. package/dist/chunks/icons-yy10wk9r.js +402 -0
  27. package/dist/chunks/iframe-entry-15g3n6xv.js +114 -0
  28. package/dist/chunks/iframe-entry-b27vq81n.js +203 -0
  29. package/dist/chunks/iframe-entry-nbyxee1y.js +154 -0
  30. package/dist/chunks/iframe-host-jgesd924.js +115 -0
  31. package/dist/chunks/{index-wcnt3r9g.js → index-mdmyx6s2.js} +11 -11
  32. package/dist/chunks/index-n4j7qxyr.js +109 -0
  33. package/dist/chunks/{javascript-q8kpmfah.js → javascript-xvrfrpkb.js} +8 -9
  34. package/dist/chunks/{jsonMode-dkr040ga.js → jsonMode-tyx2kj6p.js} +526 -527
  35. package/dist/chunks/jx-validate-j0wxk72w.js +15 -0
  36. package/dist/chunks/layers-1nq5evsh.js +29 -0
  37. package/dist/chunks/layers-panel-ar2gh90g.js +40 -0
  38. package/dist/chunks/listbox-6v2ac08j.js +44 -0
  39. package/dist/chunks/lit-html-m244w9nh.js +17 -0
  40. package/dist/chunks/media-picker-2qbetq73.js +33 -0
  41. package/dist/chunks/menu-q99sy9rf.js +285 -0
  42. package/dist/chunks/menu-rf7n72mf.js +14 -0
  43. package/dist/chunks/{monaco-binding-rj6xpx9y.js → monaco-binding-sw6en4dw.js} +12 -15
  44. package/dist/chunks/monaco-buffer-bhy3hb6m.js +24 -0
  45. package/dist/chunks/{monaco-setup-vx9bhp1d.js → monaco-setup-8r62fj6y.js} +27 -37
  46. package/dist/chunks/notify-628t0anh.js +21 -0
  47. package/dist/chunks/number-field-5cndyft8.js +72 -0
  48. package/dist/chunks/parse-0zx14y15.js +10 -0
  49. package/dist/chunks/platform-ts9cg1a4.js +15 -0
  50. package/dist/chunks/popover-47z3ytva.js +25 -0
  51. package/dist/chunks/publish-panel-98f7sr3g.js +27 -0
  52. package/dist/chunks/quick-search-srzz83k2.js +45 -0
  53. package/dist/chunks/references-n553f0e1.js +26 -0
  54. package/dist/chunks/right-panel-rrgx2dn3.js +45 -0
  55. package/dist/chunks/section-registry-5pq32t0q.js +25 -0
  56. package/dist/chunks/select-2r16ez41.js +49 -0
  57. package/dist/chunks/split-g7nm1rgb.js +191 -0
  58. package/dist/chunks/studio-0hqbpbd0.js +62 -0
  59. package/dist/chunks/studio-18avnhjp.js +2430 -0
  60. package/dist/chunks/{studio-kryvsw6j.js → studio-1k325pwf.js} +48 -48
  61. package/dist/chunks/{studio-msd847zn.js → studio-25hkdrkw.js} +357 -86
  62. package/dist/chunks/studio-26d7myd1.js +258 -0
  63. package/dist/chunks/{studio-4r1pv6zf.js → studio-38emantv.js} +22 -5
  64. package/dist/chunks/studio-3gxv60tn.js +422 -0
  65. package/dist/chunks/{studio-fbf92ckf.js → studio-3hxkd92t.js} +275 -163
  66. package/dist/chunks/studio-4m0haseg.js +185 -0
  67. package/dist/chunks/{studio-ynxwkrzd.js → studio-4wbb9ct0.js} +1953 -1785
  68. package/dist/chunks/{studio-1wagb550.js → studio-5523bpzt.js} +11 -11
  69. package/dist/chunks/{studio-x4qkdat5.js → studio-57mf66w2.js} +1749 -2481
  70. package/dist/chunks/studio-5jy81t3s.js +17463 -0
  71. package/dist/chunks/studio-5rt7sqa0.js +1168 -0
  72. package/dist/chunks/studio-5te5tqng.js +81 -0
  73. package/dist/chunks/{studio-p4ngc046.js → studio-6ewqv390.js} +9 -9
  74. package/dist/chunks/{studio-9ab8gqsm.js → studio-6nzaktvy.js} +7 -7
  75. package/dist/chunks/studio-6xa21ej9.js +28 -0
  76. package/dist/chunks/{studio-5ehhackm.js → studio-727xh0e3.js} +19 -19
  77. package/dist/chunks/{studio-ahn3pdgx.js → studio-79y5sz51.js} +52 -38
  78. package/dist/chunks/{studio-gcye7qka.js → studio-7e11jpa9.js} +21 -21
  79. package/dist/chunks/{studio-q7v10cg2.js → studio-7r42c6ca.js} +17 -7
  80. package/dist/chunks/studio-7xptrg0j.js +954 -0
  81. package/dist/chunks/{studio-cft9r4r7.js → studio-81390n6f.js} +22 -22
  82. package/dist/chunks/{studio-q43a5y9h.js → studio-8jn5yhyn.js} +16 -16
  83. package/dist/chunks/studio-8qtrpzr3.js +839 -0
  84. package/dist/chunks/{studio-dg7zyt5x.js → studio-98b3z07n.js} +1 -1
  85. package/dist/chunks/studio-9xxz1q69.js +1356 -0
  86. package/dist/chunks/studio-akbww0j5.js +13615 -0
  87. package/dist/chunks/{studio-x813zp48.js → studio-arfpe389.js} +30 -30
  88. package/dist/chunks/{studio-trxrabqf.js → studio-az3zd49t.js} +475 -283
  89. package/dist/chunks/studio-cpvb2ed1.js +1171 -0
  90. package/dist/chunks/{studio-f3wqjtk5.js → studio-d0300636.js} +5076 -18
  91. package/dist/chunks/{studio-bgxt93ga.js → studio-d055c37e.js} +13 -13
  92. package/dist/chunks/studio-d1ja2x9r.js +362 -0
  93. package/dist/chunks/{studio-b9b6t1dg.js → studio-e1f24eff.js} +5664 -5666
  94. package/dist/chunks/{studio-3bqpr0zh.js → studio-em9nw8eh.js} +299 -110
  95. package/dist/chunks/studio-erdatzvp.js +1190 -0
  96. package/dist/chunks/{studio-g1wdche0.js → studio-esrxgwh6.js} +80 -80
  97. package/dist/chunks/studio-fabxk6r9.js +2811 -0
  98. package/dist/chunks/{studio-3n4ndrzh.js → studio-h4nvnjvz.js} +303 -303
  99. package/dist/chunks/{studio-dm19q43r.js → studio-hhvs4sdg.js} +10 -10
  100. package/dist/chunks/{studio-q9jd1a60.js → studio-kcgff4r0.js} +15 -18
  101. package/dist/chunks/{studio-685s0ytq.js → studio-nqgawfks.js} +572 -572
  102. package/dist/chunks/{studio-xrapxwwc.js → studio-p1dw8wfw.js} +55 -29
  103. package/dist/chunks/studio-pm6hgy7r.js +4763 -0
  104. package/dist/chunks/studio-q0a3yk4n.js +33 -0
  105. package/dist/chunks/{studio-wcy8trfx.js → studio-q3x06vmj.js} +23 -22
  106. package/dist/chunks/studio-qahxvnan.js +111 -0
  107. package/dist/chunks/{studio-53n8bp04.js → studio-r6xmmtfh.js} +1 -1
  108. package/dist/chunks/{studio-2b946z26.js → studio-rkstz940.js} +3658 -6134
  109. package/dist/chunks/{studio-tvgyw9xw.js → studio-rnyf3f8e.js} +83 -250
  110. package/dist/chunks/studio-rpyah2ew.js +2805 -0
  111. package/dist/chunks/studio-rs76k67r.js +12 -0
  112. package/dist/chunks/{studio-jkftxyvz.js → studio-s6z853aq.js} +5858 -2152
  113. package/dist/chunks/{studio-6yw4y3my.js → studio-sgdc5pfc.js} +8 -8
  114. package/dist/chunks/studio-tb3ev01j.js +763 -0
  115. package/dist/chunks/{studio-t5wqwzdh.js → studio-tjjk7jvk.js} +1 -1
  116. package/dist/chunks/studio-vhv5tgxm.js +51 -0
  117. package/dist/chunks/{studio-qjy2z9bv.js → studio-vz68yn9e.js} +32 -137
  118. package/dist/chunks/studio-wqpngpsk.js +174 -0
  119. package/dist/chunks/{studio-b55y6ewx.js → studio-x3rty6tr.js} +58 -58
  120. package/dist/chunks/studio-xsykpvsh.js +8448 -0
  121. package/dist/chunks/{studio-92ab2jqe.js → studio-y9zedyjn.js} +14 -14
  122. package/dist/chunks/studio-yxa81f45.js +12 -0
  123. package/dist/chunks/swatch-group-t0n6h2rn.js +139 -0
  124. package/dist/chunks/tabs-wbp1cdws.js +156 -0
  125. package/dist/chunks/textfield-abk6qyee.js +73 -0
  126. package/dist/chunks/toast-780pvfbv.js +27 -0
  127. package/dist/chunks/toast-host-3sqv46sn.js +116 -0
  128. package/dist/chunks/toolbar-xbh5bhtr.js +194 -0
  129. package/dist/chunks/tooltip-eptb614w.js +369 -0
  130. package/dist/chunks/tree-a73p0pqm.js +367 -0
  131. package/dist/chunks/{tsMode-76kr53q0.js → tsMode-jy4m86rv.js} +83 -85
  132. package/dist/chunks/workspace-wrew8zk4.js +69 -0
  133. package/dist/chunks/{ws-client-6shc5rze.js → ws-client-ny4zc51r.js} +20 -22
  134. package/dist/iframe-entry.js +1780 -667
  135. package/dist/studio.js +52819 -36480
  136. package/dist/workers/editor.worker.js +1040 -1085
  137. package/dist/workers/json.worker.js +1946 -1991
  138. package/dist/workers/ts.worker.js +4960 -5005
  139. package/index.html +1 -0
  140. package/package.json +18 -52
  141. package/src/about/about-modal.ts +72 -123
  142. package/src/browse/library-commands.ts +20 -49
  143. package/src/browse/library-layouts.ts +87 -218
  144. package/src/browse/library-pane.ts +406 -438
  145. package/src/canvas/canvas-helpers.ts +0 -49
  146. package/src/canvas/canvas-live-render.ts +25 -2
  147. package/src/canvas/canvas-render.ts +666 -231
  148. package/src/canvas/canvas-surface.ts +0 -1
  149. package/src/canvas/canvas-utils.ts +310 -117
  150. package/src/canvas/dialog-path.ts +76 -0
  151. package/src/canvas/dialog-state.ts +67 -0
  152. package/src/canvas/diff-marks.ts +252 -0
  153. package/src/canvas/diff-toolbar.ts +349 -0
  154. package/src/canvas/diff-view.ts +123 -0
  155. package/src/canvas/edit-width-drag.ts +187 -0
  156. package/src/canvas/edit-width.ts +150 -0
  157. package/src/canvas/iframe-entry.ts +125 -8
  158. package/src/canvas/iframe-host.ts +271 -15
  159. package/src/canvas/iframe-interaction.ts +133 -13
  160. package/src/canvas/iframe-patch.ts +7 -1
  161. package/src/canvas/iframe-position.ts +5 -6
  162. package/src/canvas/iframe-protocol.ts +106 -5
  163. package/src/canvas/iframe-render.ts +373 -5
  164. package/src/canvas/iframe-slash.ts +2 -2
  165. package/src/canvas/iframe-subtree.ts +5 -8
  166. package/src/canvas/path-mapping.ts +25 -3
  167. package/src/canvas/popover-path.ts +106 -0
  168. package/src/canvas/popover-state.ts +155 -0
  169. package/src/canvas/surface-registry.ts +21 -10
  170. package/src/collab/presence-chips.ts +51 -73
  171. package/src/commands/app-commands.ts +10 -0
  172. package/src/commands/budget.ts +12 -0
  173. package/src/commands/command-args.ts +369 -6
  174. package/src/commands/defaults.ts +44 -14
  175. package/src/commands/live-context.ts +11 -5
  176. package/src/commands/reference.ts +12 -2
  177. package/src/commands/registry.ts +165 -8
  178. package/src/content/entry-commands.ts +16 -23
  179. package/src/content/entry-editor.ts +144 -101
  180. package/src/editor/canvas-slash-bridge.ts +2 -2
  181. package/src/editor/context-menu.ts +49 -200
  182. package/src/editor/convert-to-component.ts +42 -82
  183. package/src/editor/convert-to-repeater.ts +132 -173
  184. package/src/editor/inline-edit.ts +11 -0
  185. package/src/editor/shortcuts.ts +65 -26
  186. package/src/editor/slash-menu.ts +150 -157
  187. package/src/files/file-ops.ts +117 -11
  188. package/src/files/files.ts +943 -660
  189. package/src/files/json-layout.ts +474 -0
  190. package/src/files/serialize-document.ts +8 -1
  191. package/src/format/convert-file.ts +16 -10
  192. package/src/format/format-host.ts +50 -1
  193. package/src/github/github-auth.ts +71 -29
  194. package/src/github/github-publish.ts +57 -65
  195. package/src/grid/cell-editors.ts +46 -4
  196. package/src/grid/cell-popovers.ts +65 -62
  197. package/src/grid/grid-open.ts +86 -64
  198. package/src/grid/grid-panel.ts +390 -469
  199. package/src/grid/redirects-grid.ts +40 -44
  200. package/src/hosting/layout.ts +2 -0
  201. package/src/i18n/i18n-commands.ts +127 -62
  202. package/src/media/media-pane.ts +83 -133
  203. package/src/new-project/add-repo-modal.ts +125 -176
  204. package/src/new-project/import-tab.ts +181 -252
  205. package/src/new-project/location-fields.ts +93 -160
  206. package/src/new-project/new-project-modal.ts +376 -367
  207. package/src/packages/jxsuite-update.ts +76 -18
  208. package/src/panels/activity-panel.ts +137 -104
  209. package/src/panels/ai-chat/chat-markdown.ts +15 -13
  210. package/src/panels/ai-chat/chat-view.ts +307 -472
  211. package/src/panels/ai-chat/composer.ts +175 -163
  212. package/src/panels/ai-chat/sessions-view.ts +31 -62
  213. package/src/panels/ai-panel.ts +210 -185
  214. package/src/panels/block-action-bar.ts +437 -643
  215. package/src/panels/bottom-dock.ts +112 -47
  216. package/src/panels/chat-panel.ts +19 -14
  217. package/src/panels/data-explorer.ts +141 -102
  218. package/src/panels/data-grid.ts +183 -150
  219. package/src/panels/dnd.ts +75 -20
  220. package/src/panels/editors.ts +26 -19
  221. package/src/panels/elements-panel.ts +243 -186
  222. package/src/panels/empty-state.ts +26 -42
  223. package/src/panels/events-panel.ts +950 -729
  224. package/src/panels/formula-workspace.ts +434 -207
  225. package/src/panels/frontmatter-fields.ts +185 -153
  226. package/src/panels/frontmatter-panel.ts +411 -187
  227. package/src/panels/git-diff-open.ts +148 -0
  228. package/src/panels/git-panel.ts +679 -605
  229. package/src/panels/head-panel.ts +617 -435
  230. package/src/panels/i18n-panel.ts +153 -133
  231. package/src/panels/imports-panel.ts +388 -414
  232. package/src/panels/jump-bar.ts +174 -147
  233. package/src/panels/layers-panel.ts +785 -671
  234. package/src/panels/left-panel.ts +166 -94
  235. package/src/panels/navigator-panels.ts +1 -1
  236. package/src/panels/pane-context.ts +529 -677
  237. package/src/panels/pane-grid.ts +265 -241
  238. package/src/panels/panel-registry.ts +15 -4
  239. package/src/panels/problems-panel.ts +177 -99
  240. package/src/panels/properties-panel.ts +1525 -966
  241. package/src/panels/provenance.ts +31 -113
  242. package/src/panels/quick-search.ts +217 -172
  243. package/src/panels/right-panel.ts +135 -169
  244. package/src/panels/seo-modal.ts +346 -167
  245. package/src/panels/settings-menu.ts +109 -646
  246. package/src/panels/settings-pane.ts +102 -86
  247. package/src/panels/signals-panel.ts +1318 -1172
  248. package/src/panels/statement-editor.ts +556 -428
  249. package/src/panels/style-panel.ts +1367 -630
  250. package/src/panels/style-utils.ts +36 -32
  251. package/src/panels/stylebook-doc.ts +30 -73
  252. package/src/panels/stylebook-layers-panel.ts +202 -109
  253. package/src/panels/stylebook-panel.ts +124 -88
  254. package/src/panels/tab-strip.ts +385 -287
  255. package/src/platforms/cloud.ts +106 -16
  256. package/src/platforms/devserver.ts +13 -0
  257. package/src/publish/deploy-checklist.ts +124 -78
  258. package/src/publish/publish-commands.ts +4 -12
  259. package/src/publish/publish-panel.ts +134 -328
  260. package/src/services/a11y-report.ts +100 -43
  261. package/src/services/ai-command-tools.ts +468 -0
  262. package/src/services/ai-session-store.ts +6 -0
  263. package/src/services/ai-system-prompt.ts +110 -18
  264. package/src/services/ai-tools.ts +28 -143
  265. package/src/services/ai-write-report.ts +174 -0
  266. package/src/services/document-assistant.ts +50 -2
  267. package/src/services/idle.ts +16 -14
  268. package/src/services/live-surfaces.ts +237 -0
  269. package/src/services/model-uri.ts +75 -0
  270. package/src/services/notify.ts +63 -6
  271. package/src/services/popover-report.ts +325 -0
  272. package/src/services/surface-registry.ts +56 -0
  273. package/src/services/tool-executor.ts +6 -0
  274. package/src/settings/contexts-section.ts +301 -299
  275. package/src/settings/contributed-section.ts +361 -222
  276. package/src/settings/css-vars-editor.ts +320 -325
  277. package/src/settings/defs-editor.ts +572 -535
  278. package/src/settings/dependencies-editor.ts +79 -107
  279. package/src/settings/extension-commands.ts +379 -0
  280. package/src/settings/extension-rows.ts +113 -0
  281. package/src/settings/extensions-section.ts +287 -0
  282. package/src/settings/general-settings.ts +130 -181
  283. package/src/settings/head-editor.ts +10 -305
  284. package/src/settings/locales-section.ts +146 -196
  285. package/src/settings/preferences-accounts.ts +1 -1
  286. package/src/settings/preferences-dialog.ts +350 -330
  287. package/src/settings/project-sections.ts +108 -181
  288. package/src/settings/schema-field-ui.ts +11 -399
  289. package/src/settings/section-registry.ts +8 -1
  290. package/src/settings/settings-document.ts +31 -29
  291. package/src/shell/tree.ts +42 -87
  292. package/src/shell.ts +48 -36
  293. package/src/studio.ts +90 -67
  294. package/src/style/token-ref.ts +0 -39
  295. package/src/surfaces/about.json +263 -0
  296. package/src/surfaces/about.ts +163 -0
  297. package/src/surfaces/add-repo.json +486 -0
  298. package/src/surfaces/add-repo.ts +236 -0
  299. package/src/surfaces/ai-chat.json +2185 -0
  300. package/src/surfaces/ai-chat.ts +549 -0
  301. package/src/surfaces/ai-credentials-form.json +332 -0
  302. package/src/surfaces/ai-credentials-form.ts +151 -0
  303. package/src/surfaces/ai-managed-connect.json +108 -0
  304. package/src/surfaces/ai-managed-connect.ts +123 -0
  305. package/src/surfaces/ai-model-picker.json +114 -0
  306. package/src/surfaces/ai-model-picker.ts +139 -0
  307. package/src/surfaces/block-action-bar.json +640 -0
  308. package/src/surfaces/block-action-bar.ts +368 -0
  309. package/src/surfaces/bottom-dock.json +154 -0
  310. package/src/surfaces/bottom-dock.ts +125 -0
  311. package/src/surfaces/canvas-stage.json +444 -0
  312. package/src/surfaces/canvas-stage.ts +253 -0
  313. package/src/surfaces/cf-account-picker.json +300 -0
  314. package/src/surfaces/cf-account-picker.ts +214 -0
  315. package/src/surfaces/commandbar.json +984 -0
  316. package/src/{panels/toolbar.ts → surfaces/commandbar.ts} +414 -389
  317. package/src/surfaces/convert-repeater.json +309 -0
  318. package/src/surfaces/convert-repeater.ts +276 -0
  319. package/src/surfaces/data-actions.json +157 -0
  320. package/src/surfaces/data-actions.ts +115 -0
  321. package/src/surfaces/dialog.json +289 -0
  322. package/src/surfaces/dialog.ts +343 -0
  323. package/src/surfaces/diff-toolbar.json +266 -0
  324. package/src/surfaces/diff-toolbar.ts +139 -0
  325. package/src/surfaces/doc-header.json +739 -0
  326. package/src/surfaces/doc-header.ts +223 -0
  327. package/src/surfaces/empty-state.json +152 -0
  328. package/src/surfaces/empty-state.ts +173 -0
  329. package/src/surfaces/entry-editor.json +281 -0
  330. package/src/surfaces/entry-editor.ts +191 -0
  331. package/src/surfaces/expression-editor.json +977 -0
  332. package/src/surfaces/expression-editor.ts +266 -0
  333. package/src/surfaces/files-panel.json +592 -0
  334. package/src/surfaces/files-panel.ts +301 -0
  335. package/src/surfaces/formula-palette.json +364 -0
  336. package/src/surfaces/formula-palette.ts +340 -0
  337. package/src/surfaces/git-panel.json +1822 -0
  338. package/src/surfaces/git-panel.ts +272 -0
  339. package/src/surfaces/github-auth.json +112 -0
  340. package/src/surfaces/github-auth.ts +139 -0
  341. package/src/surfaces/github-publish.json +206 -0
  342. package/src/surfaces/github-publish.ts +169 -0
  343. package/src/surfaces/grid-cell.json +218 -0
  344. package/src/surfaces/grid-cell.ts +120 -0
  345. package/src/surfaces/grid-open.json +147 -0
  346. package/src/surfaces/grid-open.ts +87 -0
  347. package/src/surfaces/grid-panel.json +706 -0
  348. package/src/surfaces/grid-panel.ts +206 -0
  349. package/src/surfaces/grid-replace.json +179 -0
  350. package/src/surfaces/grid-replace.ts +89 -0
  351. package/src/surfaces/grid-views.json +446 -0
  352. package/src/surfaces/grid-views.ts +172 -0
  353. package/src/surfaces/inspector-dock.json +187 -0
  354. package/src/surfaces/inspector-dock.ts +161 -0
  355. package/src/surfaces/jump-bar.json +243 -0
  356. package/src/surfaces/jump-bar.ts +129 -0
  357. package/src/surfaces/jxsuite-update.json +146 -0
  358. package/src/surfaces/jxsuite-update.ts +149 -0
  359. package/src/surfaces/library-pane.json +1509 -0
  360. package/src/surfaces/library-pane.ts +363 -0
  361. package/src/surfaces/logic-panel.json +1949 -0
  362. package/src/surfaces/logic-panel.ts +369 -0
  363. package/src/surfaces/logic-workspace.json +685 -0
  364. package/src/surfaces/logic-workspace.ts +238 -0
  365. package/src/surfaces/media-browser.json +303 -0
  366. package/src/surfaces/media-browser.ts +145 -0
  367. package/src/surfaces/media-field.json +163 -0
  368. package/src/surfaces/media-field.ts +116 -0
  369. package/src/surfaces/media-pane.json +513 -0
  370. package/src/surfaces/media-pane.ts +197 -0
  371. package/src/surfaces/menu.json +232 -0
  372. package/src/surfaces/menu.ts +375 -0
  373. package/src/surfaces/navigator-dock.json +113 -0
  374. package/src/surfaces/navigator-dock.ts +130 -0
  375. package/src/surfaces/new-project.json +1654 -0
  376. package/src/surfaces/new-project.ts +411 -0
  377. package/src/surfaces/palette.json +535 -0
  378. package/src/surfaces/pane-context.json +1497 -0
  379. package/src/surfaces/pane-context.ts +513 -0
  380. package/src/surfaces/pane-grid.json +188 -0
  381. package/src/surfaces/pane-grid.ts +250 -0
  382. package/src/surfaces/panel-activity.json +563 -0
  383. package/src/surfaces/panel-activity.ts +187 -0
  384. package/src/surfaces/panel-data.json +141 -0
  385. package/src/surfaces/panel-data.ts +156 -0
  386. package/src/surfaces/panel-deploy-checklist.json +283 -0
  387. package/src/surfaces/panel-deploy-checklist.ts +175 -0
  388. package/src/surfaces/panel-elements.json +367 -0
  389. package/src/surfaces/panel-elements.ts +175 -0
  390. package/src/surfaces/panel-i18n.json +569 -0
  391. package/src/surfaces/panel-i18n.ts +212 -0
  392. package/src/surfaces/panel-imports.json +780 -0
  393. package/src/surfaces/panel-imports.ts +209 -0
  394. package/src/surfaces/panel-outline.json +734 -0
  395. package/src/surfaces/panel-outline.ts +326 -0
  396. package/src/surfaces/panel-page.json +1017 -0
  397. package/src/surfaces/panel-page.ts +240 -0
  398. package/src/surfaces/panel-problems.json +497 -0
  399. package/src/surfaces/panel-problems.ts +157 -0
  400. package/src/surfaces/panel-signals.json +1973 -0
  401. package/src/surfaces/panel-signals.ts +436 -0
  402. package/src/surfaces/panel-stylebook-layers.json +317 -0
  403. package/src/surfaces/panel-stylebook-layers.ts +155 -0
  404. package/src/surfaces/preferences.json +850 -0
  405. package/src/surfaces/preferences.ts +332 -0
  406. package/src/surfaces/progress-modal.json +110 -0
  407. package/src/surfaces/progress-modal.ts +150 -0
  408. package/src/surfaces/properties-panel.json +1578 -0
  409. package/src/surfaces/properties-panel.ts +337 -0
  410. package/src/surfaces/publish.json +945 -0
  411. package/src/surfaces/publish.ts +392 -0
  412. package/src/surfaces/push-plan.json +110 -0
  413. package/src/surfaces/push-plan.ts +87 -0
  414. package/src/surfaces/rail.json +255 -0
  415. package/src/surfaces/rail.ts +206 -0
  416. package/src/surfaces/reference-field.json +191 -0
  417. package/src/surfaces/reference-field.ts +168 -0
  418. package/src/surfaces/schema-builder.json +1011 -0
  419. package/src/surfaces/schema-builder.ts +181 -0
  420. package/src/surfaces/schema-form.json +1013 -0
  421. package/src/surfaces/schema-form.ts +287 -0
  422. package/src/surfaces/secret-field.json +60 -0
  423. package/src/surfaces/secret-field.ts +146 -0
  424. package/src/surfaces/seo.json +932 -0
  425. package/src/surfaces/seo.ts +334 -0
  426. package/src/surfaces/settings-contexts.json +538 -0
  427. package/src/surfaces/settings-contexts.ts +196 -0
  428. package/src/surfaces/settings-contributed.json +489 -0
  429. package/src/surfaces/settings-contributed.ts +199 -0
  430. package/src/surfaces/settings-css-vars.json +820 -0
  431. package/src/surfaces/settings-css-vars.ts +199 -0
  432. package/src/surfaces/settings-defs.json +1363 -0
  433. package/src/surfaces/settings-defs.ts +232 -0
  434. package/src/surfaces/settings-deploy.json +108 -0
  435. package/src/surfaces/settings-deploy.ts +135 -0
  436. package/src/surfaces/settings-extensions.json +486 -0
  437. package/src/surfaces/settings-extensions.ts +201 -0
  438. package/src/surfaces/settings-head.json +779 -0
  439. package/src/surfaces/settings-head.ts +397 -0
  440. package/src/surfaces/settings-locales.json +420 -0
  441. package/src/surfaces/settings-locales.ts +137 -0
  442. package/src/surfaces/settings-overview.json +423 -0
  443. package/src/surfaces/settings-overview.ts +218 -0
  444. package/src/surfaces/settings-packages.json +549 -0
  445. package/src/surfaces/settings-packages.ts +158 -0
  446. package/src/surfaces/settings-pane.json +147 -0
  447. package/src/surfaces/settings-pane.ts +150 -0
  448. package/src/surfaces/settings-rawjson.json +104 -0
  449. package/src/surfaces/settings-rawjson.ts +107 -0
  450. package/src/surfaces/shell.json +99 -0
  451. package/src/surfaces/shell.ts +30 -0
  452. package/src/surfaces/slash-menu.json +180 -0
  453. package/src/surfaces/slash-menu.ts +272 -0
  454. package/src/surfaces/statements.json +607 -0
  455. package/src/surfaces/statements.ts +238 -0
  456. package/src/surfaces/statusbar.json +94 -0
  457. package/src/{panels → surfaces}/statusbar.ts +163 -90
  458. package/src/surfaces/style-panel.json +1643 -0
  459. package/src/surfaces/style-panel.ts +338 -0
  460. package/src/surfaces/stylebook-chrome.json +122 -0
  461. package/src/surfaces/stylebook-chrome.ts +122 -0
  462. package/src/surfaces/tab-strip.json +448 -0
  463. package/src/surfaces/tab-strip.ts +200 -0
  464. package/src/surfaces/target-line.json +419 -0
  465. package/src/surfaces/target-line.ts +349 -0
  466. package/src/surfaces/toasts.json +189 -0
  467. package/src/surfaces/welcome.json +571 -0
  468. package/src/surfaces/welcome.ts +338 -0
  469. package/src/tabs/project-config.ts +71 -15
  470. package/src/tabs/tab.ts +52 -1
  471. package/src/types.ts +22 -0
  472. package/src/ui/ai-credentials-form.ts +104 -100
  473. package/src/ui/ai-managed-connect.ts +54 -36
  474. package/src/ui/ai-model-picker.ts +75 -47
  475. package/src/ui/cf-account-picker.ts +76 -141
  476. package/src/ui/color-selector.ts +80 -301
  477. package/src/ui/dynamic-slot.ts +43 -283
  478. package/src/ui/expression-editor.ts +797 -576
  479. package/src/ui/field-input.ts +8 -181
  480. package/src/ui/form-controls.ts +838 -366
  481. package/src/ui/formula-chips.ts +80 -85
  482. package/src/ui/kit.ts +33 -0
  483. package/src/ui/layers.ts +293 -593
  484. package/src/ui/media-picker.ts +203 -284
  485. package/src/ui/panel-resize.ts +251 -64
  486. package/src/ui/popover-surface.ts +153 -0
  487. package/src/ui/progress-modal.ts +22 -60
  488. package/src/ui/regions.ts +13 -7
  489. package/src/ui/schema-form.ts +906 -440
  490. package/src/ui/surface.ts +122 -0
  491. package/src/ui/unit-selector.ts +15 -131
  492. package/src/ui/value-source.ts +14 -0
  493. package/src/ui/virtual-window.ts +88 -9
  494. package/src/utils/canvas-media.ts +72 -0
  495. package/src/utils/edit-display.ts +7 -2
  496. package/src/utils/element-selectors.ts +69 -0
  497. package/src/utils/link-target.ts +2 -2
  498. package/src/utils/studio-utils.ts +0 -10
  499. package/src/view.ts +15 -0
  500. package/src/workspace/pane-derive.ts +36 -14
  501. package/src/workspace/workspace.ts +15 -6
  502. package/styles/canvas.css +72 -467
  503. package/styles/forced-colors.css +48 -82
  504. package/styles/forced-colors.json +41 -0
  505. package/styles/inspector.css +2 -1301
  506. package/styles/overlays.css +96 -2823
  507. package/styles/panels.css +6 -1423
  508. package/styles/shell-frame.css +272 -0
  509. package/styles/shell-frame.json +227 -0
  510. package/styles/shell.css +0 -1568
  511. package/styles/tokens.css +65 -136
  512. package/styles/tokens.json +84 -0
  513. package/dist/chunks/Overlay.dev-etqjwyf9.js +0 -23
  514. package/dist/chunks/adapter-jkcyhgtr.js +0 -14
  515. package/dist/chunks/cf-account-picker-en86v7x7.js +0 -163
  516. package/dist/chunks/cf-settings-45fgyx8y.js +0 -21
  517. package/dist/chunks/components-zmx10t62.js +0 -21
  518. package/dist/chunks/content-source-48xg86wj.js +0 -49
  519. package/dist/chunks/convert-to-repeater-43tfn7cs.js +0 -268
  520. package/dist/chunks/editor-cptq5jp1.js +0 -37
  521. package/dist/chunks/entry-commands-e63f1xc0.js +0 -46
  522. package/dist/chunks/extension-sections-txv08xxg.js +0 -311
  523. package/dist/chunks/file-ops-wbh4hhsx.js +0 -48
  524. package/dist/chunks/files-3kfkwhk4.js +0 -93
  525. package/dist/chunks/flow-qbfgrpeh.js +0 -634
  526. package/dist/chunks/focus-trap.esm-9t2drp0g.js +0 -1025
  527. package/dist/chunks/focus-visible-6eg70z18.js +0 -167
  528. package/dist/chunks/format-host-w16q81tf.js +0 -43
  529. package/dist/chunks/fs-events-hrrg328g.js +0 -21
  530. package/dist/chunks/git-panel-532whr1y.js +0 -59
  531. package/dist/chunks/index-zqxmqxmh.js +0 -109
  532. package/dist/chunks/jx-validate-8er6gbxy.js +0 -19
  533. package/dist/chunks/layers-panel-07hm7g5j.js +0 -67
  534. package/dist/chunks/layers-q75tj2mf.js +0 -45
  535. package/dist/chunks/lit-html-j055mkh8.js +0 -22
  536. package/dist/chunks/monaco-buffer-dqw0xxpx.js +0 -25
  537. package/dist/chunks/notify-yjm79n4s.js +0 -30
  538. package/dist/chunks/parse-24d0gay3.js +0 -19
  539. package/dist/chunks/platform-ba1j1gbh.js +0 -16
  540. package/dist/chunks/publish-panel-v9st96ja.js +0 -29
  541. package/dist/chunks/quick-search-zccjp6gv.js +0 -66
  542. package/dist/chunks/references-3qx8fkyp.js +0 -29
  543. package/dist/chunks/right-panel-vm3b6kwy.js +0 -46
  544. package/dist/chunks/section-registry-sx1ykvkw.js +0 -30
  545. package/dist/chunks/sp-overlay.dev-qgvhnxf4.js +0 -18
  546. package/dist/chunks/sp-popover.dev-n2etmqdp.js +0 -9
  547. package/dist/chunks/sp-progress-circle.dev-hryhj0es.js +0 -15
  548. package/dist/chunks/sp-tooltip.dev-4sastdnw.js +0 -15
  549. package/dist/chunks/sp-tray.dev-tkbscm06.js +0 -199
  550. package/dist/chunks/studio-0a5qrh01.js +0 -3
  551. package/dist/chunks/studio-0bq6eh7v.js +0 -12
  552. package/dist/chunks/studio-18j3bmcz.js +0 -14
  553. package/dist/chunks/studio-1ngbejft.js +0 -181
  554. package/dist/chunks/studio-4b5z1x43.js +0 -424
  555. package/dist/chunks/studio-4n2b5gzb.js +0 -357
  556. package/dist/chunks/studio-4rzyrtjz.js +0 -426
  557. package/dist/chunks/studio-5fx3g9jg.js +0 -4325
  558. package/dist/chunks/studio-5myknm0a.js +0 -1903
  559. package/dist/chunks/studio-7afd21nw.js +0 -67
  560. package/dist/chunks/studio-7nte2hj4.js +0 -613
  561. package/dist/chunks/studio-8jpzqk5s.js +0 -128
  562. package/dist/chunks/studio-8q517dfr.js +0 -24
  563. package/dist/chunks/studio-950k0dx6.js +0 -6939
  564. package/dist/chunks/studio-9n4mrmnb.js +0 -184
  565. package/dist/chunks/studio-a0tswy0w.js +0 -58
  566. package/dist/chunks/studio-ahnzd9rg.js +0 -23
  567. package/dist/chunks/studio-cmb03bg5.js +0 -13890
  568. package/dist/chunks/studio-d1ks410h.js +0 -175
  569. package/dist/chunks/studio-ef49pnzx.js +0 -387
  570. package/dist/chunks/studio-eqq1w638.js +0 -113
  571. package/dist/chunks/studio-ew0djmhg.js +0 -341
  572. package/dist/chunks/studio-gfgb8pe6.js +0 -1652
  573. package/dist/chunks/studio-h4rxscfc.js +0 -10
  574. package/dist/chunks/studio-jc994dnr.js +0 -1066
  575. package/dist/chunks/studio-kyh9avvv.js +0 -28
  576. package/dist/chunks/studio-m3hwn7y1.js +0 -120
  577. package/dist/chunks/studio-mtfnaqs9.js +0 -659
  578. package/dist/chunks/studio-n6j9t10q.js +0 -318
  579. package/dist/chunks/studio-n8b9wqwy.js +0 -291
  580. package/dist/chunks/studio-qcy05dp8.js +0 -1743
  581. package/dist/chunks/studio-qq6jym04.js +0 -53
  582. package/dist/chunks/studio-rdx8jcxy.js +0 -83
  583. package/dist/chunks/studio-rn9ta2mz.js +0 -111
  584. package/dist/chunks/studio-t7qb9db8.js +0 -665
  585. package/dist/chunks/studio-tmc5emrw.js +0 -8
  586. package/dist/chunks/studio-z6nw2ek2.js +0 -18
  587. package/dist/chunks/studio-zczps70q.js +0 -378
  588. package/dist/chunks/studio-zqazsct2.js +0 -1823
  589. package/dist/chunks/workspace-fjx52ryv.js +0 -89
  590. package/src/panels/activity-bar.ts +0 -251
  591. package/src/panels/panel-scheduler.ts +0 -219
  592. package/src/panels/style-inputs.ts +0 -237
  593. package/src/panels/target-line.ts +0 -274
  594. package/src/panels/welcome-screen.ts +0 -346
  595. package/src/ui/button-group.ts +0 -102
  596. package/src/ui/choice-field.ts +0 -72
  597. package/src/ui/field-row.ts +0 -249
  598. package/src/ui/formula-palette.ts +0 -213
  599. package/src/ui/icons.ts +0 -52
  600. package/src/ui/jx-theme.ts +0 -130
  601. package/src/ui/spectrum.ts +0 -340
  602. package/src/ui/value-selector.ts +0 -174
  603. package/src/ui/widgets.ts +0 -172
  604. /package/dist/chunks/{javascript-s56a8dpk.css → javascript-4abzjcy9.css} +0 -0
  605. /package/dist/chunks/{jsonMode-zr9sw0c7.css → jsonMode-vvmpmk34.css} +0 -0
  606. /package/dist/chunks/{monaco-setup-pav68602.css → monaco-setup-8vbyqnhr.css} +0 -0
  607. /package/dist/chunks/{tsMode-ef187txx.css → tsMode-hnxxb73h.css} +0 -0
@@ -0,0 +1,338 @@
1
+ /// <reference lib="dom" />
2
+ /**
3
+ * Start pane — the pane-grid surface shown when no project is loaded and no document tabs are open.
4
+ *
5
+ * Three regions, in the order a first run needs them: **Start** (the ways to get a project in front
6
+ * of you), **Recent** (projects you have opened, identified by name + the folder that tells two
7
+ * same-named projects apart + when you last opened them), and **Projects** (the catalogue a cloud
8
+ * platform enumerates). A repository-access prompt sits above them when the account has no GitHub
9
+ * App installation yet, because nothing else on the pane can succeed until it does.
10
+ *
11
+ * Recents never render a raw absolute path: `recentLocations()` gives every row the shortest
12
+ * trailing path that distinguishes it from the other rows sharing its name.
13
+ *
14
+ * The pane is the `welcome` surface (`surfaces/welcome.json`): this module is the projection —
15
+ * which start actions the platform offers, the recents with their labels, the catalogue minus the
16
+ * recents — and the four things a click can do. The scope is reactive, so a removed recent
17
+ * reconciles in place.
18
+ *
19
+ * @docs studio/interface/welcome-screen
20
+ */
21
+
22
+ import { getAccountStatus, needsAppInstall } from "../account-status";
23
+ import { platformSupportsAddRepo } from "../new-project/add-repo-modal";
24
+ import { getProjectList } from "../project-list";
25
+ import { reactive } from "../reactivity";
26
+ import { now } from "../services/clock";
27
+ import { clearRecentProjects, getRecentProjects, removeRecentProject } from "../recent-projects";
28
+ import { platformSupportsClone } from "../panels/git-panel";
29
+ import { mountSurface, registerSurface } from "../ui/surface";
30
+ import welcomeDoc from "./welcome.json";
31
+ import type { JxDocument } from "@jxsuite/schema/types";
32
+ import type { SurfaceHandle } from "../ui/surface";
33
+
34
+ registerSurface("welcome", welcomeDoc as unknown as JxDocument);
35
+
36
+ interface WelcomeCtx {
37
+ openProject: () => void;
38
+ openRecentProject: (root: string) => void;
39
+ openNewProject: (options?: { tab?: "starter" }) => void;
40
+ cloneRepository: () => void;
41
+ addExistingRepo: () => void;
42
+ }
43
+
44
+ let _ctx: WelcomeCtx | null = null;
45
+
46
+ /** @param {WelcomeCtx} ctx */
47
+ export function initWelcome(ctx: WelcomeCtx) {
48
+ _ctx = ctx;
49
+ }
50
+
51
+ // ─── Location labels ──────────────────────────────────────────────────────────
52
+
53
+ /** Home-relative form of an absolute path (`/home/you/x` → `~/x`); other paths are unchanged. */
54
+ export function shortenPath(path: string): string {
55
+ const match = /^\/(?:home|Users)\/[^/]+(?=\/|$)/.exec(path);
56
+ return match ? `~${path.slice(match[0].length)}` : path;
57
+ }
58
+
59
+ /** The ancestor segments of a root, home-shortened — everything above the project folder itself. */
60
+ function ancestorSegments(root: string): string[] {
61
+ const segments = shortenPath(root).split("/").filter(Boolean);
62
+ return segments.slice(0, -1);
63
+ }
64
+
65
+ /**
66
+ * The last `depth` ancestor folders of a root, as a path. Truncated labels lead with `…/`; a
67
+ * complete absolute one keeps its leading `/`, so the string always says how much it is showing.
68
+ */
69
+ function ancestorLabel(root: string, depth: number): string {
70
+ const segments = ancestorSegments(root);
71
+ if (segments.length === 0) {
72
+ return shortenPath(root);
73
+ }
74
+ const tail = segments.slice(Math.max(0, segments.length - depth));
75
+ if (tail.length < segments.length) {
76
+ return `…/${tail.join("/")}`;
77
+ }
78
+ return tail[0] === "~" || !root.startsWith("/") ? tail.join("/") : `/${tail.join("/")}`;
79
+ }
80
+
81
+ /**
82
+ * The location line for each entry: the shortest trailing path that tells apart the entries sharing
83
+ * a display name. One folder deep is enough for almost every list; same-named projects under the
84
+ * same parent fall back to their whole home-shortened root, which is unique because roots are.
85
+ *
86
+ * @param entries Projects to label, in any order.
87
+ * @returns Root → location label, one entry per distinct root.
88
+ */
89
+ export function recentLocations(
90
+ entries: readonly { name: string; root: string }[],
91
+ ): Map<string, string> {
92
+ const groups = new Map<string, { name: string; root: string }[]>();
93
+ for (const entry of entries) {
94
+ const group = groups.get(entry.name);
95
+ if (group) {
96
+ group.push(entry);
97
+ } else {
98
+ groups.set(entry.name, [entry]);
99
+ }
100
+ }
101
+
102
+ const labels = new Map<string, string>();
103
+ for (const group of groups.values()) {
104
+ const maxDepth = Math.max(...group.map((e) => ancestorSegments(e.root).length), 1);
105
+ let chosen = group.map((e) => ancestorLabel(e.root, maxDepth));
106
+ for (let depth = 1; depth <= maxDepth; depth++) {
107
+ const candidate = group.map((e) => ancestorLabel(e.root, depth));
108
+ if (new Set(candidate).size === group.length) {
109
+ chosen = candidate;
110
+ break;
111
+ }
112
+ }
113
+ if (new Set(chosen).size !== group.length) {
114
+ chosen = group.map((e) => shortenPath(e.root));
115
+ }
116
+ for (const [index, entry] of group.entries()) {
117
+ labels.set(entry.root, chosen[index]!);
118
+ }
119
+ }
120
+ return labels;
121
+ }
122
+
123
+ const MINUTE = 60_000;
124
+ const HOUR = 60 * MINUTE;
125
+ const DAY = 24 * HOUR;
126
+
127
+ /**
128
+ * "When you last opened it", in the coarse units a project list is read in. Deliberately vague past
129
+ * a week — the exact timestamp is in the row's tooltip.
130
+ *
131
+ * @param timestamp Epoch milliseconds.
132
+ * @param at Epoch milliseconds to measure against. Defaults to the {@link now} seam, so a pinned
133
+ * clock makes "last opened" answer the same on every read.
134
+ */
135
+ export function lastOpenedLabel(timestamp: number, at: number = now()): string {
136
+ const elapsed = Math.max(0, at - timestamp);
137
+ if (elapsed < MINUTE) {
138
+ return "just now";
139
+ }
140
+ if (elapsed < HOUR) {
141
+ const minutes = Math.floor(elapsed / MINUTE);
142
+ return `${minutes} minute${minutes === 1 ? "" : "s"} ago`;
143
+ }
144
+ if (elapsed < DAY) {
145
+ const hours = Math.floor(elapsed / HOUR);
146
+ return `${hours} hour${hours === 1 ? "" : "s"} ago`;
147
+ }
148
+ const days = Math.floor(elapsed / DAY);
149
+ if (days === 1) {
150
+ return "yesterday";
151
+ }
152
+ if (days < 30) {
153
+ return `${days} days ago`;
154
+ }
155
+ const months = Math.floor(days / 30);
156
+ return months < 12 ? `${months} month${months === 1 ? "" : "s"} ago` : "over a year ago";
157
+ }
158
+
159
+ // ─── The surface ──────────────────────────────────────────────────────────────
160
+
161
+ /** One Start-list action: a labelled, icon-led button, run through `run`. */
162
+ interface StartAction {
163
+ id: "new" | "open" | "clone" | "add";
164
+ title: string;
165
+ icon: string;
166
+ }
167
+
168
+ interface RecentRow {
169
+ root: string;
170
+ name: string;
171
+ location: string;
172
+ when: string;
173
+ }
174
+
175
+ interface CatalogueRow {
176
+ root: string;
177
+ name: string;
178
+ detail: string;
179
+ }
180
+
181
+ interface WelcomeScope extends Record<string, unknown> {
182
+ actions: StartAction[];
183
+ installNeeded: boolean;
184
+ installUrl: string;
185
+ hasRecent: boolean;
186
+ recent: RecentRow[];
187
+ hasCatalogue: boolean;
188
+ catalogue: CatalogueRow[];
189
+ run: (id: string) => void;
190
+ open: (root: string) => void;
191
+ remove: (root: string) => void;
192
+ clear: () => void;
193
+ }
194
+
195
+ let _state: WelcomeScope | null = null;
196
+ let _host: HTMLElement | null = null;
197
+ let _mount: Promise<SurfaceHandle> | null = null;
198
+ let _handle: SurfaceHandle | null = null;
199
+
200
+ /** The pane, as the surface reads it, from the platform and the stores as they stand now. */
201
+ function project(): Omit<WelcomeScope, "run" | "open" | "remove" | "clear"> {
202
+ const recent = getRecentProjects();
203
+ // Catalogue entries already in Recent stay in that section only.
204
+ const catalogue = getProjectList().filter((p) => !recent.some((r) => r.root === p.root));
205
+ const recentLabels = recentLocations(recent);
206
+ const catalogueLabels = recentLocations(catalogue);
207
+ const actions: StartAction[] = [
208
+ // One entry, not two: the starter gallery IS the first step of New Project now, so the
209
+ // Separate "Start from an Example…" button it used to hide behind is gone.
210
+ { icon: "plus", id: "new", title: "New Project…" },
211
+ { icon: "folder-open", id: "open", title: "Open Project…" },
212
+ ...(platformSupportsClone()
213
+ ? [{ icon: "download-simple", id: "clone", title: "Clone Git Repository…" } as const]
214
+ : []),
215
+ ...(platformSupportsAddRepo()
216
+ ? [{ icon: "cube", id: "add", title: "Add Existing Repository…" } as const]
217
+ : []),
218
+ ];
219
+ return {
220
+ actions,
221
+ catalogue: catalogue.map((entry) => ({
222
+ detail: entry.description ?? catalogueLabels.get(entry.root) ?? entry.root,
223
+ name: entry.name,
224
+ root: entry.root,
225
+ })),
226
+ hasCatalogue: catalogue.length > 0,
227
+ hasRecent: recent.length > 0,
228
+ /* The GitHub-App install prompt, gated on `needsAppInstall()`: the account is connected,
229
+ reports zero installations, and told us where to fix that — the recovery path for the
230
+ structured needs-installation 403 that `platform-errors.ts` decodes. */
231
+ installNeeded: needsAppInstall(),
232
+ installUrl: getAccountStatus()?.appInstallUrl ?? "#",
233
+ recent: recent.map((entry) => ({
234
+ location: recentLabels.get(entry.root) ?? entry.root,
235
+ name: entry.name,
236
+ root: entry.root,
237
+ when: lastOpenedLabel(entry.timestamp),
238
+ })),
239
+ };
240
+ }
241
+
242
+ /** The reactive scope the surface reads, made once. */
243
+ function state(): WelcomeScope {
244
+ _state ??= reactive({
245
+ actions: [],
246
+ catalogue: [],
247
+ clear: () => {
248
+ clearRecentProjects();
249
+ refresh();
250
+ },
251
+ hasCatalogue: false,
252
+ hasRecent: false,
253
+ installNeeded: false,
254
+ installUrl: "#",
255
+ open: (root: string) => {
256
+ _ctx?.openRecentProject(root);
257
+ },
258
+ recent: [],
259
+ remove: (root: string) => {
260
+ removeRecentProject(root);
261
+ refresh();
262
+ },
263
+ run: (id: string) => {
264
+ switch (id) {
265
+ case "new": {
266
+ _ctx?.openNewProject();
267
+ break;
268
+ }
269
+ case "open": {
270
+ _ctx?.openProject();
271
+ break;
272
+ }
273
+ case "clone": {
274
+ _ctx?.cloneRepository();
275
+ break;
276
+ }
277
+ case "add": {
278
+ _ctx?.addExistingRepo();
279
+ break;
280
+ }
281
+ default: {
282
+ break;
283
+ }
284
+ }
285
+ },
286
+ }) as WelcomeScope;
287
+ return _state;
288
+ }
289
+
290
+ /** Recompute the projection; the surface follows. */
291
+ function refresh(): void {
292
+ Object.assign(state(), project());
293
+ }
294
+
295
+ /**
296
+ * Draw the pane into `host`, or bring the one already there up to date.
297
+ *
298
+ * The canvas calls this on every render while nothing is open, into the same wrap it clears when
299
+ * something opens — so a mount whose root has since left the document is disposed and remade, and
300
+ * one still standing is only refreshed.
301
+ *
302
+ * @param {HTMLElement} host
303
+ */
304
+ export function renderWelcome(host: HTMLElement): void {
305
+ refresh();
306
+ const standing = _host === host && _handle !== null && _handle.root.isConnected;
307
+ if (standing) {
308
+ return;
309
+ }
310
+ if (_mount && _host === host && !_handle) {
311
+ // Still mounting into this very host.
312
+ return;
313
+ }
314
+ disposeWelcome();
315
+ _host = host;
316
+ const pending = mountSurface("welcome", state(), host);
317
+ _mount = pending;
318
+ void pending.then((handle) => {
319
+ if (_mount === pending) {
320
+ _handle = handle;
321
+ } else {
322
+ handle.dispose();
323
+ }
324
+ });
325
+ }
326
+
327
+ /** Take the pane down and forget its host. */
328
+ export function disposeWelcome(): void {
329
+ const pending = _mount;
330
+ _mount = null;
331
+ if (_handle) {
332
+ _handle.dispose();
333
+ _handle = null;
334
+ } else if (pending) {
335
+ void pending.then((handle) => handle.dispose());
336
+ }
337
+ _host = null;
338
+ }
@@ -33,18 +33,24 @@
33
33
  * from the other side: a config that reached disk without passing through here (the assistant's
34
34
  * `write_file`) is put INTO the document rather than left beside it as a rival.
35
35
  *
36
- * **One serialisation.** {@link serializeProjectConfig} is `JSON.stringify(config, null, 2)` —
37
- * byte-for-byte what `files/file-ops.ts`'s native-JSON branch writes, so a ⌘S on the open tab and a
38
- * settings edit produce the same file. The predecessor had `null, 2` in `site-context.ts` and
39
- * `"\t"` in both `settings/contributed-section.ts` and `settings/defs-editor.ts`, so a settings
40
- * edit re-indented the entire file — every `project.json` in this repository is on disk with two
41
- * spaces.
36
+ * **One serialisation.** {@link serializeProjectConfig} is `files/json-layout.ts`'s serializer over
37
+ * the layout the file was read in — byte-for-byte what `files/serialize-document.ts`'s native-JSON
38
+ * branch writes, so a ⌘S on the open tab and a settings edit produce the same file. The predecessor
39
+ * had `null, 2` in `site-context.ts` and `"\t"` in both `settings/contributed-section.ts` and
40
+ * `settings/defs-editor.ts`, so a settings edit re-indented the entire file — every `project.json`
41
+ * in this repository is on disk with two spaces. It was then `JSON.stringify(config, null, 2)`,
42
+ * which agreed with the tab's save and disagreed with the formatter: a one-field edit re-laid every
43
+ * short array in the file (issue 308).
42
44
  *
43
45
  * **A no-op edit writes nothing.** A form that re-commits the value it already holds — and every
44
46
  * one of these surfaces has such a path — used to rewrite the whole file. A commit now compares the
45
47
  * serialised result against what the file itself says and returns without a transaction, without a
46
48
  * write, and therefore with an empty diff.
47
49
  *
50
+ * **A real edit is a one-line diff.** The file's layout — which objects sit on one line, where a
51
+ * blank line falls — is read once per binding ({@link seedCommitted}) and every serialisation here
52
+ * honours it, so what changes on disk is the field that changed.
53
+ *
48
54
  * **Collaboration.** `project.json` is out of collab replication; the gate is in
49
55
  * `collab/collab-session.ts`'s `ensureCollab`, which is what would otherwise register the Yjs
50
56
  * history delegate over this tab.
@@ -58,6 +64,8 @@ import { requireProjectState, setProjectState } from "../state";
58
64
  import { setWorkspaceProject, workspace } from "../workspace/workspace";
59
65
  import { PROJECT_CONFIG_PATH, createTab, disposeTab } from "./tab";
60
66
  import { transactDoc } from "./transact";
67
+ import { parseJsonDocument, serializeJson } from "../files/json-layout";
68
+ import type { JsonLayout } from "../files/json-layout";
61
69
 
62
70
  import type { Tab } from "./tab";
63
71
  import type { ProjectConfig } from "@jxsuite/schema/types";
@@ -67,15 +75,16 @@ export { PROJECT_CONFIG_PATH } from "./tab";
67
75
  /**
68
76
  * The project configuration, as a file.
69
77
  *
70
- * The ONE serialisation. Identical to the native-JSON branch of `files/file-ops.ts`'s
78
+ * The ONE serialisation. Identical to the native-JSON branch of `files/serialize-document.ts`'s
71
79
  * `serializeDocument`, which is what ⌘S on the open `project.json` tab goes through — the two must
72
- * agree or every save would fight the last one.
80
+ * agree or every save would fight the last one. Both honour the layout the file was read in ({@link
81
+ * configLayout}); with none known, the formatter's layout for fresh output.
73
82
  *
74
83
  * @param {ProjectConfig} config
75
84
  * @returns {string}
76
85
  */
77
86
  export function serializeProjectConfig(config: ProjectConfig): string {
78
- return JSON.stringify(config, null, 2);
87
+ return serializeJson(config, configLayout());
79
88
  }
80
89
 
81
90
  // ─── The document of record ───────────────────────────────────────────────────
@@ -94,15 +103,34 @@ let _detached: Tab | null = null;
94
103
  * variables editor writes into `config.style` and then hands the same object over as a patch. A
95
104
  * value taken from memory would call every one of those edits a no-op.
96
105
  *
97
- * Comparing two `JSON.stringify(_, null, 2)` renderings of the same key order is a SEMANTIC test,
98
- * not a textual one, which is the point: `project.json` on disk is formatted by `oxfmt` (two-space
99
- * indent, short arrays kept on one line), so a byte comparison against the file would call every
100
- * edit a change and rewrite the whole file. This calls it unchanged and writes nothing.
106
+ * Comparing two renderings of the same key order through the same serializer is a SEMANTIC test,
107
+ * not a textual one, which is the point: a `project.json` formatted some other way than the
108
+ * serializer would lay it out — every array expanded, say — would otherwise look changed on every
109
+ * commit, and a byte comparison against the file would rewrite it whole. This calls it unchanged
110
+ * and writes nothing.
101
111
  *
102
112
  * `null` means "unknown" — no project file, or one that would not parse — and every commit writes.
103
113
  */
104
114
  let _committed: string | null = null;
105
115
 
116
+ /**
117
+ * The layout the file was read in ({@link seedCommitted}), for a binding with no open tab to carry
118
+ * it. Reset with the binding: a different project is a different file.
119
+ */
120
+ let _layout: JsonLayout | null = null;
121
+
122
+ /**
123
+ * The layout a configuration write honours.
124
+ *
125
+ * The open tab's, when there is one — it read the file when it opened, or was lent the seed's
126
+ * record ({@link lendProjectConfigLayout}), and a source-view commit or a reload from disk keeps it
127
+ * current — and otherwise what the seed read. The two describe the same file, so the choice only
128
+ * matters for freshness, and the tab is the fresher.
129
+ */
130
+ function configLayout(): JsonLayout | null {
131
+ return openConfigTab()?.doc.layout ?? _layout;
132
+ }
133
+
106
134
  /** Whether {@link _committed} has been sought for the current binding. One read per project. */
107
135
  let _seeding: Promise<void> | null = null;
108
136
 
@@ -126,6 +154,7 @@ function unbind(): void {
126
154
  }
127
155
  _bound = null;
128
156
  _committed = null;
157
+ _layout = null;
129
158
  _seeding = null;
130
159
  }
131
160
 
@@ -134,7 +163,9 @@ function seedCommitted(): Promise<void> {
134
163
  _seeding ??= (async () => {
135
164
  try {
136
165
  const text = await getPlatform().readFile(PROJECT_CONFIG_PATH);
137
- _committed = serializeProjectConfig(JSON.parse(text) as ProjectConfig);
166
+ const { document, layout } = parseJsonDocument(text);
167
+ _layout = layout;
168
+ _committed = serializeProjectConfig(document as unknown as ProjectConfig);
138
169
  } catch {
139
170
  // No file, an unreadable one, or one that does not parse. "Unknown" is the honest answer,
140
171
  // And an unknown file is one every commit must write.
@@ -144,6 +175,29 @@ function seedCommitted(): Promise<void> {
144
175
  return _seeding;
145
176
  }
146
177
 
178
+ /**
179
+ * Lend the file's layout to `tab` when it has none of its own.
180
+ *
181
+ * The Settings door (`settings/settings-document.ts`) opens the `project.json` tab over the
182
+ * configuration object the project was loaded with — an object the platform parsed, with no text
183
+ * behind it — so that tab arrives with `doc.layout === null`. Left there, a ⌘S on it would write
184
+ * the formatter's layout for fresh output while a settings commit on the SAME document wrote the
185
+ * file's: every short object in the file blown open by the save the settings edit had just kept,
186
+ * which is the one disagreement this module exists to make impossible. The file is read once per
187
+ * binding anyway ({@link seedCommitted}); this hands the record that read made to the tab, and
188
+ * {@link configLayout} then takes it back from the tab like every other writer. A tab that read the
189
+ * file itself — the Files tree, a reload, a source-view commit — keeps the fresher record it has.
190
+ *
191
+ * @param {Tab} tab
192
+ * @returns {Promise<void>}
193
+ */
194
+ export async function lendProjectConfigLayout(tab: Tab): Promise<void> {
195
+ await seedCommitted();
196
+ if (tab.doc.layout === null && _layout !== null) {
197
+ tab.doc.layout = _layout;
198
+ }
199
+ }
200
+
147
201
  /**
148
202
  * Forget the configuration document — the project-switch and test hook.
149
203
  *
@@ -307,7 +361,9 @@ export async function commitProjectConfig(
307
361
  // Tab's own parse, and the caller has already mutated the object it rendered from.
308
362
  const live = liveConfig();
309
363
  const tab = projectConfigDocument();
310
- await seedCommitted();
364
+ // The seed, and the record it read handed to the document — so the tab the Settings door opened
365
+ // Writes the same bytes from ⌘S that this commit is about to.
366
+ await lendProjectConfigLayout(tab);
311
367
  const current = toRaw(tab.doc.document) as unknown as ProjectConfig;
312
368
  const currentText = serializeProjectConfig(current);
313
369
 
package/src/tabs/tab.ts CHANGED
@@ -2,11 +2,13 @@
2
2
  import { effectScope, reactive } from "../reactivity";
3
3
  import { formatByName, formatForPath } from "../format/format-host";
4
4
  import { normalizeArrayChildren } from "../state";
5
+ import type { JxPath } from "../state";
5
6
  import type { FormulaEditDef, FunctionEditDef, InlineEditDef, JsonValue } from "../types";
6
7
  import { editorKindForMode } from "../commands/context";
7
8
  import type { EditorKind } from "../commands/context";
8
9
  import type { JxMutableNode } from "@jxsuite/schema/types";
9
10
  import type { JxDocOp, JxFmOp } from "./patch-ops";
11
+ import type { JsonLayout } from "../files/json-layout";
10
12
 
11
13
  /**
12
14
  * The project's configuration file, project-relative.
@@ -39,6 +41,29 @@ export interface TabUi {
39
41
  editZoom: number;
40
42
  activeMedia: string | null;
41
43
  activeSelector: string | null;
44
+ /**
45
+ * The popover the canvas is drawing OPEN, by document path, or null for none.
46
+ *
47
+ * Per tab rather than per pane, beside `activeMedia` and `previewColorScheme`, because it is a
48
+ * fact about a DOCUMENT's elements: two panes showing the same tab show the same open panel,
49
+ * which is correct — it is the same element.
50
+ *
51
+ * Exactly one, never a set. Several de-popovered panels in flow at once would shove the page
52
+ * around and make the canvas unreadable; native `popover="auto"` already enforces a single stack,
53
+ * so a set would model something the platform does not have; and "exactly one" makes the
54
+ * open-on-selection rule total, with no ordering decision to make.
55
+ *
56
+ * A VIEW state — it writes nothing to the document, so it takes no undo entry, does not dirty the
57
+ * tab and is not replicated over collaboration. It is also deliberately not restored with a
58
+ * session (§14.8): reopening a project with a modal spread across the page is a worse first frame
59
+ * than reopening it closed.
60
+ */
61
+ openPopover: JxPath | null;
62
+ /**
63
+ * The `<dialog>` the canvas draws open in place, per tab and exactly one; view state like
64
+ * `openPopover`.
65
+ */
66
+ openDialog: JxPath | null;
42
67
  editingFunction: FunctionEditDef | null;
43
68
  /** Logic-tab formula target ($expression editing); editingFunction wins if both are set. */
44
69
  editingFormula: FormulaEditDef | null;
@@ -161,6 +186,18 @@ export interface Tab {
161
186
  sourceFormat: string | null;
162
187
  handlersSource: string | null;
163
188
  dirty: boolean;
189
+ /**
190
+ * The layout the document's JSON file was written in (`files/json-layout.ts`), or null for a
191
+ * document with no JSON source — a format-class file, a new document, a stub.
192
+ *
193
+ * On the document record because it is a fact about the FILE the document came from, and it
194
+ * lives exactly as long as that fact does: every edit leaves it alone (the pointers it is keyed
195
+ * by survive a changed value, a new sibling, a removed one), a reload from disk or a
196
+ * source-view commit replaces it with the layout of the text just read, and opening a different
197
+ * file makes a new tab. The history ring never snapshots it — undo restores a document, not a
198
+ * file.
199
+ */
200
+ layout: JsonLayout | null;
164
201
  };
165
202
  session: {
166
203
  /**
@@ -216,6 +253,8 @@ function createDefaultUi(canvasMode: string, preview = false) {
216
253
  return {
217
254
  activeMedia: null,
218
255
  activeSelector: null,
256
+ openPopover: null,
257
+ openDialog: null,
219
258
  canvasMode,
220
259
  editZoom: 1,
221
260
  editingFormula: null,
@@ -239,7 +278,15 @@ function createDefaultUi(canvasMode: string, preview = false) {
239
278
  };
240
279
  }
241
280
 
242
- const ALL_MODES = ["edit", "design", "preview", "source", "stylebook"];
281
+ /*
282
+ * `git-diff` is here because a comparison is a MODE of a document, and until now nothing said so.
283
+ * The Source Control panel reached it through the injected `setCanvasMode`, which performs no
284
+ * capability check — so the mode worked while `editorKindsOf` could never report `diff`, the Editor
285
+ * picker could never offer it, and `canvas.setMode { mode: "git-diff" }` threw for every document in
286
+ * the project. Declaring it makes the palette, the assistant and the screenshot runner able to open
287
+ * a comparison by name, and puts Diff on the Editor axis where §18.4 already says it belongs.
288
+ */
289
+ const ALL_MODES = ["edit", "design", "preview", "source", "stylebook", "git-diff"];
243
290
 
244
291
  /**
245
292
  * Create a new tab with reactive doc/session/history trees, owned by an effectScope.
@@ -254,6 +301,7 @@ const ALL_MODES = ["edit", "design", "preview", "source", "stylebook"];
254
301
  * capabilities?: { modes?: string[] };
255
302
  * openedFrom?: TabOrigin | null;
256
303
  * preview?: boolean;
304
+ * layout?: JsonLayout | null;
257
305
  * }} opts
258
306
  * @returns {Tab}
259
307
  */
@@ -267,6 +315,7 @@ export function createTab({
267
315
  capabilities,
268
316
  openedFrom = null,
269
317
  preview: previewTab = false,
318
+ layout = null,
270
319
  }: {
271
320
  id: string;
272
321
  documentPath?: string | null;
@@ -277,6 +326,7 @@ export function createTab({
277
326
  capabilities?: { modes?: string[] };
278
327
  openedFrom?: TabOrigin | null;
279
328
  preview?: boolean;
329
+ layout?: JsonLayout | null;
280
330
  }) {
281
331
  const scope = effectScope();
282
332
 
@@ -299,6 +349,7 @@ export function createTab({
299
349
  dirty: false,
300
350
  document,
301
351
  handlersSource: null,
352
+ layout,
302
353
  mode: inferDocumentMode(documentPath, sourceFormat),
303
354
  sourceFormat,
304
355
  }),
package/src/types.ts CHANGED
@@ -28,6 +28,7 @@ import type {
28
28
  DataRowsResult,
29
29
  DataRowUpdate,
30
30
  DirEntry,
31
+ ExtensionCatalogEntry,
31
32
  ExtensionsInfo,
32
33
  FsEvent,
33
34
  GitBranchesResult,
@@ -77,8 +78,10 @@ export type {
77
78
  DataRowUpdate,
78
79
  DirEntry,
79
80
  ErrorBody,
81
+ ExtensionCatalogEntry,
80
82
  ExtensionContributionInfo,
81
83
  ExtensionProjectBlock,
84
+ ExtensionSectionInfo,
82
85
  ExtensionsInfo,
83
86
  FsEvent,
84
87
  GitBranchesResult,
@@ -386,6 +389,25 @@ export interface StudioPlatform {
386
389
  * platforms without it lose descriptor-contributed settings sections.
387
390
  */
388
391
  listExtensions?: () => Promise<ExtensionsInfo[]>;
392
+ /**
393
+ * The extensions this backend can OFFER, enabled or not — the AVAILABLE half of the pair whose
394
+ * enabled half is {@link StudioPlatform.listExtensions} above.
395
+ *
396
+ * A capability rather than a constant because not every host can run every extension: a Worker
397
+ * ships a fixed set of extension packages (specs/extensions.md §5.5), and one it does not bundle
398
+ * is dropped from the registry before composition rather than passed to it. A studio that
399
+ * advertised a shipped list would offer rows the host would refuse.
400
+ *
401
+ * Each entry additionally carries two facts only a backend holds: whether THIS host resolves the
402
+ * package without a project install (`bundled`), and whether the project has it installed
403
+ * (`installed`) — the latter because `listPackages` does not mean one thing across backends.
404
+ * `enabled` is deliberately absent: it is `projectConfig.extensions.includes(name)`, which Studio
405
+ * already owns and rewrites through the `updateSiteConfig` chokepoint.
406
+ *
407
+ * Optional: platforms without it offer no catalogue, and adding an extension stays a typed
408
+ * package name.
409
+ */
410
+ listExtensionCatalog?: () => Promise<ExtensionCatalogEntry[]>;
389
411
  /**
390
412
  * Fetch the project's generated entry schemas (project.schema.json / document.schema.json),
391
413
  * PRE-BUNDLED into self-contained documents for editor registration. Optional: without it the