@jxsuite/studio 4.0.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 (613) 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-xp8vnwma.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-nj44jpzj.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-rws7k4br.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-hbtp0eh1.js → studio-25hkdrkw.js} +356 -85
  62. package/dist/chunks/studio-26d7myd1.js +258 -0
  63. package/dist/chunks/{studio-p8j86wag.js → studio-38emantv.js} +22 -5
  64. package/dist/chunks/studio-3gxv60tn.js +422 -0
  65. package/dist/chunks/{studio-8swrjbny.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-gv8vvshc.js → studio-5523bpzt.js} +11 -11
  69. package/dist/chunks/{studio-5q69gfag.js → studio-57mf66w2.js} +1753 -2484
  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-eenfkt4e.js → studio-79y5sz51.js} +52 -38
  78. package/dist/chunks/{studio-4q0wznzf.js → studio-7e11jpa9.js} +461 -145
  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-arfpe389.js +195 -0
  88. package/dist/chunks/{studio-2zkz8evn.js → studio-az3zd49t.js} +473 -281
  89. package/dist/chunks/studio-cpvb2ed1.js +1171 -0
  90. package/dist/chunks/{studio-9r6j5ev1.js → studio-d0300636.js} +5078 -19
  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-4pqqxbrt.js → studio-em9nw8eh.js} +301 -112
  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-hhvs4sdg.js +195 -0
  100. package/dist/chunks/{studio-68n67094.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-kjjsjak4.js → studio-rkstz940.js} +3661 -6136
  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-yxrkyv8a.js → studio-s6z853aq.js} +12108 -11766
  113. package/dist/chunks/{studio-sf7zjgv1.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-htsf0w6m.js → studio-vz68yn9e.js} +31 -154
  118. package/dist/chunks/studio-wqpngpsk.js +174 -0
  119. package/dist/chunks/{studio-eesa1myv.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 +1812 -698
  135. package/dist/manifest.json +7 -0
  136. package/dist/studio.js +52900 -36573
  137. package/dist/workers/editor.worker.js +1040 -1085
  138. package/dist/workers/json.worker.js +1946 -1991
  139. package/dist/workers/ts.worker.js +4960 -5005
  140. package/favicon.ico +0 -0
  141. package/index.html +2 -0
  142. package/package.json +22 -55
  143. package/src/about/about-modal.ts +73 -123
  144. package/src/browse/library-commands.ts +20 -49
  145. package/src/browse/library-layouts.ts +87 -218
  146. package/src/browse/library-pane.ts +406 -438
  147. package/src/canvas/canvas-helpers.ts +0 -49
  148. package/src/canvas/canvas-live-render.ts +25 -2
  149. package/src/canvas/canvas-render.ts +666 -231
  150. package/src/canvas/canvas-surface.ts +0 -1
  151. package/src/canvas/canvas-utils.ts +310 -117
  152. package/src/canvas/dialog-path.ts +76 -0
  153. package/src/canvas/dialog-state.ts +67 -0
  154. package/src/canvas/diff-marks.ts +252 -0
  155. package/src/canvas/diff-toolbar.ts +349 -0
  156. package/src/canvas/diff-view.ts +123 -0
  157. package/src/canvas/edit-width-drag.ts +187 -0
  158. package/src/canvas/edit-width.ts +150 -0
  159. package/src/canvas/iframe-entry.ts +125 -8
  160. package/src/canvas/iframe-host.ts +271 -15
  161. package/src/canvas/iframe-interaction.ts +133 -13
  162. package/src/canvas/iframe-patch.ts +7 -1
  163. package/src/canvas/iframe-position.ts +5 -6
  164. package/src/canvas/iframe-protocol.ts +106 -5
  165. package/src/canvas/iframe-render.ts +373 -5
  166. package/src/canvas/iframe-slash.ts +2 -2
  167. package/src/canvas/iframe-subtree.ts +5 -8
  168. package/src/canvas/path-mapping.ts +25 -3
  169. package/src/canvas/popover-path.ts +106 -0
  170. package/src/canvas/popover-state.ts +155 -0
  171. package/src/canvas/surface-registry.ts +21 -10
  172. package/src/collab/presence-chips.ts +51 -73
  173. package/src/commands/app-commands.ts +10 -0
  174. package/src/commands/budget.ts +12 -0
  175. package/src/commands/command-args.ts +369 -6
  176. package/src/commands/defaults.ts +44 -14
  177. package/src/commands/live-context.ts +11 -5
  178. package/src/commands/reference.ts +12 -2
  179. package/src/commands/registry.ts +165 -8
  180. package/src/content/entry-commands.ts +16 -23
  181. package/src/content/entry-editor.ts +144 -101
  182. package/src/editor/canvas-slash-bridge.ts +2 -2
  183. package/src/editor/context-menu.ts +49 -200
  184. package/src/editor/convert-to-component.ts +42 -82
  185. package/src/editor/convert-to-repeater.ts +132 -173
  186. package/src/editor/inline-edit.ts +11 -0
  187. package/src/editor/shortcuts.ts +65 -26
  188. package/src/editor/slash-menu.ts +150 -157
  189. package/src/files/file-ops.ts +117 -11
  190. package/src/files/files.ts +949 -664
  191. package/src/files/json-layout.ts +474 -0
  192. package/src/files/media-paths.ts +13 -42
  193. package/src/files/media-usage.ts +41 -131
  194. package/src/files/serialize-document.ts +8 -1
  195. package/src/format/convert-file.ts +16 -10
  196. package/src/format/format-host.ts +50 -1
  197. package/src/github/github-auth.ts +71 -29
  198. package/src/github/github-publish.ts +57 -65
  199. package/src/grid/cell-editors.ts +46 -4
  200. package/src/grid/cell-popovers.ts +65 -62
  201. package/src/grid/grid-open.ts +86 -64
  202. package/src/grid/grid-panel.ts +390 -469
  203. package/src/grid/redirects-grid.ts +40 -44
  204. package/src/hosting/document.ts +2 -0
  205. package/src/hosting/layout.ts +10 -0
  206. package/src/i18n/i18n-commands.ts +127 -62
  207. package/src/media/media-pane.ts +83 -133
  208. package/src/new-project/add-repo-modal.ts +125 -176
  209. package/src/new-project/import-tab.ts +201 -250
  210. package/src/new-project/location-fields.ts +101 -165
  211. package/src/new-project/new-project-modal.ts +376 -367
  212. package/src/packages/jxsuite-update.ts +76 -18
  213. package/src/panels/activity-panel.ts +137 -104
  214. package/src/panels/ai-chat/chat-markdown.ts +15 -13
  215. package/src/panels/ai-chat/chat-view.ts +307 -472
  216. package/src/panels/ai-chat/composer.ts +175 -153
  217. package/src/panels/ai-chat/sessions-view.ts +31 -62
  218. package/src/panels/ai-panel.ts +210 -185
  219. package/src/panels/block-action-bar.ts +437 -643
  220. package/src/panels/bottom-dock.ts +112 -47
  221. package/src/panels/chat-panel.ts +19 -14
  222. package/src/panels/data-explorer.ts +141 -102
  223. package/src/panels/data-grid.ts +183 -150
  224. package/src/panels/dnd.ts +75 -20
  225. package/src/panels/editors.ts +26 -19
  226. package/src/panels/elements-panel.ts +243 -186
  227. package/src/panels/empty-state.ts +26 -42
  228. package/src/panels/events-panel.ts +950 -729
  229. package/src/panels/formula-workspace.ts +434 -207
  230. package/src/panels/frontmatter-fields.ts +185 -153
  231. package/src/panels/frontmatter-panel.ts +411 -187
  232. package/src/panels/git-diff-open.ts +148 -0
  233. package/src/panels/git-panel.ts +679 -605
  234. package/src/panels/head-panel.ts +617 -435
  235. package/src/panels/i18n-panel.ts +153 -133
  236. package/src/panels/imports-panel.ts +388 -414
  237. package/src/panels/jump-bar.ts +174 -147
  238. package/src/panels/layers-panel.ts +785 -671
  239. package/src/panels/left-panel.ts +166 -94
  240. package/src/panels/navigator-panels.ts +1 -1
  241. package/src/panels/pane-context.ts +529 -677
  242. package/src/panels/pane-grid.ts +265 -241
  243. package/src/panels/panel-registry.ts +15 -4
  244. package/src/panels/problems-panel.ts +177 -99
  245. package/src/panels/properties-panel.ts +1525 -966
  246. package/src/panels/provenance.ts +31 -113
  247. package/src/panels/quick-search.ts +217 -172
  248. package/src/panels/right-panel.ts +135 -169
  249. package/src/panels/seo-modal.ts +346 -167
  250. package/src/panels/settings-menu.ts +109 -646
  251. package/src/panels/settings-pane.ts +102 -86
  252. package/src/panels/signals-panel.ts +1318 -1172
  253. package/src/panels/statement-editor.ts +556 -428
  254. package/src/panels/style-panel.ts +1367 -630
  255. package/src/panels/style-utils.ts +36 -32
  256. package/src/panels/stylebook-doc.ts +30 -73
  257. package/src/panels/stylebook-layers-panel.ts +202 -109
  258. package/src/panels/stylebook-panel.ts +124 -88
  259. package/src/panels/tab-strip.ts +385 -287
  260. package/src/platforms/cloud.ts +347 -86
  261. package/src/platforms/devserver.ts +13 -0
  262. package/src/publish/deploy-checklist.ts +124 -78
  263. package/src/publish/publish-commands.ts +4 -12
  264. package/src/publish/publish-panel.ts +165 -299
  265. package/src/services/a11y-report.ts +100 -43
  266. package/src/services/ai-command-tools.ts +468 -0
  267. package/src/services/ai-import-tools.ts +50 -12
  268. package/src/services/ai-models.ts +108 -1
  269. package/src/services/ai-session-store.ts +6 -0
  270. package/src/services/ai-system-prompt.ts +110 -18
  271. package/src/services/ai-tools.ts +28 -143
  272. package/src/services/ai-write-report.ts +174 -0
  273. package/src/services/context-manager.ts +12 -1
  274. package/src/services/document-assistant.ts +50 -2
  275. package/src/services/idle.ts +16 -14
  276. package/src/services/live-surfaces.ts +237 -0
  277. package/src/services/model-uri.ts +75 -0
  278. package/src/services/notify.ts +63 -6
  279. package/src/services/popover-report.ts +325 -0
  280. package/src/services/surface-registry.ts +56 -0
  281. package/src/services/tool-executor.ts +17 -0
  282. package/src/settings/contexts-section.ts +301 -299
  283. package/src/settings/contributed-section.ts +361 -222
  284. package/src/settings/css-vars-editor.ts +320 -325
  285. package/src/settings/defs-editor.ts +572 -535
  286. package/src/settings/dependencies-editor.ts +79 -107
  287. package/src/settings/extension-commands.ts +379 -0
  288. package/src/settings/extension-rows.ts +113 -0
  289. package/src/settings/extensions-section.ts +287 -0
  290. package/src/settings/general-settings.ts +130 -181
  291. package/src/settings/head-editor.ts +10 -305
  292. package/src/settings/locales-section.ts +146 -196
  293. package/src/settings/preferences-accounts.ts +193 -12
  294. package/src/settings/preferences-dialog.ts +384 -307
  295. package/src/settings/project-sections.ts +108 -181
  296. package/src/settings/schema-field-ui.ts +11 -399
  297. package/src/settings/section-registry.ts +8 -1
  298. package/src/settings/settings-document.ts +31 -29
  299. package/src/shell/tree.ts +42 -87
  300. package/src/shell.ts +48 -36
  301. package/src/studio.ts +90 -67
  302. package/src/style/token-ref.ts +0 -39
  303. package/src/surfaces/about.json +263 -0
  304. package/src/surfaces/about.ts +163 -0
  305. package/src/surfaces/add-repo.json +486 -0
  306. package/src/surfaces/add-repo.ts +236 -0
  307. package/src/surfaces/ai-chat.json +2185 -0
  308. package/src/surfaces/ai-chat.ts +549 -0
  309. package/src/surfaces/ai-credentials-form.json +332 -0
  310. package/src/surfaces/ai-credentials-form.ts +151 -0
  311. package/src/surfaces/ai-managed-connect.json +108 -0
  312. package/src/surfaces/ai-managed-connect.ts +123 -0
  313. package/src/surfaces/ai-model-picker.json +114 -0
  314. package/src/surfaces/ai-model-picker.ts +139 -0
  315. package/src/surfaces/block-action-bar.json +640 -0
  316. package/src/surfaces/block-action-bar.ts +368 -0
  317. package/src/surfaces/bottom-dock.json +154 -0
  318. package/src/surfaces/bottom-dock.ts +125 -0
  319. package/src/surfaces/canvas-stage.json +444 -0
  320. package/src/surfaces/canvas-stage.ts +253 -0
  321. package/src/surfaces/cf-account-picker.json +300 -0
  322. package/src/surfaces/cf-account-picker.ts +214 -0
  323. package/src/surfaces/commandbar.json +984 -0
  324. package/src/{panels/toolbar.ts → surfaces/commandbar.ts} +414 -389
  325. package/src/surfaces/convert-repeater.json +309 -0
  326. package/src/surfaces/convert-repeater.ts +276 -0
  327. package/src/surfaces/data-actions.json +157 -0
  328. package/src/surfaces/data-actions.ts +115 -0
  329. package/src/surfaces/dialog.json +289 -0
  330. package/src/surfaces/dialog.ts +343 -0
  331. package/src/surfaces/diff-toolbar.json +266 -0
  332. package/src/surfaces/diff-toolbar.ts +139 -0
  333. package/src/surfaces/doc-header.json +739 -0
  334. package/src/surfaces/doc-header.ts +223 -0
  335. package/src/surfaces/empty-state.json +152 -0
  336. package/src/surfaces/empty-state.ts +173 -0
  337. package/src/surfaces/entry-editor.json +281 -0
  338. package/src/surfaces/entry-editor.ts +191 -0
  339. package/src/surfaces/expression-editor.json +977 -0
  340. package/src/surfaces/expression-editor.ts +266 -0
  341. package/src/surfaces/files-panel.json +592 -0
  342. package/src/surfaces/files-panel.ts +301 -0
  343. package/src/surfaces/formula-palette.json +364 -0
  344. package/src/surfaces/formula-palette.ts +340 -0
  345. package/src/surfaces/git-panel.json +1822 -0
  346. package/src/surfaces/git-panel.ts +272 -0
  347. package/src/surfaces/github-auth.json +112 -0
  348. package/src/surfaces/github-auth.ts +139 -0
  349. package/src/surfaces/github-publish.json +206 -0
  350. package/src/surfaces/github-publish.ts +169 -0
  351. package/src/surfaces/grid-cell.json +218 -0
  352. package/src/surfaces/grid-cell.ts +120 -0
  353. package/src/surfaces/grid-open.json +147 -0
  354. package/src/surfaces/grid-open.ts +87 -0
  355. package/src/surfaces/grid-panel.json +706 -0
  356. package/src/surfaces/grid-panel.ts +206 -0
  357. package/src/surfaces/grid-replace.json +179 -0
  358. package/src/surfaces/grid-replace.ts +89 -0
  359. package/src/surfaces/grid-views.json +446 -0
  360. package/src/surfaces/grid-views.ts +172 -0
  361. package/src/surfaces/inspector-dock.json +187 -0
  362. package/src/surfaces/inspector-dock.ts +161 -0
  363. package/src/surfaces/jump-bar.json +243 -0
  364. package/src/surfaces/jump-bar.ts +129 -0
  365. package/src/surfaces/jxsuite-update.json +146 -0
  366. package/src/surfaces/jxsuite-update.ts +149 -0
  367. package/src/surfaces/library-pane.json +1509 -0
  368. package/src/surfaces/library-pane.ts +363 -0
  369. package/src/surfaces/logic-panel.json +1949 -0
  370. package/src/surfaces/logic-panel.ts +369 -0
  371. package/src/surfaces/logic-workspace.json +685 -0
  372. package/src/surfaces/logic-workspace.ts +238 -0
  373. package/src/surfaces/media-browser.json +303 -0
  374. package/src/surfaces/media-browser.ts +145 -0
  375. package/src/surfaces/media-field.json +163 -0
  376. package/src/surfaces/media-field.ts +116 -0
  377. package/src/surfaces/media-pane.json +513 -0
  378. package/src/surfaces/media-pane.ts +197 -0
  379. package/src/surfaces/menu.json +232 -0
  380. package/src/surfaces/menu.ts +375 -0
  381. package/src/surfaces/navigator-dock.json +113 -0
  382. package/src/surfaces/navigator-dock.ts +130 -0
  383. package/src/surfaces/new-project.json +1654 -0
  384. package/src/surfaces/new-project.ts +411 -0
  385. package/src/surfaces/palette.json +535 -0
  386. package/src/surfaces/pane-context.json +1497 -0
  387. package/src/surfaces/pane-context.ts +513 -0
  388. package/src/surfaces/pane-grid.json +188 -0
  389. package/src/surfaces/pane-grid.ts +250 -0
  390. package/src/surfaces/panel-activity.json +563 -0
  391. package/src/surfaces/panel-activity.ts +187 -0
  392. package/src/surfaces/panel-data.json +141 -0
  393. package/src/surfaces/panel-data.ts +156 -0
  394. package/src/surfaces/panel-deploy-checklist.json +283 -0
  395. package/src/surfaces/panel-deploy-checklist.ts +175 -0
  396. package/src/surfaces/panel-elements.json +367 -0
  397. package/src/surfaces/panel-elements.ts +175 -0
  398. package/src/surfaces/panel-i18n.json +569 -0
  399. package/src/surfaces/panel-i18n.ts +212 -0
  400. package/src/surfaces/panel-imports.json +780 -0
  401. package/src/surfaces/panel-imports.ts +209 -0
  402. package/src/surfaces/panel-outline.json +734 -0
  403. package/src/surfaces/panel-outline.ts +326 -0
  404. package/src/surfaces/panel-page.json +1017 -0
  405. package/src/surfaces/panel-page.ts +240 -0
  406. package/src/surfaces/panel-problems.json +497 -0
  407. package/src/surfaces/panel-problems.ts +157 -0
  408. package/src/surfaces/panel-signals.json +1973 -0
  409. package/src/surfaces/panel-signals.ts +436 -0
  410. package/src/surfaces/panel-stylebook-layers.json +317 -0
  411. package/src/surfaces/panel-stylebook-layers.ts +155 -0
  412. package/src/surfaces/preferences.json +850 -0
  413. package/src/surfaces/preferences.ts +332 -0
  414. package/src/surfaces/progress-modal.json +110 -0
  415. package/src/surfaces/progress-modal.ts +150 -0
  416. package/src/surfaces/properties-panel.json +1578 -0
  417. package/src/surfaces/properties-panel.ts +337 -0
  418. package/src/surfaces/publish.json +945 -0
  419. package/src/surfaces/publish.ts +392 -0
  420. package/src/surfaces/push-plan.json +110 -0
  421. package/src/surfaces/push-plan.ts +87 -0
  422. package/src/surfaces/rail.json +255 -0
  423. package/src/surfaces/rail.ts +206 -0
  424. package/src/surfaces/reference-field.json +191 -0
  425. package/src/surfaces/reference-field.ts +168 -0
  426. package/src/surfaces/schema-builder.json +1011 -0
  427. package/src/surfaces/schema-builder.ts +181 -0
  428. package/src/surfaces/schema-form.json +1013 -0
  429. package/src/surfaces/schema-form.ts +287 -0
  430. package/src/surfaces/secret-field.json +60 -0
  431. package/src/surfaces/secret-field.ts +146 -0
  432. package/src/surfaces/seo.json +932 -0
  433. package/src/surfaces/seo.ts +334 -0
  434. package/src/surfaces/settings-contexts.json +538 -0
  435. package/src/surfaces/settings-contexts.ts +196 -0
  436. package/src/surfaces/settings-contributed.json +489 -0
  437. package/src/surfaces/settings-contributed.ts +199 -0
  438. package/src/surfaces/settings-css-vars.json +820 -0
  439. package/src/surfaces/settings-css-vars.ts +199 -0
  440. package/src/surfaces/settings-defs.json +1363 -0
  441. package/src/surfaces/settings-defs.ts +232 -0
  442. package/src/surfaces/settings-deploy.json +108 -0
  443. package/src/surfaces/settings-deploy.ts +135 -0
  444. package/src/surfaces/settings-extensions.json +486 -0
  445. package/src/surfaces/settings-extensions.ts +201 -0
  446. package/src/surfaces/settings-head.json +779 -0
  447. package/src/surfaces/settings-head.ts +397 -0
  448. package/src/surfaces/settings-locales.json +420 -0
  449. package/src/surfaces/settings-locales.ts +137 -0
  450. package/src/surfaces/settings-overview.json +423 -0
  451. package/src/surfaces/settings-overview.ts +218 -0
  452. package/src/surfaces/settings-packages.json +549 -0
  453. package/src/surfaces/settings-packages.ts +158 -0
  454. package/src/surfaces/settings-pane.json +147 -0
  455. package/src/surfaces/settings-pane.ts +150 -0
  456. package/src/surfaces/settings-rawjson.json +104 -0
  457. package/src/surfaces/settings-rawjson.ts +107 -0
  458. package/src/surfaces/shell.json +99 -0
  459. package/src/surfaces/shell.ts +30 -0
  460. package/src/surfaces/slash-menu.json +180 -0
  461. package/src/surfaces/slash-menu.ts +272 -0
  462. package/src/surfaces/statements.json +607 -0
  463. package/src/surfaces/statements.ts +238 -0
  464. package/src/surfaces/statusbar.json +94 -0
  465. package/src/{panels → surfaces}/statusbar.ts +163 -90
  466. package/src/surfaces/style-panel.json +1643 -0
  467. package/src/surfaces/style-panel.ts +338 -0
  468. package/src/surfaces/stylebook-chrome.json +122 -0
  469. package/src/surfaces/stylebook-chrome.ts +122 -0
  470. package/src/surfaces/tab-strip.json +448 -0
  471. package/src/surfaces/tab-strip.ts +200 -0
  472. package/src/surfaces/target-line.json +419 -0
  473. package/src/surfaces/target-line.ts +349 -0
  474. package/src/surfaces/toasts.json +189 -0
  475. package/src/surfaces/welcome.json +571 -0
  476. package/src/surfaces/welcome.ts +338 -0
  477. package/src/tabs/project-config.ts +71 -15
  478. package/src/tabs/tab.ts +52 -1
  479. package/src/types.ts +49 -3
  480. package/src/ui/ai-credentials-form.ts +104 -100
  481. package/src/ui/ai-managed-connect.ts +116 -49
  482. package/src/ui/ai-model-picker.ts +102 -47
  483. package/src/ui/cf-account-picker.ts +135 -0
  484. package/src/ui/color-selector.ts +80 -301
  485. package/src/ui/dynamic-slot.ts +43 -283
  486. package/src/ui/expression-editor.ts +797 -576
  487. package/src/ui/field-input.ts +8 -181
  488. package/src/ui/form-controls.ts +838 -366
  489. package/src/ui/formula-chips.ts +80 -85
  490. package/src/ui/kit.ts +33 -0
  491. package/src/ui/layers.ts +293 -593
  492. package/src/ui/media-picker.ts +203 -284
  493. package/src/ui/panel-resize.ts +251 -64
  494. package/src/ui/popover-surface.ts +153 -0
  495. package/src/ui/progress-modal.ts +22 -60
  496. package/src/ui/regions.ts +13 -7
  497. package/src/ui/schema-form.ts +906 -440
  498. package/src/ui/surface.ts +122 -0
  499. package/src/ui/unit-selector.ts +15 -131
  500. package/src/ui/value-source.ts +14 -0
  501. package/src/ui/virtual-window.ts +88 -9
  502. package/src/utils/canvas-media.ts +72 -0
  503. package/src/utils/edit-display.ts +7 -2
  504. package/src/utils/element-selectors.ts +69 -0
  505. package/src/utils/link-target.ts +2 -2
  506. package/src/utils/studio-utils.ts +0 -10
  507. package/src/view.ts +15 -0
  508. package/src/workspace/pane-derive.ts +36 -14
  509. package/src/workspace/workspace.ts +15 -6
  510. package/styles/canvas.css +72 -467
  511. package/styles/forced-colors.css +48 -82
  512. package/styles/forced-colors.json +41 -0
  513. package/styles/inspector.css +2 -1301
  514. package/styles/overlays.css +96 -2823
  515. package/styles/panels.css +6 -1423
  516. package/styles/shell-frame.css +272 -0
  517. package/styles/shell-frame.json +227 -0
  518. package/styles/shell.css +0 -1532
  519. package/styles/tokens.css +65 -136
  520. package/styles/tokens.json +84 -0
  521. package/dist/chunks/Overlay.dev-723bv0df.js +0 -23
  522. package/dist/chunks/adapter-jkcyhgtr.js +0 -14
  523. package/dist/chunks/cf-settings-45fgyx8y.js +0 -21
  524. package/dist/chunks/components-zmx10t62.js +0 -21
  525. package/dist/chunks/content-source-t88vb2dr.js +0 -49
  526. package/dist/chunks/convert-to-repeater-dhw1nrz9.js +0 -268
  527. package/dist/chunks/editor-cptq5jp1.js +0 -37
  528. package/dist/chunks/entry-commands-6z6bhxg0.js +0 -46
  529. package/dist/chunks/extension-sections-3ydgjbdy.js +0 -311
  530. package/dist/chunks/file-ops-rskmhxyq.js +0 -48
  531. package/dist/chunks/files-0cratsr0.js +0 -92
  532. package/dist/chunks/flow-qbfgrpeh.js +0 -634
  533. package/dist/chunks/focus-trap.esm-ggawf1hy.js +0 -1024
  534. package/dist/chunks/focus-visible-6eg70z18.js +0 -167
  535. package/dist/chunks/format-host-tgcmfs4p.js +0 -43
  536. package/dist/chunks/fs-events-hrrg328g.js +0 -21
  537. package/dist/chunks/git-panel-0yywyk76.js +0 -58
  538. package/dist/chunks/index-zqxmqxmh.js +0 -109
  539. package/dist/chunks/jx-validate-8q2g05x1.js +0 -19
  540. package/dist/chunks/layers-panel-z3tqe5cd.js +0 -67
  541. package/dist/chunks/layers-q75tj2mf.js +0 -45
  542. package/dist/chunks/lit-html-j055mkh8.js +0 -22
  543. package/dist/chunks/monaco-buffer-dqw0xxpx.js +0 -25
  544. package/dist/chunks/notify-yjm79n4s.js +0 -30
  545. package/dist/chunks/parse-24d0gay3.js +0 -19
  546. package/dist/chunks/platform-ba1j1gbh.js +0 -16
  547. package/dist/chunks/publish-panel-54jvhskq.js +0 -28
  548. package/dist/chunks/quick-search-cgfmjwq5.js +0 -65
  549. package/dist/chunks/references-3qx8fkyp.js +0 -29
  550. package/dist/chunks/right-panel-cqp8bc4v.js +0 -46
  551. package/dist/chunks/section-registry-sx1ykvkw.js +0 -30
  552. package/dist/chunks/sp-overlay.dev-yztmtrs1.js +0 -18
  553. package/dist/chunks/sp-popover.dev-n2etmqdp.js +0 -9
  554. package/dist/chunks/sp-progress-circle.dev-hryhj0es.js +0 -15
  555. package/dist/chunks/sp-tooltip.dev-xcvpnqr0.js +0 -15
  556. package/dist/chunks/sp-tray.dev-tkbscm06.js +0 -199
  557. package/dist/chunks/studio-0a5qrh01.js +0 -3
  558. package/dist/chunks/studio-18j3bmcz.js +0 -14
  559. package/dist/chunks/studio-1hbqnt19.js +0 -1823
  560. package/dist/chunks/studio-4rzyrtjz.js +0 -426
  561. package/dist/chunks/studio-5rywrkxz.js +0 -1903
  562. package/dist/chunks/studio-7afd21nw.js +0 -67
  563. package/dist/chunks/studio-7nte2hj4.js +0 -613
  564. package/dist/chunks/studio-8jpzqk5s.js +0 -128
  565. package/dist/chunks/studio-8q517dfr.js +0 -24
  566. package/dist/chunks/studio-9c4k4vhv.js +0 -357
  567. package/dist/chunks/studio-9n4mrmnb.js +0 -184
  568. package/dist/chunks/studio-a0tswy0w.js +0 -58
  569. package/dist/chunks/studio-ahjfdy6e.js +0 -387
  570. package/dist/chunks/studio-ahnzd9rg.js +0 -23
  571. package/dist/chunks/studio-bw5dz6km.js +0 -608
  572. package/dist/chunks/studio-cb12yz9w.js +0 -15055
  573. package/dist/chunks/studio-d1ks410h.js +0 -175
  574. package/dist/chunks/studio-eqq1w638.js +0 -113
  575. package/dist/chunks/studio-ex8zyp98.js +0 -1813
  576. package/dist/chunks/studio-h4rxscfc.js +0 -10
  577. package/dist/chunks/studio-kyh9avvv.js +0 -28
  578. package/dist/chunks/studio-kyk2v92a.js +0 -659
  579. package/dist/chunks/studio-m3hwn7y1.js +0 -120
  580. package/dist/chunks/studio-mmv72a27.js +0 -4344
  581. package/dist/chunks/studio-n8b9wqwy.js +0 -291
  582. package/dist/chunks/studio-pdfjntbk.js +0 -6939
  583. package/dist/chunks/studio-qq6jym04.js +0 -53
  584. package/dist/chunks/studio-r1r7afxx.js +0 -1066
  585. package/dist/chunks/studio-rdx8jcxy.js +0 -83
  586. package/dist/chunks/studio-rn9ta2mz.js +0 -111
  587. package/dist/chunks/studio-t7qb9db8.js +0 -665
  588. package/dist/chunks/studio-tmc5emrw.js +0 -8
  589. package/dist/chunks/studio-txwkm6p9.js +0 -12
  590. package/dist/chunks/studio-v38hmvx1.js +0 -390
  591. package/dist/chunks/studio-vadzgkrz.js +0 -318
  592. package/dist/chunks/studio-wx5609xd.js +0 -341
  593. package/dist/chunks/studio-z6nw2ek2.js +0 -18
  594. package/dist/chunks/studio-zczps70q.js +0 -378
  595. package/dist/chunks/workspace-xpres40v.js +0 -89
  596. package/src/panels/activity-bar.ts +0 -251
  597. package/src/panels/panel-scheduler.ts +0 -219
  598. package/src/panels/style-inputs.ts +0 -237
  599. package/src/panels/target-line.ts +0 -274
  600. package/src/panels/welcome-screen.ts +0 -346
  601. package/src/ui/button-group.ts +0 -102
  602. package/src/ui/choice-field.ts +0 -72
  603. package/src/ui/field-row.ts +0 -249
  604. package/src/ui/formula-palette.ts +0 -213
  605. package/src/ui/icons.ts +0 -52
  606. package/src/ui/jx-theme.ts +0 -130
  607. package/src/ui/spectrum.ts +0 -340
  608. package/src/ui/value-selector.ts +0 -174
  609. package/src/ui/widgets.ts +0 -172
  610. /package/dist/chunks/{javascript-s56a8dpk.css → javascript-4abzjcy9.css} +0 -0
  611. /package/dist/chunks/{jsonMode-zr9sw0c7.css → jsonMode-vvmpmk34.css} +0 -0
  612. /package/dist/chunks/{monaco-setup-pav68602.css → monaco-setup-8vbyqnhr.css} +0 -0
  613. /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
  }),