@jxsuite/studio 4.1.0 → 5.1.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 (612) 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-qwkm9zrf.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-vj72gqtq.js +20 -0
  15. package/dist/chunks/content-source-5mex4x1h.js +46 -0
  16. package/dist/chunks/convert-to-repeater-n64h8znc.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-234hjnqw.js +42 -0
  20. package/dist/chunks/extension-sections-3hpcambp.js +651 -0
  21. package/dist/chunks/file-ops-8yhd83xr.js +47 -0
  22. package/dist/chunks/files-hnxqxtsg.js +72 -0
  23. package/dist/chunks/format-host-779sfp2n.js +40 -0
  24. package/dist/chunks/fs-events-b88z7g4h.js +18 -0
  25. package/dist/chunks/git-panel-xwj8fpab.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-syr0f9sb.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-ff81wthn.js +30 -0
  37. package/dist/chunks/layers-panel-pnnghw12.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-cqhhxhhe.js +33 -0
  41. package/dist/chunks/menu-9p2w46gj.js +15 -0
  42. package/dist/chunks/menu-q99sy9rf.js +285 -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-5zt840sy.js +27 -0
  52. package/dist/chunks/quick-search-3748564r.js +45 -0
  53. package/dist/chunks/references-qvvxej7h.js +26 -0
  54. package/dist/chunks/right-panel-eccz92qt.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-kryvsw6j.js → studio-1k325pwf.js} +48 -48
  60. package/dist/chunks/{studio-b55y6ewx.js → studio-1knfqgy7.js} +58 -58
  61. package/dist/chunks/{studio-2b946z26.js → studio-1z1kcjv4.js} +3628 -6095
  62. package/dist/chunks/studio-3773086z.js +111 -0
  63. package/dist/chunks/{studio-4r1pv6zf.js → studio-38emantv.js} +22 -5
  64. package/dist/chunks/{studio-x813zp48.js → studio-3rz5md06.js} +30 -30
  65. package/dist/chunks/{studio-ynxwkrzd.js → studio-4wbb9ct0.js} +1953 -1785
  66. package/dist/chunks/studio-5te5tqng.js +81 -0
  67. package/dist/chunks/studio-5wt9nvhd.js +4763 -0
  68. package/dist/chunks/{studio-p4ngc046.js → studio-6ewqv390.js} +9 -9
  69. package/dist/chunks/{studio-9ab8gqsm.js → studio-6nzaktvy.js} +7 -7
  70. package/dist/chunks/studio-6xa21ej9.js +28 -0
  71. package/dist/chunks/{studio-5ehhackm.js → studio-727xh0e3.js} +19 -19
  72. package/dist/chunks/{studio-gcye7qka.js → studio-7e11jpa9.js} +21 -21
  73. package/dist/chunks/{studio-q7v10cg2.js → studio-7r42c6ca.js} +17 -7
  74. package/dist/chunks/studio-7xnmgv66.js +13615 -0
  75. package/dist/chunks/studio-8scdew0e.js +258 -0
  76. package/dist/chunks/{studio-fbf92ckf.js → studio-93zafjcm.js} +275 -163
  77. package/dist/chunks/{studio-dg7zyt5x.js → studio-98b3z07n.js} +1 -1
  78. package/dist/chunks/studio-a79k90n9.js +1162 -0
  79. package/dist/chunks/studio-aj37eer3.js +422 -0
  80. package/dist/chunks/studio-b0k5tqgp.js +2432 -0
  81. package/dist/chunks/studio-b4s9eykd.js +2811 -0
  82. package/dist/chunks/{studio-f3wqjtk5.js → studio-d0300636.js} +5076 -18
  83. package/dist/chunks/{studio-b9b6t1dg.js → studio-e1f24eff.js} +5664 -5666
  84. package/dist/chunks/studio-e1pgga3k.js +839 -0
  85. package/dist/chunks/{studio-g1wdche0.js → studio-esrxgwh6.js} +80 -80
  86. package/dist/chunks/{studio-q43a5y9h.js → studio-g3jevyjt.js} +16 -16
  87. package/dist/chunks/studio-gb13095a.js +2806 -0
  88. package/dist/chunks/{studio-3n4ndrzh.js → studio-h4nvnjvz.js} +303 -303
  89. package/dist/chunks/{studio-dm19q43r.js → studio-hhvs4sdg.js} +10 -10
  90. package/dist/chunks/{studio-x4qkdat5.js → studio-js8nv53n.js} +1749 -2481
  91. package/dist/chunks/{studio-qjy2z9bv.js → studio-jxp12rnj.js} +32 -137
  92. package/dist/chunks/{studio-trxrabqf.js → studio-k75n7vhp.js} +527 -283
  93. package/dist/chunks/{studio-q9jd1a60.js → studio-kcgff4r0.js} +15 -18
  94. package/dist/chunks/studio-kkqkcp3d.js +17668 -0
  95. package/dist/chunks/studio-mhhyamxf.js +763 -0
  96. package/dist/chunks/{studio-cft9r4r7.js → studio-nq5yf5mp.js} +22 -22
  97. package/dist/chunks/{studio-685s0ytq.js → studio-nqgawfks.js} +572 -572
  98. package/dist/chunks/studio-nzzpvzx9.js +185 -0
  99. package/dist/chunks/{studio-xrapxwwc.js → studio-p1dw8wfw.js} +55 -29
  100. package/dist/chunks/{studio-3bqpr0zh.js → studio-pxbgypmg.js} +299 -110
  101. package/dist/chunks/studio-q0a3yk4n.js +33 -0
  102. package/dist/chunks/{studio-wcy8trfx.js → studio-q3x06vmj.js} +23 -22
  103. package/dist/chunks/{studio-6yw4y3my.js → studio-qhhm6hbt.js} +8 -8
  104. package/dist/chunks/studio-qk4bfsq2.js +362 -0
  105. package/dist/chunks/{studio-msd847zn.js → studio-qk90fgqm.js} +361 -90
  106. package/dist/chunks/{studio-jkftxyvz.js → studio-r6nvacqs.js} +5861 -2154
  107. package/dist/chunks/{studio-53n8bp04.js → studio-r6xmmtfh.js} +1 -1
  108. package/dist/chunks/{studio-tvgyw9xw.js → studio-rnyf3f8e.js} +83 -250
  109. package/dist/chunks/studio-rs76k67r.js +12 -0
  110. package/dist/chunks/studio-sbjyc8bt.js +954 -0
  111. package/dist/chunks/{studio-1wagb550.js → studio-syg2tadj.js} +11 -11
  112. package/dist/chunks/{studio-t5wqwzdh.js → studio-tjjk7jvk.js} +1 -1
  113. package/dist/chunks/studio-tqgryxwg.js +1171 -0
  114. package/dist/chunks/studio-try13h8c.js +8448 -0
  115. package/dist/chunks/studio-vfq8qpjt.js +1219 -0
  116. package/dist/chunks/studio-vhv5tgxm.js +51 -0
  117. package/dist/chunks/{studio-ahn3pdgx.js → studio-vw7mzp6z.js} +52 -38
  118. package/dist/chunks/studio-wqpngpsk.js +174 -0
  119. package/dist/chunks/studio-wqz6dggm.js +1356 -0
  120. package/dist/chunks/{studio-92ab2jqe.js → studio-y9zedyjn.js} +14 -14
  121. package/dist/chunks/studio-yxa81f45.js +12 -0
  122. package/dist/chunks/studio-zdn2g6dt.js +148 -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-t55jdjbx.js +69 -0
  133. package/dist/chunks/{ws-client-6shc5rze.js → ws-client-ny4zc51r.js} +20 -22
  134. package/dist/iframe-entry.js +1873 -673
  135. package/dist/studio.js +52757 -36535
  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 +333 -113
  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 +404 -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 +177 -10
  178. package/src/commands/run-reported.ts +120 -0
  179. package/src/content/entry-commands.ts +16 -23
  180. package/src/content/entry-editor.ts +144 -101
  181. package/src/editor/canvas-slash-bridge.ts +2 -2
  182. package/src/editor/context-menu.ts +49 -200
  183. package/src/editor/convert-to-component.ts +42 -82
  184. package/src/editor/convert-to-repeater.ts +132 -173
  185. package/src/editor/inline-edit.ts +11 -0
  186. package/src/editor/shortcuts.ts +76 -49
  187. package/src/editor/slash-menu.ts +150 -157
  188. package/src/files/components.ts +39 -0
  189. package/src/files/file-ops.ts +117 -11
  190. package/src/files/files.ts +944 -660
  191. package/src/files/json-layout.ts +474 -0
  192. package/src/files/serialize-document.ts +8 -1
  193. package/src/format/convert-file.ts +16 -10
  194. package/src/format/format-host.ts +50 -1
  195. package/src/github/github-auth.ts +71 -29
  196. package/src/github/github-publish.ts +57 -65
  197. package/src/grid/cell-editors.ts +46 -4
  198. package/src/grid/cell-popovers.ts +65 -62
  199. package/src/grid/grid-open.ts +86 -64
  200. package/src/grid/grid-panel.ts +390 -469
  201. package/src/grid/redirects-grid.ts +40 -44
  202. package/src/hosting/layout.ts +2 -0
  203. package/src/i18n/i18n-commands.ts +137 -62
  204. package/src/media/media-pane.ts +83 -133
  205. package/src/new-project/add-repo-modal.ts +125 -176
  206. package/src/new-project/import-tab.ts +181 -252
  207. package/src/new-project/location-fields.ts +93 -160
  208. package/src/new-project/new-project-modal.ts +376 -367
  209. package/src/packages/jxsuite-update.ts +76 -18
  210. package/src/packages/pull-package-sync.ts +5 -1
  211. package/src/panels/activity-panel.ts +137 -104
  212. package/src/panels/ai-chat/chat-markdown.ts +15 -13
  213. package/src/panels/ai-chat/chat-view.ts +307 -472
  214. package/src/panels/ai-chat/composer.ts +175 -163
  215. package/src/panels/ai-chat/sessions-view.ts +31 -62
  216. package/src/panels/ai-panel.ts +213 -185
  217. package/src/panels/block-action-bar.ts +444 -645
  218. package/src/panels/bottom-dock.ts +112 -47
  219. package/src/panels/chat-panel.ts +19 -14
  220. package/src/panels/data-explorer.ts +141 -102
  221. package/src/panels/data-grid.ts +183 -150
  222. package/src/panels/dnd.ts +75 -20
  223. package/src/panels/editors.ts +26 -19
  224. package/src/panels/elements-panel.ts +243 -186
  225. package/src/panels/empty-state.ts +26 -42
  226. package/src/panels/events-panel.ts +950 -729
  227. package/src/panels/formula-workspace.ts +434 -207
  228. package/src/panels/frontmatter-fields.ts +185 -153
  229. package/src/panels/frontmatter-panel.ts +411 -187
  230. package/src/panels/git-diff-open.ts +148 -0
  231. package/src/panels/git-panel.ts +679 -605
  232. package/src/panels/head-panel.ts +617 -435
  233. package/src/panels/i18n-panel.ts +148 -135
  234. package/src/panels/imports-panel.ts +388 -414
  235. package/src/panels/jump-bar.ts +175 -147
  236. package/src/panels/layers-panel.ts +785 -671
  237. package/src/panels/left-panel.ts +166 -94
  238. package/src/panels/navigator-panels.ts +1 -1
  239. package/src/panels/pane-context.ts +529 -677
  240. package/src/panels/pane-grid.ts +265 -241
  241. package/src/panels/panel-registry.ts +15 -4
  242. package/src/panels/problems-panel.ts +181 -99
  243. package/src/panels/properties-panel.ts +1525 -966
  244. package/src/panels/provenance.ts +31 -113
  245. package/src/panels/quick-search.ts +223 -184
  246. package/src/panels/right-panel.ts +135 -169
  247. package/src/panels/seo-modal.ts +346 -167
  248. package/src/panels/settings-menu.ts +114 -661
  249. package/src/panels/settings-pane.ts +102 -86
  250. package/src/panels/signals-panel.ts +1318 -1172
  251. package/src/panels/statement-editor.ts +556 -428
  252. package/src/panels/style-panel.ts +1367 -630
  253. package/src/panels/style-utils.ts +36 -32
  254. package/src/panels/stylebook-doc.ts +30 -73
  255. package/src/panels/stylebook-layers-panel.ts +202 -109
  256. package/src/panels/stylebook-panel.ts +124 -88
  257. package/src/panels/tab-strip.ts +386 -288
  258. package/src/platforms/cloud.ts +106 -16
  259. package/src/platforms/devserver.ts +13 -0
  260. package/src/publish/deploy-checklist.ts +124 -78
  261. package/src/publish/publish-commands.ts +4 -12
  262. package/src/publish/publish-panel.ts +134 -328
  263. package/src/services/a11y-report.ts +100 -43
  264. package/src/services/ai-command-tools.ts +538 -0
  265. package/src/services/ai-project-tools.ts +8 -2
  266. package/src/services/ai-session-store.ts +6 -0
  267. package/src/services/ai-system-prompt.ts +112 -24
  268. package/src/services/ai-tools.ts +36 -212
  269. package/src/services/ai-write-report.ts +174 -0
  270. package/src/services/document-assistant.ts +59 -8
  271. package/src/services/idle.ts +16 -14
  272. package/src/services/live-surfaces.ts +237 -0
  273. package/src/services/model-uri.ts +75 -0
  274. package/src/services/notify.ts +63 -6
  275. package/src/services/popover-report.ts +325 -0
  276. package/src/services/surface-registry.ts +56 -0
  277. package/src/services/tool-executor.ts +6 -0
  278. package/src/settings/contexts-section.ts +301 -299
  279. package/src/settings/contributed-section.ts +361 -222
  280. package/src/settings/css-vars-editor.ts +320 -325
  281. package/src/settings/defs-editor.ts +572 -535
  282. package/src/settings/dependencies-editor.ts +79 -107
  283. package/src/settings/extension-commands.ts +379 -0
  284. package/src/settings/extension-rows.ts +113 -0
  285. package/src/settings/extensions-section.ts +287 -0
  286. package/src/settings/general-settings.ts +130 -181
  287. package/src/settings/head-editor.ts +10 -305
  288. package/src/settings/locales-section.ts +146 -196
  289. package/src/settings/preferences-accounts.ts +1 -1
  290. package/src/settings/preferences-dialog.ts +350 -330
  291. package/src/settings/project-sections.ts +108 -181
  292. package/src/settings/schema-field-ui.ts +11 -399
  293. package/src/settings/section-registry.ts +8 -1
  294. package/src/settings/settings-document.ts +31 -29
  295. package/src/shell/tree.ts +42 -87
  296. package/src/shell.ts +48 -36
  297. package/src/studio.ts +104 -69
  298. package/src/style/token-ref.ts +0 -39
  299. package/src/surfaces/about.json +263 -0
  300. package/src/surfaces/about.ts +163 -0
  301. package/src/surfaces/add-repo.json +486 -0
  302. package/src/surfaces/add-repo.ts +236 -0
  303. package/src/surfaces/ai-chat.json +2185 -0
  304. package/src/surfaces/ai-chat.ts +549 -0
  305. package/src/surfaces/ai-credentials-form.json +332 -0
  306. package/src/surfaces/ai-credentials-form.ts +151 -0
  307. package/src/surfaces/ai-managed-connect.json +108 -0
  308. package/src/surfaces/ai-managed-connect.ts +123 -0
  309. package/src/surfaces/ai-model-picker.json +114 -0
  310. package/src/surfaces/ai-model-picker.ts +139 -0
  311. package/src/surfaces/block-action-bar.json +640 -0
  312. package/src/surfaces/block-action-bar.ts +368 -0
  313. package/src/surfaces/bottom-dock.json +154 -0
  314. package/src/surfaces/bottom-dock.ts +125 -0
  315. package/src/surfaces/canvas-stage.json +444 -0
  316. package/src/surfaces/canvas-stage.ts +253 -0
  317. package/src/surfaces/cf-account-picker.json +300 -0
  318. package/src/surfaces/cf-account-picker.ts +214 -0
  319. package/src/surfaces/commandbar.json +985 -0
  320. package/src/{panels/toolbar.ts → surfaces/commandbar.ts} +417 -391
  321. package/src/surfaces/convert-repeater.json +309 -0
  322. package/src/surfaces/convert-repeater.ts +276 -0
  323. package/src/surfaces/data-actions.json +157 -0
  324. package/src/surfaces/data-actions.ts +115 -0
  325. package/src/surfaces/dialog.json +289 -0
  326. package/src/surfaces/dialog.ts +343 -0
  327. package/src/surfaces/diff-toolbar.json +266 -0
  328. package/src/surfaces/diff-toolbar.ts +139 -0
  329. package/src/surfaces/doc-header.json +739 -0
  330. package/src/surfaces/doc-header.ts +223 -0
  331. package/src/surfaces/empty-state.json +152 -0
  332. package/src/surfaces/empty-state.ts +173 -0
  333. package/src/surfaces/entry-editor.json +281 -0
  334. package/src/surfaces/entry-editor.ts +191 -0
  335. package/src/surfaces/expression-editor.json +977 -0
  336. package/src/surfaces/expression-editor.ts +266 -0
  337. package/src/surfaces/files-panel.json +592 -0
  338. package/src/surfaces/files-panel.ts +301 -0
  339. package/src/surfaces/formula-palette.json +364 -0
  340. package/src/surfaces/formula-palette.ts +340 -0
  341. package/src/surfaces/git-panel.json +1822 -0
  342. package/src/surfaces/git-panel.ts +272 -0
  343. package/src/surfaces/github-auth.json +112 -0
  344. package/src/surfaces/github-auth.ts +139 -0
  345. package/src/surfaces/github-publish.json +206 -0
  346. package/src/surfaces/github-publish.ts +169 -0
  347. package/src/surfaces/grid-cell.json +218 -0
  348. package/src/surfaces/grid-cell.ts +120 -0
  349. package/src/surfaces/grid-open.json +147 -0
  350. package/src/surfaces/grid-open.ts +87 -0
  351. package/src/surfaces/grid-panel.json +706 -0
  352. package/src/surfaces/grid-panel.ts +206 -0
  353. package/src/surfaces/grid-replace.json +179 -0
  354. package/src/surfaces/grid-replace.ts +89 -0
  355. package/src/surfaces/grid-views.json +446 -0
  356. package/src/surfaces/grid-views.ts +172 -0
  357. package/src/surfaces/inspector-dock.json +187 -0
  358. package/src/surfaces/inspector-dock.ts +161 -0
  359. package/src/surfaces/jump-bar.json +243 -0
  360. package/src/surfaces/jump-bar.ts +129 -0
  361. package/src/surfaces/jxsuite-update.json +146 -0
  362. package/src/surfaces/jxsuite-update.ts +149 -0
  363. package/src/surfaces/library-pane.json +1509 -0
  364. package/src/surfaces/library-pane.ts +363 -0
  365. package/src/surfaces/logic-panel.json +1949 -0
  366. package/src/surfaces/logic-panel.ts +369 -0
  367. package/src/surfaces/logic-workspace.json +685 -0
  368. package/src/surfaces/logic-workspace.ts +238 -0
  369. package/src/surfaces/media-browser.json +303 -0
  370. package/src/surfaces/media-browser.ts +145 -0
  371. package/src/surfaces/media-field.json +163 -0
  372. package/src/surfaces/media-field.ts +116 -0
  373. package/src/surfaces/media-pane.json +513 -0
  374. package/src/surfaces/media-pane.ts +197 -0
  375. package/src/surfaces/menu.json +232 -0
  376. package/src/surfaces/menu.ts +375 -0
  377. package/src/surfaces/navigator-dock.json +113 -0
  378. package/src/surfaces/navigator-dock.ts +130 -0
  379. package/src/surfaces/new-project.json +1654 -0
  380. package/src/surfaces/new-project.ts +411 -0
  381. package/src/surfaces/palette.json +535 -0
  382. package/src/surfaces/pane-context.json +1497 -0
  383. package/src/surfaces/pane-context.ts +513 -0
  384. package/src/surfaces/pane-grid.json +188 -0
  385. package/src/surfaces/pane-grid.ts +250 -0
  386. package/src/surfaces/panel-activity.json +563 -0
  387. package/src/surfaces/panel-activity.ts +187 -0
  388. package/src/surfaces/panel-data.json +141 -0
  389. package/src/surfaces/panel-data.ts +156 -0
  390. package/src/surfaces/panel-deploy-checklist.json +283 -0
  391. package/src/surfaces/panel-deploy-checklist.ts +175 -0
  392. package/src/surfaces/panel-elements.json +367 -0
  393. package/src/surfaces/panel-elements.ts +175 -0
  394. package/src/surfaces/panel-i18n.json +569 -0
  395. package/src/surfaces/panel-i18n.ts +212 -0
  396. package/src/surfaces/panel-imports.json +780 -0
  397. package/src/surfaces/panel-imports.ts +209 -0
  398. package/src/surfaces/panel-outline.json +734 -0
  399. package/src/surfaces/panel-outline.ts +326 -0
  400. package/src/surfaces/panel-page.json +1017 -0
  401. package/src/surfaces/panel-page.ts +240 -0
  402. package/src/surfaces/panel-problems.json +497 -0
  403. package/src/surfaces/panel-problems.ts +157 -0
  404. package/src/surfaces/panel-signals.json +1973 -0
  405. package/src/surfaces/panel-signals.ts +436 -0
  406. package/src/surfaces/panel-stylebook-layers.json +317 -0
  407. package/src/surfaces/panel-stylebook-layers.ts +155 -0
  408. package/src/surfaces/preferences.json +850 -0
  409. package/src/surfaces/preferences.ts +332 -0
  410. package/src/surfaces/progress-modal.json +110 -0
  411. package/src/surfaces/progress-modal.ts +150 -0
  412. package/src/surfaces/properties-panel.json +1578 -0
  413. package/src/surfaces/properties-panel.ts +337 -0
  414. package/src/surfaces/publish.json +945 -0
  415. package/src/surfaces/publish.ts +392 -0
  416. package/src/surfaces/push-plan.json +110 -0
  417. package/src/surfaces/push-plan.ts +87 -0
  418. package/src/surfaces/rail.json +255 -0
  419. package/src/surfaces/rail.ts +206 -0
  420. package/src/surfaces/reference-field.json +191 -0
  421. package/src/surfaces/reference-field.ts +168 -0
  422. package/src/surfaces/schema-builder.json +1011 -0
  423. package/src/surfaces/schema-builder.ts +181 -0
  424. package/src/surfaces/schema-form.json +1013 -0
  425. package/src/surfaces/schema-form.ts +287 -0
  426. package/src/surfaces/secret-field.json +60 -0
  427. package/src/surfaces/secret-field.ts +146 -0
  428. package/src/surfaces/seo.json +932 -0
  429. package/src/surfaces/seo.ts +334 -0
  430. package/src/surfaces/settings-contexts.json +538 -0
  431. package/src/surfaces/settings-contexts.ts +196 -0
  432. package/src/surfaces/settings-contributed.json +489 -0
  433. package/src/surfaces/settings-contributed.ts +199 -0
  434. package/src/surfaces/settings-css-vars.json +820 -0
  435. package/src/surfaces/settings-css-vars.ts +199 -0
  436. package/src/surfaces/settings-defs.json +1363 -0
  437. package/src/surfaces/settings-defs.ts +232 -0
  438. package/src/surfaces/settings-deploy.json +108 -0
  439. package/src/surfaces/settings-deploy.ts +135 -0
  440. package/src/surfaces/settings-extensions.json +486 -0
  441. package/src/surfaces/settings-extensions.ts +201 -0
  442. package/src/surfaces/settings-head.json +779 -0
  443. package/src/surfaces/settings-head.ts +397 -0
  444. package/src/surfaces/settings-locales.json +420 -0
  445. package/src/surfaces/settings-locales.ts +137 -0
  446. package/src/surfaces/settings-overview.json +423 -0
  447. package/src/surfaces/settings-overview.ts +218 -0
  448. package/src/surfaces/settings-packages.json +549 -0
  449. package/src/surfaces/settings-packages.ts +158 -0
  450. package/src/surfaces/settings-pane.json +147 -0
  451. package/src/surfaces/settings-pane.ts +150 -0
  452. package/src/surfaces/settings-rawjson.json +104 -0
  453. package/src/surfaces/settings-rawjson.ts +107 -0
  454. package/src/surfaces/shell.json +99 -0
  455. package/src/surfaces/shell.ts +30 -0
  456. package/src/surfaces/slash-menu.json +180 -0
  457. package/src/surfaces/slash-menu.ts +272 -0
  458. package/src/surfaces/statements.json +607 -0
  459. package/src/surfaces/statements.ts +238 -0
  460. package/src/surfaces/statusbar.json +94 -0
  461. package/src/{panels → surfaces}/statusbar.ts +164 -90
  462. package/src/surfaces/style-panel.json +1643 -0
  463. package/src/surfaces/style-panel.ts +338 -0
  464. package/src/surfaces/stylebook-chrome.json +122 -0
  465. package/src/surfaces/stylebook-chrome.ts +122 -0
  466. package/src/surfaces/tab-strip.json +448 -0
  467. package/src/surfaces/tab-strip.ts +200 -0
  468. package/src/surfaces/target-line.json +419 -0
  469. package/src/surfaces/target-line.ts +349 -0
  470. package/src/surfaces/toasts.json +189 -0
  471. package/src/surfaces/welcome.json +571 -0
  472. package/src/surfaces/welcome.ts +338 -0
  473. package/src/tabs/project-config.ts +111 -21
  474. package/src/tabs/tab.ts +52 -1
  475. package/src/types.ts +22 -0
  476. package/src/ui/ai-credentials-form.ts +104 -100
  477. package/src/ui/ai-managed-connect.ts +54 -36
  478. package/src/ui/ai-model-picker.ts +75 -47
  479. package/src/ui/cf-account-picker.ts +76 -141
  480. package/src/ui/color-selector.ts +80 -301
  481. package/src/ui/dynamic-slot.ts +43 -283
  482. package/src/ui/expression-editor.ts +797 -576
  483. package/src/ui/field-input.ts +8 -181
  484. package/src/ui/form-controls.ts +838 -366
  485. package/src/ui/formula-chips.ts +80 -85
  486. package/src/ui/kit.ts +33 -0
  487. package/src/ui/layers.ts +296 -593
  488. package/src/ui/media-picker.ts +203 -284
  489. package/src/ui/panel-resize.ts +251 -64
  490. package/src/ui/popover-surface.ts +153 -0
  491. package/src/ui/progress-modal.ts +22 -60
  492. package/src/ui/regions.ts +13 -7
  493. package/src/ui/schema-form.ts +906 -440
  494. package/src/ui/surface.ts +122 -0
  495. package/src/ui/unit-selector.ts +15 -131
  496. package/src/ui/value-source.ts +14 -0
  497. package/src/ui/virtual-window.ts +88 -9
  498. package/src/utils/canvas-media.ts +72 -0
  499. package/src/utils/edit-display.ts +7 -2
  500. package/src/utils/element-selectors.ts +69 -0
  501. package/src/utils/link-target.ts +2 -2
  502. package/src/utils/studio-utils.ts +0 -10
  503. package/src/view.ts +15 -0
  504. package/src/workspace/pane-derive.ts +36 -14
  505. package/src/workspace/workspace.ts +75 -6
  506. package/styles/canvas.css +72 -467
  507. package/styles/forced-colors.css +48 -82
  508. package/styles/forced-colors.json +41 -0
  509. package/styles/inspector.css +2 -1301
  510. package/styles/overlays.css +96 -2823
  511. package/styles/panels.css +6 -1423
  512. package/styles/shell-frame.css +272 -0
  513. package/styles/shell-frame.json +227 -0
  514. package/styles/shell.css +0 -1568
  515. package/styles/tokens.css +65 -136
  516. package/styles/tokens.json +84 -0
  517. package/dist/chunks/Overlay.dev-etqjwyf9.js +0 -23
  518. package/dist/chunks/adapter-jkcyhgtr.js +0 -14
  519. package/dist/chunks/cf-account-picker-en86v7x7.js +0 -163
  520. package/dist/chunks/cf-settings-45fgyx8y.js +0 -21
  521. package/dist/chunks/components-zmx10t62.js +0 -21
  522. package/dist/chunks/content-source-48xg86wj.js +0 -49
  523. package/dist/chunks/convert-to-repeater-43tfn7cs.js +0 -268
  524. package/dist/chunks/editor-cptq5jp1.js +0 -37
  525. package/dist/chunks/entry-commands-e63f1xc0.js +0 -46
  526. package/dist/chunks/extension-sections-txv08xxg.js +0 -311
  527. package/dist/chunks/file-ops-wbh4hhsx.js +0 -48
  528. package/dist/chunks/files-3kfkwhk4.js +0 -93
  529. package/dist/chunks/flow-qbfgrpeh.js +0 -634
  530. package/dist/chunks/focus-trap.esm-9t2drp0g.js +0 -1025
  531. package/dist/chunks/focus-visible-6eg70z18.js +0 -167
  532. package/dist/chunks/format-host-w16q81tf.js +0 -43
  533. package/dist/chunks/fs-events-hrrg328g.js +0 -21
  534. package/dist/chunks/git-panel-532whr1y.js +0 -59
  535. package/dist/chunks/index-zqxmqxmh.js +0 -109
  536. package/dist/chunks/jx-validate-8er6gbxy.js +0 -19
  537. package/dist/chunks/layers-panel-07hm7g5j.js +0 -67
  538. package/dist/chunks/layers-q75tj2mf.js +0 -45
  539. package/dist/chunks/lit-html-j055mkh8.js +0 -22
  540. package/dist/chunks/monaco-buffer-dqw0xxpx.js +0 -25
  541. package/dist/chunks/notify-yjm79n4s.js +0 -30
  542. package/dist/chunks/parse-24d0gay3.js +0 -19
  543. package/dist/chunks/platform-ba1j1gbh.js +0 -16
  544. package/dist/chunks/publish-panel-v9st96ja.js +0 -29
  545. package/dist/chunks/quick-search-zccjp6gv.js +0 -66
  546. package/dist/chunks/references-3qx8fkyp.js +0 -29
  547. package/dist/chunks/right-panel-vm3b6kwy.js +0 -46
  548. package/dist/chunks/section-registry-sx1ykvkw.js +0 -30
  549. package/dist/chunks/sp-overlay.dev-qgvhnxf4.js +0 -18
  550. package/dist/chunks/sp-popover.dev-n2etmqdp.js +0 -9
  551. package/dist/chunks/sp-progress-circle.dev-hryhj0es.js +0 -15
  552. package/dist/chunks/sp-tooltip.dev-4sastdnw.js +0 -15
  553. package/dist/chunks/sp-tray.dev-tkbscm06.js +0 -199
  554. package/dist/chunks/studio-0a5qrh01.js +0 -3
  555. package/dist/chunks/studio-0bq6eh7v.js +0 -12
  556. package/dist/chunks/studio-18j3bmcz.js +0 -14
  557. package/dist/chunks/studio-1ngbejft.js +0 -181
  558. package/dist/chunks/studio-4b5z1x43.js +0 -424
  559. package/dist/chunks/studio-4n2b5gzb.js +0 -357
  560. package/dist/chunks/studio-4rzyrtjz.js +0 -426
  561. package/dist/chunks/studio-5fx3g9jg.js +0 -4325
  562. package/dist/chunks/studio-5myknm0a.js +0 -1903
  563. package/dist/chunks/studio-7afd21nw.js +0 -67
  564. package/dist/chunks/studio-7nte2hj4.js +0 -613
  565. package/dist/chunks/studio-8jpzqk5s.js +0 -128
  566. package/dist/chunks/studio-8q517dfr.js +0 -24
  567. package/dist/chunks/studio-950k0dx6.js +0 -6939
  568. package/dist/chunks/studio-9n4mrmnb.js +0 -184
  569. package/dist/chunks/studio-a0tswy0w.js +0 -58
  570. package/dist/chunks/studio-ahnzd9rg.js +0 -23
  571. package/dist/chunks/studio-bgxt93ga.js +0 -62
  572. package/dist/chunks/studio-cmb03bg5.js +0 -13890
  573. package/dist/chunks/studio-d1ks410h.js +0 -175
  574. package/dist/chunks/studio-ef49pnzx.js +0 -387
  575. package/dist/chunks/studio-eqq1w638.js +0 -113
  576. package/dist/chunks/studio-ew0djmhg.js +0 -341
  577. package/dist/chunks/studio-gfgb8pe6.js +0 -1652
  578. package/dist/chunks/studio-h4rxscfc.js +0 -10
  579. package/dist/chunks/studio-jc994dnr.js +0 -1066
  580. package/dist/chunks/studio-kyh9avvv.js +0 -28
  581. package/dist/chunks/studio-m3hwn7y1.js +0 -120
  582. package/dist/chunks/studio-mtfnaqs9.js +0 -659
  583. package/dist/chunks/studio-n6j9t10q.js +0 -318
  584. package/dist/chunks/studio-n8b9wqwy.js +0 -291
  585. package/dist/chunks/studio-qcy05dp8.js +0 -1743
  586. package/dist/chunks/studio-qq6jym04.js +0 -53
  587. package/dist/chunks/studio-rdx8jcxy.js +0 -83
  588. package/dist/chunks/studio-rn9ta2mz.js +0 -111
  589. package/dist/chunks/studio-t7qb9db8.js +0 -665
  590. package/dist/chunks/studio-tmc5emrw.js +0 -8
  591. package/dist/chunks/studio-z6nw2ek2.js +0 -18
  592. package/dist/chunks/studio-zczps70q.js +0 -378
  593. package/dist/chunks/studio-zqazsct2.js +0 -1823
  594. package/dist/chunks/workspace-fjx52ryv.js +0 -89
  595. package/src/panels/activity-bar.ts +0 -251
  596. package/src/panels/panel-scheduler.ts +0 -219
  597. package/src/panels/style-inputs.ts +0 -237
  598. package/src/panels/target-line.ts +0 -274
  599. package/src/panels/welcome-screen.ts +0 -346
  600. package/src/ui/button-group.ts +0 -102
  601. package/src/ui/choice-field.ts +0 -72
  602. package/src/ui/field-row.ts +0 -249
  603. package/src/ui/formula-palette.ts +0 -213
  604. package/src/ui/icons.ts +0 -52
  605. package/src/ui/jx-theme.ts +0 -130
  606. package/src/ui/spectrum.ts +0 -340
  607. package/src/ui/value-selector.ts +0 -174
  608. package/src/ui/widgets.ts +0 -172
  609. /package/dist/chunks/{javascript-s56a8dpk.css → javascript-4abzjcy9.css} +0 -0
  610. /package/dist/chunks/{jsonMode-zr9sw0c7.css → jsonMode-vvmpmk34.css} +0 -0
  611. /package/dist/chunks/{monaco-setup-pav68602.css → monaco-setup-8vbyqnhr.css} +0 -0
  612. /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,21 +33,30 @@
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}), replaced by the record of
52
+ * whatever bytes reached the file another way ({@link adoptProjectConfig}), and every serialisation
53
+ * here honours it, so what changes on disk is the field that changed.
54
+ *
48
55
  * **Collaboration.** `project.json` is out of collab replication; the gate is in
49
56
  * `collab/collab-session.ts`'s `ensureCollab`, which is what would otherwise register the Yjs
50
57
  * history delegate over this tab.
58
+ *
59
+ * @docs studio/projects/settings
51
60
  */
52
61
 
53
62
  import { errorMessage } from "@jxsuite/schema/parse";
@@ -58,6 +67,8 @@ import { requireProjectState, setProjectState } from "../state";
58
67
  import { setWorkspaceProject, workspace } from "../workspace/workspace";
59
68
  import { PROJECT_CONFIG_PATH, createTab, disposeTab } from "./tab";
60
69
  import { transactDoc } from "./transact";
70
+ import { deriveJsonLayout, parseJsonDocument, serializeJson } from "../files/json-layout";
71
+ import type { JsonLayout } from "../files/json-layout";
61
72
 
62
73
  import type { Tab } from "./tab";
63
74
  import type { ProjectConfig } from "@jxsuite/schema/types";
@@ -67,15 +78,16 @@ export { PROJECT_CONFIG_PATH } from "./tab";
67
78
  /**
68
79
  * The project configuration, as a file.
69
80
  *
70
- * The ONE serialisation. Identical to the native-JSON branch of `files/file-ops.ts`'s
81
+ * The ONE serialisation. Identical to the native-JSON branch of `files/serialize-document.ts`'s
71
82
  * `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.
83
+ * agree or every save would fight the last one. Both honour the layout the file was read in ({@link
84
+ * configLayout}); with none known, the formatter's layout for fresh output.
73
85
  *
74
86
  * @param {ProjectConfig} config
75
87
  * @returns {string}
76
88
  */
77
89
  export function serializeProjectConfig(config: ProjectConfig): string {
78
- return JSON.stringify(config, null, 2);
90
+ return serializeJson(config, configLayout());
79
91
  }
80
92
 
81
93
  // ─── The document of record ───────────────────────────────────────────────────
@@ -94,15 +106,35 @@ let _detached: Tab | null = null;
94
106
  * variables editor writes into `config.style` and then hands the same object over as a patch. A
95
107
  * value taken from memory would call every one of those edits a no-op.
96
108
  *
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.
109
+ * Comparing two renderings of the same key order through the same serializer is a SEMANTIC test,
110
+ * not a textual one, which is the point: a `project.json` formatted some other way than the
111
+ * serializer would lay it out — every array expanded, say — would otherwise look changed on every
112
+ * commit, and a byte comparison against the file would rewrite it whole. This calls it unchanged
113
+ * and writes nothing.
101
114
  *
102
115
  * `null` means "unknown" — no project file, or one that would not parse — and every commit writes.
103
116
  */
104
117
  let _committed: string | null = null;
105
118
 
119
+ /**
120
+ * The layout the file was read in ({@link seedCommitted}) or last written in ({@link
121
+ * adoptProjectConfig}), for a binding with no open tab to carry it. Reset with the binding: a
122
+ * different project is a different file.
123
+ */
124
+ let _layout: JsonLayout | null = null;
125
+
126
+ /**
127
+ * The layout a configuration write honours.
128
+ *
129
+ * The open tab's, when there is one — it read the file when it opened, or was lent the seed's
130
+ * record ({@link lendProjectConfigLayout}), and a source-view commit or a reload from disk keeps it
131
+ * current — and otherwise what the seed read. The two describe the same file, so the choice only
132
+ * matters for freshness, and the tab is the fresher.
133
+ */
134
+ function configLayout(): JsonLayout | null {
135
+ return openConfigTab()?.doc.layout ?? _layout;
136
+ }
137
+
106
138
  /** Whether {@link _committed} has been sought for the current binding. One read per project. */
107
139
  let _seeding: Promise<void> | null = null;
108
140
 
@@ -126,6 +158,7 @@ function unbind(): void {
126
158
  }
127
159
  _bound = null;
128
160
  _committed = null;
161
+ _layout = null;
129
162
  _seeding = null;
130
163
  }
131
164
 
@@ -134,7 +167,9 @@ function seedCommitted(): Promise<void> {
134
167
  _seeding ??= (async () => {
135
168
  try {
136
169
  const text = await getPlatform().readFile(PROJECT_CONFIG_PATH);
137
- _committed = serializeProjectConfig(JSON.parse(text) as ProjectConfig);
170
+ const { document, layout } = parseJsonDocument(text);
171
+ _layout = layout;
172
+ _committed = serializeProjectConfig(document as unknown as ProjectConfig);
138
173
  } catch {
139
174
  // No file, an unreadable one, or one that does not parse. "Unknown" is the honest answer,
140
175
  // And an unknown file is one every commit must write.
@@ -144,6 +179,29 @@ function seedCommitted(): Promise<void> {
144
179
  return _seeding;
145
180
  }
146
181
 
182
+ /**
183
+ * Lend the file's layout to `tab` when it has none of its own.
184
+ *
185
+ * The Settings door (`settings/settings-document.ts`) opens the `project.json` tab over the
186
+ * configuration object the project was loaded with — an object the platform parsed, with no text
187
+ * behind it — so that tab arrives with `doc.layout === null`. Left there, a ⌘S on it would write
188
+ * the formatter's layout for fresh output while a settings commit on the SAME document wrote the
189
+ * file's: every short object in the file blown open by the save the settings edit had just kept,
190
+ * which is the one disagreement this module exists to make impossible. The file is read once per
191
+ * binding anyway ({@link seedCommitted}); this hands the record that read made to the tab, and
192
+ * {@link configLayout} then takes it back from the tab like every other writer. A tab that read the
193
+ * file itself — the Files tree, a reload, a source-view commit — keeps the fresher record it has.
194
+ *
195
+ * @param {Tab} tab
196
+ * @returns {Promise<void>}
197
+ */
198
+ export async function lendProjectConfigLayout(tab: Tab): Promise<void> {
199
+ await seedCommitted();
200
+ if (tab.doc.layout === null && _layout !== null) {
201
+ tab.doc.layout = _layout;
202
+ }
203
+ }
204
+
147
205
  /**
148
206
  * Forget the configuration document — the project-switch and test hook.
149
207
  *
@@ -187,16 +245,28 @@ function liveConfig(): ProjectConfig | null {
187
245
  }
188
246
 
189
247
  /**
190
- * Make `target` hold exactly `source`'s keys — assignment alone would leave the removals behind.
248
+ * Make `target` hold exactly `source`'s keys, in `source`'s ORDER.
249
+ *
250
+ * Every key goes, the ones `source` keeps included, before `source` is assigned over the empty
251
+ * object. Assignment alone would leave the removals behind; deleting only the removals would leave
252
+ * the ORDER behind, because a key that survives keeps the position it had and only a new one takes
253
+ * `source`'s. Key order is what the serializer writes the file in, so a document holding the
254
+ * assistant's configuration in the previous file's order re-lays the file the assistant just wrote
255
+ * on the next commit — even a no-op one, which then disagrees with {@link _committed} (issue 331).
256
+ * The rival handover in {@link commitProjectConfig} is the same case from the other side: the
257
+ * object of record is the source, and its order is the file's.
258
+ *
259
+ * The transient — an observer of a single key would see it gone and back within the transaction —
260
+ * is one the removals already had, and nothing observes the configuration document key by key: the
261
+ * settings surfaces render from `projectState` and mutate it in place, and every reader of the
262
+ * document follows the root reference {@link transactDoc} replaces when the transaction ends.
191
263
  *
192
264
  * @param {Record<string, unknown>} target
193
265
  * @param {ProjectConfig} source
194
266
  */
195
267
  function replaceContents(target: Record<string, unknown>, source: ProjectConfig): void {
196
268
  for (const key of Object.keys(target)) {
197
- if (!Object.hasOwn(source, key)) {
198
- delete target[key];
199
- }
269
+ delete target[key];
200
270
  }
201
271
  Object.assign(target, source);
202
272
  }
@@ -307,7 +377,9 @@ export async function commitProjectConfig(
307
377
  // Tab's own parse, and the caller has already mutated the object it rendered from.
308
378
  const live = liveConfig();
309
379
  const tab = projectConfigDocument();
310
- await seedCommitted();
380
+ // The seed, and the record it read handed to the document — so the tab the Settings door opened
381
+ // Writes the same bytes from ⌘S that this commit is about to.
382
+ await lendProjectConfigLayout(tab);
311
383
  const current = toRaw(tab.doc.document) as unknown as ProjectConfig;
312
384
  const currentText = serializeProjectConfig(current);
313
385
 
@@ -388,14 +460,26 @@ export async function commitProjectConfig(
388
460
  * assistant's write back out of existence. Putting the bytes into the document instead keeps the
389
461
  * one-object rule true, and the state sync follows from the bind effect like every other change.
390
462
  *
463
+ * **The layout record travels with the write.** The assistant authored the whole file, and the text
464
+ * it wrote is the file now — so the record the next commit honours is derived from THAT text
465
+ * (`json-layout.ts`'s `deriveJsonLayout`), exactly as a reload from disk would derive it, and it
466
+ * replaces both the seed's record and the open tab's. Keeping the record the chokepoint read BEFORE
467
+ * the write is what turned the settings edit after an assistant rewrite into a whole-file diff: the
468
+ * file was on disk in the assistant's layout and the commit re-laid it in the previous one (issue
469
+ * 331). The key order travels the same way: {@link replaceContents} installs `config`'s order on
470
+ * the document, so a rewrite that moved a top-level key stays moved. Nothing here re-reads the file
471
+ * to learn any of this: the caller just produced the bytes, and a second parse of `project.json` is
472
+ * the rival object this door exists to prevent.
473
+ *
391
474
  * `skipHistory`, because this is a RELOAD and not an edit: the file already says this, and
392
475
  * `write_file` tells the model in as many words that its disk writes are not undoable. A history
393
476
  * entry here would offer ⌘Z a change it could not take back from the file.
394
477
  *
395
478
  * @param {ProjectConfig} config The configuration as written — already past the schema gate.
479
+ * @param {string} text The bytes that reached the file, which `config` is the parse of.
396
480
  * @returns {Promise<void>}
397
481
  */
398
- export async function adoptProjectConfig(config: ProjectConfig): Promise<void> {
482
+ export async function adoptProjectConfig(config: ProjectConfig, text: string): Promise<void> {
399
483
  if (!requireProjectState()) {
400
484
  // No project state is no configuration document either ({@link projectConfigDocument} reads
401
485
  // One). The workspace copy is then the only thing there is to keep true.
@@ -410,7 +494,13 @@ export async function adoptProjectConfig(config: ProjectConfig): Promise<void> {
410
494
  );
411
495
  tab.doc.dirty = false;
412
496
  // Settle a read already in flight before saying what the file holds, or it would resolve after
413
- // This line with the PREVIOUS bytes and make the next real commit look like a no-op.
497
+ // These lines with the PREVIOUS bytes and record, and make the next real commit look like a
498
+ // No-op — or re-lay the file the assistant just formatted.
414
499
  await seedCommitted();
500
+ // The record of the bytes just written, on the tab as well as on the seed: `configLayout`
501
+ // Prefers the open tab's, and the tab's is the record of the file it opened, which the write has
502
+ // Just replaced. The tab holds the config now; it holds the config's layout too.
503
+ _layout = deriveJsonLayout(text);
504
+ tab.doc.layout = _layout;
415
505
  _committed = serializeProjectConfig(config);
416
506
  }