@jxsuite/studio 2.0.1 → 2.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 (419) hide show
  1. package/canvas.html +21 -0
  2. package/dist/chunks/Overlay.dev-qfjzkms8.js +23 -0
  3. package/dist/chunks/{Overlay.dev-cszx7yn5.js.map → Overlay.dev-qfjzkms8.js.map} +1 -1
  4. package/dist/chunks/{adapter-z8fkmbmv.js → adapter-qfnesn5v.js} +2 -2
  5. package/dist/chunks/{cf-settings-mmyv436d.js → cf-settings-73sh5z4p.js} +3 -3
  6. package/dist/chunks/{components-799jpvhm.js → components-2bpqp4mq.js} +5 -7
  7. package/dist/chunks/{components-799jpvhm.js.map → components-2bpqp4mq.js.map} +1 -1
  8. package/dist/chunks/content-source-34gnkbnn.js +38 -0
  9. package/dist/chunks/content-source-34gnkbnn.js.map +9 -0
  10. package/dist/chunks/convert-to-repeater-t8kxre5s.js +258 -0
  11. package/dist/chunks/convert-to-repeater-t8kxre5s.js.map +10 -0
  12. package/dist/chunks/extension-sections-6mqydz35.js +301 -0
  13. package/dist/chunks/extension-sections-6mqydz35.js.map +11 -0
  14. package/dist/chunks/files-8kn321h1.js +70 -0
  15. package/dist/chunks/files-8kn321h1.js.map +9 -0
  16. package/dist/chunks/{format-host-8jxwts2p.js → format-host-mkwsmt8d.js} +3 -3
  17. package/dist/chunks/git-panel-tjbr191d.js +51 -0
  18. package/dist/chunks/git-panel-tjbr191d.js.map +9 -0
  19. package/dist/chunks/{index-baeg2x3y.js → index-7pxchcce.js} +4 -3
  20. package/dist/chunks/{index-baeg2x3y.js.map → index-7pxchcce.js.map} +3 -3
  21. package/dist/chunks/{jx-validate-qx8degj7.js → jx-validate-v6qgknrs.js} +3 -3
  22. package/dist/chunks/layers-panel-87pb25km.js +58 -0
  23. package/dist/chunks/layers-panel-87pb25km.js.map +9 -0
  24. package/dist/chunks/{monaco-setup-zjdp9zdw.js → monaco-setup-e0yh25j4.js} +4 -75
  25. package/dist/chunks/{monaco-setup-zjdp9zdw.js.map → monaco-setup-e0yh25j4.js.map} +3 -4
  26. package/dist/chunks/publish-panel-m0fcgdrt.js +21 -0
  27. package/dist/chunks/publish-panel-m0fcgdrt.js.map +9 -0
  28. package/dist/chunks/quick-search-rhs6cbsb.js +53 -0
  29. package/dist/chunks/quick-search-rhs6cbsb.js.map +9 -0
  30. package/dist/chunks/right-panel-zpdxd98t.js +37 -0
  31. package/dist/chunks/right-panel-zpdxd98t.js.map +9 -0
  32. package/dist/chunks/section-registry-zmckaxf4.js +30 -0
  33. package/dist/chunks/section-registry-zmckaxf4.js.map +9 -0
  34. package/dist/chunks/sp-overlay.dev-9ran0sfq.js +18 -0
  35. package/dist/chunks/sp-overlay.dev-9ran0sfq.js.map +9 -0
  36. package/dist/chunks/sp-popover.dev-rebmdncr.js +9 -0
  37. package/dist/chunks/sp-popover.dev-rebmdncr.js.map +9 -0
  38. package/dist/chunks/sp-progress-circle.dev-5vkppfkt.js +15 -0
  39. package/dist/chunks/sp-progress-circle.dev-5vkppfkt.js.map +9 -0
  40. package/dist/chunks/sp-tooltip.dev-ykqrg8h5.js +15 -0
  41. package/dist/chunks/sp-tooltip.dev-ykqrg8h5.js.map +9 -0
  42. package/dist/chunks/{sp-tray.dev-pb2fsd65.js → sp-tray.dev-ey59vfh7.js} +10 -8
  43. package/dist/chunks/{sp-tray.dev-pb2fsd65.js.map → sp-tray.dev-ey59vfh7.js.map} +2 -2
  44. package/dist/chunks/{studio-9qr6fq7p.js → studio-0rqqhrdy.js} +10 -378
  45. package/dist/chunks/{studio-9qr6fq7p.js.map → studio-0rqqhrdy.js.map} +4 -14
  46. package/dist/chunks/{studio-5x6tqqec.js → studio-2sj1jj3v.js} +10 -8
  47. package/dist/chunks/{studio-5x6tqqec.js.map → studio-2sj1jj3v.js.map} +2 -2
  48. package/dist/chunks/{studio-h3fw0wn8.js → studio-3fk1ac9z.js} +6 -4
  49. package/dist/chunks/{studio-h3fw0wn8.js.map → studio-3fk1ac9z.js.map} +2 -2
  50. package/dist/chunks/studio-41exgzhq.js +6690 -0
  51. package/dist/chunks/studio-41exgzhq.js.map +38 -0
  52. package/dist/chunks/{studio-7v2bpweq.js → studio-4mqb9a0j.js} +6 -4
  53. package/dist/chunks/{studio-7v2bpweq.js.map → studio-4mqb9a0j.js.map} +2 -2
  54. package/dist/chunks/studio-50k77cca.js +1787 -0
  55. package/dist/chunks/studio-50k77cca.js.map +18 -0
  56. package/dist/chunks/{studio-56v3zwk6.js → studio-6a0h7sne.js} +10 -8
  57. package/dist/chunks/{studio-56v3zwk6.js.map → studio-6a0h7sne.js.map} +2 -2
  58. package/dist/chunks/studio-6f0am8f2.js +53 -0
  59. package/dist/chunks/studio-6f0am8f2.js.map +10 -0
  60. package/dist/chunks/studio-6nmjc5qk.js +7002 -0
  61. package/dist/chunks/studio-6nmjc5qk.js.map +32 -0
  62. package/dist/chunks/studio-7afd21nw.js +67 -0
  63. package/dist/chunks/studio-7afd21nw.js.map +10 -0
  64. package/dist/chunks/{studio-xze4zrs1.js → studio-7bmdvzne.js} +2 -2
  65. package/dist/chunks/studio-7bmdvzne.js.map +10 -0
  66. package/dist/chunks/studio-8jpzqk5s.js +128 -0
  67. package/dist/chunks/studio-8jpzqk5s.js.map +10 -0
  68. package/dist/chunks/studio-94m77tgk.js +1482 -0
  69. package/dist/chunks/studio-94m77tgk.js.map +13 -0
  70. package/dist/chunks/studio-9je6w5sq.js +1000 -0
  71. package/dist/chunks/studio-9je6w5sq.js.map +16 -0
  72. package/dist/chunks/{studio-w7c6kvk7.js → studio-a7pzveq1.js} +2 -2
  73. package/dist/chunks/{studio-gs6kqdt1.js → studio-adn0z1k3.js} +14 -8
  74. package/dist/chunks/{studio-gs6kqdt1.js.map → studio-adn0z1k3.js.map} +3 -3
  75. package/dist/chunks/{studio-kr88ryr7.js → studio-bsxxx3x2.js} +17 -15
  76. package/dist/chunks/{studio-kr88ryr7.js.map → studio-bsxxx3x2.js.map} +7 -6
  77. package/dist/chunks/studio-bw5dz6km.js +608 -0
  78. package/dist/chunks/studio-bw5dz6km.js.map +21 -0
  79. package/dist/chunks/studio-c6s5getc.js +4232 -0
  80. package/dist/chunks/studio-c6s5getc.js.map +39 -0
  81. package/dist/chunks/studio-cjm7fdjf.js +451 -0
  82. package/dist/chunks/studio-cjm7fdjf.js.map +14 -0
  83. package/dist/chunks/{studio-cx91mnh8.js → studio-ckwz4d3x.js} +53 -53
  84. package/dist/chunks/{studio-cx91mnh8.js.map → studio-ckwz4d3x.js.map} +5 -5
  85. package/dist/chunks/studio-dg7zyt5x.js +28 -0
  86. package/dist/chunks/studio-dg7zyt5x.js.map +10 -0
  87. package/dist/chunks/studio-dzcyvvvz.js +166 -0
  88. package/dist/chunks/studio-dzcyvvvz.js.map +10 -0
  89. package/dist/chunks/{studio-7kqe41sa.js → studio-evkr5d3b.js} +15 -7
  90. package/dist/chunks/{studio-7kqe41sa.js.map → studio-evkr5d3b.js.map} +5 -4
  91. package/dist/chunks/studio-f12h6177.js +411 -0
  92. package/dist/chunks/studio-f12h6177.js.map +13 -0
  93. package/dist/chunks/studio-hbp1s8m9.js +1817 -0
  94. package/dist/chunks/studio-hbp1s8m9.js.map +14 -0
  95. package/dist/chunks/studio-k2sxjph5.js +15295 -0
  96. package/dist/chunks/studio-k2sxjph5.js.map +235 -0
  97. package/dist/chunks/studio-myje51t3.js +385 -0
  98. package/dist/chunks/studio-myje51t3.js.map +12 -0
  99. package/dist/chunks/studio-na6rwzhw.js +353 -0
  100. package/dist/chunks/studio-na6rwzhw.js.map +11 -0
  101. package/dist/chunks/{studio-4k0m4fmp.js → studio-p471gg5n.js} +126 -3
  102. package/dist/chunks/studio-p471gg5n.js.map +14 -0
  103. package/dist/chunks/{studio-9bjwf0d4.js → studio-rgwcpast.js} +5 -46
  104. package/dist/chunks/{studio-9bjwf0d4.js.map → studio-rgwcpast.js.map} +4 -5
  105. package/dist/chunks/{studio-esrewxap.js → studio-s519zc99.js} +3 -3
  106. package/dist/chunks/studio-sas7mt3k.js +75 -0
  107. package/dist/chunks/studio-sas7mt3k.js.map +10 -0
  108. package/dist/chunks/studio-t5wqwzdh.js +74 -0
  109. package/dist/chunks/studio-t5wqwzdh.js.map +10 -0
  110. package/dist/chunks/{studio-9qpspwfn.js → studio-temv5bw4.js} +5 -5
  111. package/dist/chunks/{studio-9qpspwfn.js.map → studio-temv5bw4.js.map} +1 -1
  112. package/dist/chunks/{studio-qkvt37c0.js → studio-tr20m8g8.js} +669 -18
  113. package/dist/chunks/studio-tr20m8g8.js.map +9 -0
  114. package/dist/chunks/{studio-v7jkjqng.js → studio-v96qfnww.js} +5514 -4291
  115. package/dist/chunks/studio-v96qfnww.js.map +54 -0
  116. package/dist/chunks/studio-vcetc91k.js +659 -0
  117. package/dist/chunks/studio-vcetc91k.js.map +10 -0
  118. package/dist/chunks/{studio-jm0z592w.js → studio-vmymjefp.js} +2972 -4937
  119. package/dist/chunks/studio-vmymjefp.js.map +98 -0
  120. package/dist/chunks/{studio-bghqfmea.js → studio-wwseqyw0.js} +2 -2
  121. package/dist/chunks/studio-x9hmy5kr.js +390 -0
  122. package/dist/chunks/studio-x9hmy5kr.js.map +10 -0
  123. package/dist/chunks/studio-xa0ce0n7.js +335 -0
  124. package/dist/chunks/studio-xa0ce0n7.js.map +10 -0
  125. package/dist/chunks/studio-xm3dbxvn.js +378 -0
  126. package/dist/chunks/studio-xm3dbxvn.js.map +19 -0
  127. package/dist/chunks/{studio-nqw433kv.js → studio-xmvbhg31.js} +2 -2
  128. package/dist/chunks/studio-zv37yrfa.js +1054 -0
  129. package/dist/chunks/studio-zv37yrfa.js.map +13 -0
  130. package/dist/chunks/{ws-client-q7hkz3j9.js → ws-client-654ts66j.js} +6 -4
  131. package/dist/chunks/ws-client-654ts66j.js.map +11 -0
  132. package/dist/iframe-entry.js +668 -230
  133. package/dist/studio.js +21080 -32585
  134. package/index.html +35 -4685
  135. package/package.json +17 -15
  136. package/src/about/about-modal.ts +42 -11
  137. package/src/browse/library-commands.ts +240 -0
  138. package/src/browse/library-layouts.ts +369 -0
  139. package/src/browse/library-model.ts +396 -0
  140. package/src/browse/library-pane.ts +1049 -0
  141. package/src/browse/library-preview.ts +252 -0
  142. package/src/browse/library-source.ts +150 -0
  143. package/src/canvas/canvas-helpers.ts +33 -9
  144. package/src/canvas/canvas-live-render.ts +45 -29
  145. package/src/canvas/canvas-patcher.ts +151 -31
  146. package/src/canvas/canvas-perf.ts +46 -7
  147. package/src/canvas/canvas-render.ts +1092 -220
  148. package/src/canvas/canvas-surface.ts +240 -0
  149. package/src/canvas/canvas-utils.ts +1166 -124
  150. package/src/canvas/iframe-drop.ts +2 -1
  151. package/src/canvas/iframe-entry.ts +241 -4
  152. package/src/canvas/iframe-host.ts +1226 -143
  153. package/src/canvas/iframe-inline-edit.ts +31 -0
  154. package/src/canvas/iframe-interaction.ts +76 -16
  155. package/src/canvas/iframe-keys.ts +107 -45
  156. package/src/canvas/iframe-overlay.ts +38 -1
  157. package/src/canvas/iframe-protocol.ts +137 -2
  158. package/src/canvas/iframe-render.ts +70 -2
  159. package/src/canvas/iframe-slash.ts +6 -0
  160. package/src/canvas/path-mapping.ts +56 -6
  161. package/src/canvas/surface-registry.ts +292 -0
  162. package/src/collab/collab-commands.ts +244 -0
  163. package/src/collab/collab-session.ts +198 -32
  164. package/src/collab/collab-state.ts +28 -2
  165. package/src/collab/presence-chips.ts +100 -9
  166. package/src/commands/active-registry.ts +42 -0
  167. package/src/commands/app-commands.ts +176 -0
  168. package/src/commands/budget.ts +158 -0
  169. package/src/commands/command-args.ts +253 -0
  170. package/src/commands/context.ts +387 -0
  171. package/src/commands/defaults.ts +580 -0
  172. package/src/commands/keymap.ts +571 -0
  173. package/src/commands/levels.ts +345 -0
  174. package/src/commands/live-context.ts +193 -0
  175. package/src/commands/reference.ts +227 -0
  176. package/src/commands/registry.ts +293 -0
  177. package/src/component-props.ts +3 -2
  178. package/src/content/draft-state.ts +106 -0
  179. package/src/content/entry-commands.ts +282 -0
  180. package/src/content/entry-editor.ts +327 -0
  181. package/src/content/entry-fields.ts +116 -0
  182. package/src/content/entry-model.ts +175 -0
  183. package/src/editor/canvas-context-menu.ts +17 -11
  184. package/src/editor/canvas-slash-bridge.ts +11 -4
  185. package/src/editor/context-menu.ts +804 -204
  186. package/src/editor/convert-to-component.ts +31 -10
  187. package/src/editor/convert-to-repeater.ts +3 -2
  188. package/src/editor/inline-edit-apply.ts +5 -5
  189. package/src/editor/inline-edit.ts +86 -50
  190. package/src/editor/insert-zone-action.ts +1 -1
  191. package/src/editor/shortcuts.ts +1043 -329
  192. package/src/editor/slash-menu.ts +1 -0
  193. package/src/files/elements.ts +166 -0
  194. package/src/files/file-ops.ts +178 -31
  195. package/src/files/files.ts +898 -268
  196. package/src/files/fs-events.ts +21 -0
  197. package/src/files/media-meta.ts +203 -0
  198. package/src/files/media-paths.ts +141 -0
  199. package/src/files/media-upload.ts +7 -3
  200. package/src/files/media-usage.ts +189 -0
  201. package/src/github/github-auth.ts +235 -33
  202. package/src/github/github-publish.ts +80 -21
  203. package/src/grid/grid-controller.ts +152 -12
  204. package/src/grid/grid-layout.ts +225 -20
  205. package/src/grid/grid-open.ts +120 -0
  206. package/src/grid/grid-panel.ts +554 -39
  207. package/src/grid/grid-source.ts +27 -1
  208. package/src/grid/redirects-grid.ts +599 -0
  209. package/src/grid/redirects.ts +398 -0
  210. package/src/grid/sources/content-source.ts +108 -2
  211. package/src/i18n/i18n-commands.ts +322 -0
  212. package/src/new-project/add-repo-modal.ts +8 -10
  213. package/src/new-project/import-tab.ts +7 -0
  214. package/src/new-project/location-fields.ts +9 -1
  215. package/src/new-project/new-project-modal.ts +221 -245
  216. package/src/packages/jxsuite-update.ts +97 -48
  217. package/src/packages/pull-package-sync.ts +18 -9
  218. package/src/page-params.ts +2 -1
  219. package/src/panels/activity-bar.ts +152 -91
  220. package/src/panels/activity-panel.ts +481 -0
  221. package/src/panels/ai-chat/chat-markdown.ts +8 -3
  222. package/src/panels/ai-chat/chat-view.ts +245 -18
  223. package/src/panels/ai-chat/composer.ts +31 -5
  224. package/src/panels/ai-chat/sessions-view.ts +17 -10
  225. package/src/panels/ai-panel.ts +279 -53
  226. package/src/panels/block-action-bar.ts +1115 -224
  227. package/src/panels/bottom-dock.ts +337 -0
  228. package/src/panels/canvas-dnd-bridge.ts +37 -8
  229. package/src/panels/chat-panel.ts +29 -12
  230. package/src/panels/data-explorer.ts +254 -112
  231. package/src/panels/data-grid.ts +10 -4
  232. package/src/panels/dnd.ts +28 -51
  233. package/src/panels/editors.ts +434 -107
  234. package/src/panels/elements-panel.ts +66 -2
  235. package/src/panels/empty-state.ts +112 -0
  236. package/src/panels/events-panel.ts +596 -82
  237. package/src/panels/formula-workspace.ts +408 -218
  238. package/src/panels/frontmatter-fields.ts +51 -10
  239. package/src/panels/frontmatter-panel.ts +357 -79
  240. package/src/panels/git-panel.ts +375 -164
  241. package/src/panels/head-panel.ts +791 -85
  242. package/src/panels/i18n-panel.ts +592 -0
  243. package/src/panels/imports-panel.ts +95 -85
  244. package/src/panels/jump-bar.ts +584 -0
  245. package/src/panels/layers-panel.ts +1106 -328
  246. package/src/panels/left-panel.ts +109 -204
  247. package/src/panels/navigator-panels.ts +131 -0
  248. package/src/panels/overlays.ts +7 -2
  249. package/src/panels/pane-context.ts +1477 -0
  250. package/src/panels/pane-grid.ts +406 -0
  251. package/src/panels/panel-registry.ts +340 -0
  252. package/src/panels/panel-scheduler.ts +75 -0
  253. package/src/panels/problems-panel.ts +234 -0
  254. package/src/panels/properties-panel.ts +946 -1139
  255. package/src/panels/provenance.ts +258 -0
  256. package/src/panels/quick-search.ts +791 -122
  257. package/src/panels/right-panel.ts +228 -102
  258. package/src/panels/seo-modal.ts +395 -0
  259. package/src/panels/settings-pane.ts +203 -0
  260. package/src/panels/signals-panel.ts +354 -137
  261. package/src/panels/statement-editor.ts +41 -25
  262. package/src/panels/statusbar.ts +342 -106
  263. package/src/panels/style-panel.ts +974 -463
  264. package/src/panels/style-utils.ts +3 -3
  265. package/src/panels/stylebook-doc.ts +4 -2
  266. package/src/panels/stylebook-layers-panel.ts +3 -2
  267. package/src/panels/stylebook-panel.ts +51 -26
  268. package/src/panels/tab-strip.ts +1058 -78
  269. package/src/panels/target-line.ts +274 -0
  270. package/src/panels/toolbar.ts +631 -484
  271. package/src/panels/welcome-screen.ts +301 -198
  272. package/src/platform-errors.ts +26 -3
  273. package/src/platform.ts +96 -2
  274. package/src/platforms/cloud.ts +71 -8
  275. package/src/platforms/devserver.ts +92 -29
  276. package/src/publish/deploy-checklist.ts +383 -0
  277. package/src/publish/pages-service.ts +10 -1
  278. package/src/publish/publish-commands.ts +218 -0
  279. package/src/publish/publish-panel.ts +110 -16
  280. package/src/recent-projects.ts +3 -2
  281. package/src/services/a11y-report.ts +505 -0
  282. package/src/services/ai-models.ts +30 -1
  283. package/src/services/ai-project-tools.ts +29 -10
  284. package/src/services/ai-system-prompt.ts +53 -14
  285. package/src/services/ai-tools.ts +39 -3
  286. package/src/services/ai-writes.ts +154 -0
  287. package/src/services/announce.ts +97 -0
  288. package/src/services/automation.ts +424 -175
  289. package/src/services/cem-export.ts +9 -4
  290. package/src/services/clock.ts +61 -0
  291. package/src/services/document-assistant.ts +36 -8
  292. package/src/services/idle.ts +179 -0
  293. package/src/services/import-client.ts +26 -0
  294. package/src/services/monaco-buffer.ts +627 -0
  295. package/src/services/monaco-lazy.ts +36 -0
  296. package/src/services/notify.ts +271 -0
  297. package/src/services/profile.ts +155 -0
  298. package/src/services/references.ts +0 -0
  299. package/src/services/token-lint.ts +3 -2
  300. package/src/services/tool-executor.ts +53 -7
  301. package/src/services/trusted-types.ts +123 -0
  302. package/src/settings/contexts-section.ts +427 -0
  303. package/src/settings/contributed-section.ts +149 -11
  304. package/src/settings/css-vars-editor.ts +306 -349
  305. package/src/settings/defs-editor.ts +93 -34
  306. package/src/settings/dependencies-editor.ts +7 -7
  307. package/src/settings/extension-sections.ts +37 -5
  308. package/src/settings/general-settings.ts +204 -121
  309. package/src/settings/locales-section.ts +347 -0
  310. package/src/settings/preferences-accounts.ts +140 -0
  311. package/src/settings/preferences-dialog.ts +620 -0
  312. package/src/settings/preferences-keymap.ts +270 -0
  313. package/src/settings/project-sections.ts +260 -0
  314. package/src/settings/section-registry.ts +169 -0
  315. package/src/settings/settings-document.ts +385 -0
  316. package/src/shell.ts +1549 -0
  317. package/src/site-context.ts +45 -20
  318. package/src/state.ts +14 -275
  319. package/src/store.ts +88 -43
  320. package/src/studio.ts +790 -346
  321. package/src/style/live-preview.ts +36 -0
  322. package/src/style/project-styles.ts +349 -0
  323. package/src/style/token-ref.ts +123 -0
  324. package/src/tabs/patch-ops.ts +16 -4
  325. package/src/tabs/project-config.ts +416 -0
  326. package/src/tabs/selection.ts +323 -0
  327. package/src/tabs/tab.ts +182 -47
  328. package/src/tabs/transact.ts +209 -28
  329. package/src/types.ts +68 -13
  330. package/src/ui/ai-credentials-form.ts +39 -35
  331. package/src/ui/ai-managed-connect.ts +7 -1
  332. package/src/ui/dynamic-slot.ts +224 -81
  333. package/src/ui/expression-editor.ts +71 -63
  334. package/src/ui/field-input.ts +4 -6
  335. package/src/ui/field-row.ts +215 -38
  336. package/src/ui/form-controls.ts +194 -7
  337. package/src/ui/layers.ts +492 -69
  338. package/src/ui/media-picker.ts +115 -25
  339. package/src/ui/panel-resize.ts +109 -91
  340. package/src/ui/progress-modal.ts +131 -50
  341. package/src/ui/regions.ts +462 -0
  342. package/src/ui/schema-form.ts +229 -45
  343. package/src/ui/spectrum.ts +24 -8
  344. package/src/ui/timing.ts +49 -0
  345. package/src/ui/value-selector.ts +20 -0
  346. package/src/ui/value-source.ts +348 -0
  347. package/src/ui/virtual-window.ts +400 -0
  348. package/src/utils/edit-display.ts +3 -2
  349. package/src/utils/inherited-style.ts +67 -37
  350. package/src/view.ts +64 -78
  351. package/src/workspace/pane-derive.ts +1258 -0
  352. package/src/workspace/session.ts +317 -0
  353. package/src/workspace/workspace.ts +1345 -23
  354. package/styles/canvas.css +653 -0
  355. package/styles/forced-colors.css +106 -0
  356. package/styles/inspector.css +1454 -0
  357. package/styles/overlays.css +2704 -0
  358. package/styles/panels.css +1701 -0
  359. package/styles/shell.css +1423 -0
  360. package/styles/tokens.css +151 -0
  361. package/dist/chunks/Overlay.dev-cszx7yn5.js +0 -22
  362. package/dist/chunks/extension-sections-qr3drkkw.js +0 -531
  363. package/dist/chunks/extension-sections-qr3drkkw.js.map +0 -12
  364. package/dist/chunks/files-j8a1t06k.js +0 -50
  365. package/dist/chunks/files-j8a1t06k.js.map +0 -9
  366. package/dist/chunks/git-panel-xtq5z81x.js +0 -28
  367. package/dist/chunks/git-panel-xtq5z81x.js.map +0 -9
  368. package/dist/chunks/layers-panel-ancn2wpq.js +0 -26
  369. package/dist/chunks/layers-panel-ancn2wpq.js.map +0 -9
  370. package/dist/chunks/settings-modal-17ttr96g.js +0 -29
  371. package/dist/chunks/settings-modal-17ttr96g.js.map +0 -9
  372. package/dist/chunks/sp-overlay.dev-earn5as8.js +0 -17
  373. package/dist/chunks/sp-overlay.dev-earn5as8.js.map +0 -9
  374. package/dist/chunks/sp-popover.dev-frxtfejn.js +0 -8
  375. package/dist/chunks/sp-popover.dev-frxtfejn.js.map +0 -9
  376. package/dist/chunks/sp-progress-circle.dev-7tvw34h8.js +0 -14
  377. package/dist/chunks/sp-progress-circle.dev-7tvw34h8.js.map +0 -9
  378. package/dist/chunks/sp-tooltip.dev-a9fywesm.js +0 -21
  379. package/dist/chunks/sp-tooltip.dev-a9fywesm.js.map +0 -10
  380. package/dist/chunks/studio-0xgs7xrs.js +0 -429
  381. package/dist/chunks/studio-0xgs7xrs.js.map +0 -13
  382. package/dist/chunks/studio-4d8hzcz5.js +0 -3427
  383. package/dist/chunks/studio-4d8hzcz5.js.map +0 -26
  384. package/dist/chunks/studio-4k0m4fmp.js.map +0 -13
  385. package/dist/chunks/studio-9smfzqhc.js +0 -19
  386. package/dist/chunks/studio-9smfzqhc.js.map +0 -10
  387. package/dist/chunks/studio-jm0z592w.js.map +0 -115
  388. package/dist/chunks/studio-m2t4mdg5.js +0 -1854
  389. package/dist/chunks/studio-m2t4mdg5.js.map +0 -18
  390. package/dist/chunks/studio-n4p11dfy.js +0 -853
  391. package/dist/chunks/studio-n4p11dfy.js.map +0 -12
  392. package/dist/chunks/studio-qkvt37c0.js.map +0 -9
  393. package/dist/chunks/studio-sjh1wt1a.js +0 -3744
  394. package/dist/chunks/studio-sjh1wt1a.js.map +0 -22
  395. package/dist/chunks/studio-v7jkjqng.js.map +0 -46
  396. package/dist/chunks/studio-xq0t7n5t.js +0 -140
  397. package/dist/chunks/studio-xq0t7n5t.js.map +0 -10
  398. package/dist/chunks/studio-xze4zrs1.js.map +0 -10
  399. package/dist/chunks/studio-yfcstdzy.js +0 -399
  400. package/dist/chunks/studio-yfcstdzy.js.map +0 -12
  401. package/dist/chunks/studio-zp7jncs8.js +0 -469
  402. package/dist/chunks/studio-zp7jncs8.js.map +0 -14
  403. package/dist/chunks/ws-client-q7hkz3j9.js.map +0 -11
  404. package/src/browse/browse-modal.ts +0 -69
  405. package/src/browse/browse.ts +0 -904
  406. package/src/canvas/canvas-diff.ts +0 -216
  407. package/src/canvas/nested-site-style.ts +0 -58
  408. package/src/new-project/design-fields.ts +0 -264
  409. package/src/new-project/templates.ts +0 -61
  410. package/src/panels/tab-bar.ts +0 -548
  411. package/src/settings/settings-modal.ts +0 -203
  412. /package/dist/chunks/{adapter-z8fkmbmv.js.map → adapter-qfnesn5v.js.map} +0 -0
  413. /package/dist/chunks/{cf-settings-mmyv436d.js.map → cf-settings-73sh5z4p.js.map} +0 -0
  414. /package/dist/chunks/{format-host-8jxwts2p.js.map → format-host-mkwsmt8d.js.map} +0 -0
  415. /package/dist/chunks/{jx-validate-qx8degj7.js.map → jx-validate-v6qgknrs.js.map} +0 -0
  416. /package/dist/chunks/{studio-w7c6kvk7.js.map → studio-a7pzveq1.js.map} +0 -0
  417. /package/dist/chunks/{studio-esrewxap.js.map → studio-s519zc99.js.map} +0 -0
  418. /package/dist/chunks/{studio-bghqfmea.js.map → studio-wwseqyw0.js.map} +0 -0
  419. /package/dist/chunks/{studio-nqw433kv.js.map → studio-xmvbhg31.js.map} +0 -0
@@ -1,28 +1,37 @@
1
1
  /// <reference lib="dom" />
2
2
  /**
3
- * New Project modal — a two-step wizard. Step 1 ("Start new project from:") picks the source on one
4
- * of four tabs: a built-in Template, a Starter Site, an Import of an existing site, or an Agent
5
- * prompt. Step 2 ("New Project Parameters") collects the project identity plus a design quickstart
6
- * (colors, fonts, logo, breakpoints — a creation-time subset of the settings modal), prefilled from
7
- * the chosen source and customizable before anything is written.
3
+ * New Project modal — a two-step wizard.
4
+ *
5
+ * **Step 1 · Choose a starting point.** Starters lead: the first thing offered is a complete site
6
+ * you can look at. The gallery ends with one **Start from scratch** card — the minimal scaffold —
7
+ * because a first-run choice between four breakpoint presets described by media-query direction is
8
+ * a choice nobody can make. Two further sources sit on their own tabs: an **Import** of an existing
9
+ * site, and an **Agent** prompt.
10
+ *
11
+ * **Step 2 · Name your project.** Name and location, nothing else. A project's URL, deployment
12
+ * adapter and design tokens are settings, not creation-time decisions, so they live in the
13
+ * project's own settings surface where they can be changed more than once.
8
14
  *
9
15
  * Import and Agent need working AI, so both are gated until credentials exist — a key stored in
10
16
  * this browser, or a backend that holds its own (managed cloud platforms, env-keyed dev servers).
11
17
  * While gated they offer the same two paths as the assistant sidebar: connect Cloudflare for
12
18
  * Workers AI, or bring a key.
19
+ *
20
+ * @docs studio/projects/create
13
21
  */
14
22
 
15
23
  import { html } from "lit-html";
16
24
  import { errorMessage } from "@jxsuite/schema/parse";
17
25
  import { openModal } from "../ui/layers";
26
+ import { enumArg, enumProperty } from "../commands/command-args";
27
+ import type { AnyCommand, CommandRegistry } from "../commands/registry";
18
28
  import { getPlatform } from "../platform";
19
29
  import { installUrlOf } from "../platform-errors";
20
30
  import { hasAiCredentials } from "../services/ai-models";
21
31
  import { setPendingAgentPrompt } from "../services/agent-seed";
32
+ import { initProjectRepo } from "../files/files";
22
33
  import { createAiCredentialsForm } from "../ui/ai-credentials-form";
23
34
  import { createManagedConnect } from "../ui/ai-managed-connect";
24
- import { PROJECT_TEMPLATES } from "./templates";
25
- import { collectDesign, renderDesignFields, resetDesignFields } from "./design-fields";
26
35
  import {
27
36
  collectDestination,
28
37
  loadLocationOptions,
@@ -44,28 +53,21 @@ import type { ProjectConfig } from "@jxsuite/schema/types";
44
53
  import type { CreateProjectDestination, StarterInfo } from "../types";
45
54
  import type { ImportTabCtx } from "./import-tab";
46
55
 
47
- type NewProjectTab = "template" | "starter" | "import" | "agent";
56
+ type NewProjectTab = (typeof NEW_PROJECT_TABS)[number];
48
57
  type WizardStep = "source" | "params";
49
58
 
50
59
  let _handle: ReturnType<typeof openModal> | null = null;
51
60
 
52
- let _form = {
53
- adapter: "static",
54
- description: "",
55
- directory: "",
56
- name: "",
57
- url: "",
58
- };
61
+ let _form = { directory: "", name: "" };
59
62
 
60
- let _tab: NewProjectTab = "template";
63
+ let _tab: NewProjectTab = "starter";
61
64
  let _step: WizardStep = "source";
62
- let _template = "blank";
65
+ /** The step-1 choice on the Starters tab: a starter id, or `""` for "Start from scratch". */
63
66
  let _starter = "";
67
+ /** Set once the user picks a card, so an arriving starter list stops overriding their choice. */
68
+ let _starterTouched = false;
64
69
  let _agentPrompt = "";
65
70
 
66
- /** Which source the Parameters step was last seeded for — Back/Next keeps user edits intact. */
67
- let _paramsSeededFor = "";
68
-
69
71
  let _error = "";
70
72
 
71
73
  /**
@@ -93,7 +95,7 @@ function focusNameField() {
93
95
 
94
96
  let _creating = false;
95
97
 
96
- /** Starter templates offered in the picker (empty until loaded / on platforms without starters). */
98
+ /** Starter templates offered in the gallery (empty until loaded / on platforms without starters). */
97
99
  let _starters: StarterInfo[] = [];
98
100
 
99
101
  let _resolve: ((result: { root: string; config: ProjectConfig } | null) => void) | null = null;
@@ -144,28 +146,22 @@ function aiGateOpen(): boolean {
144
146
  * Open the New Project modal. Returns a promise that resolves with the created project info (or
145
147
  * null if cancelled).
146
148
  *
149
+ * @param {{ tab?: NewProjectTab }} [options] Which source tab to open on; defaults to Starters.
147
150
  * @returns {Promise<{ root: string; config: object } | null>}
148
151
  */
149
- export function openNewProjectModal(options?: { tab?: "template" | "starter" }): Promise<{
152
+ export function openNewProjectModal(options?: { tab?: NewProjectTab }): Promise<{
150
153
  root: string;
151
154
  config: ProjectConfig;
152
155
  } | null> {
153
156
  if (_handle) {
154
157
  return Promise.resolve(null);
155
158
  }
156
- _form = {
157
- adapter: "static",
158
- description: "",
159
- directory: "",
160
- name: "",
161
- url: "",
162
- };
163
- _tab = options?.tab ?? "template";
159
+ _form = { directory: "", name: "" };
160
+ _tab = options?.tab ?? "starter";
164
161
  _step = "source";
165
- _template = "blank";
166
162
  _starter = "";
163
+ _starterTouched = false;
167
164
  _agentPrompt = "";
168
- _paramsSeededFor = "";
169
165
  _error = "";
170
166
  _nameError = "";
171
167
  _errorInstallUrl = "";
@@ -173,12 +169,10 @@ export function openNewProjectModal(options?: { tab?: "template" | "starter" }):
173
169
  _starters = [];
174
170
  _dirDerived = true;
175
171
  resetImportTab();
176
- resetDesignFields();
177
172
  resetLocationFields();
178
173
 
179
174
  // Load the destination options (repo-mode owners) and starter templates in the background,
180
- // Re-rendering when they arrive. Platforms without starters leave the Starter Site tab showing
181
- // Its empty note.
175
+ // Re-rendering when they arrive. A platform without starters shows the scratch card alone.
182
176
  const platform = getPlatform();
183
177
  loadLocationOptions(() => {
184
178
  if (_handle) {
@@ -190,7 +184,8 @@ export function openNewProjectModal(options?: { tab?: "template" | "starter" }):
190
184
  .listStarters()
191
185
  .then((starters) => {
192
186
  _starters = starters;
193
- if (!_starter) {
187
+ // Starters lead: the gallery opens with a real site selected, not an empty scaffold.
188
+ if (!_starterTouched) {
194
189
  _starter = starters[0]?.id ?? "";
195
190
  }
196
191
  if (_handle) {
@@ -198,7 +193,7 @@ export function openNewProjectModal(options?: { tab?: "template" | "starter" }):
198
193
  }
199
194
  })
200
195
  .catch(() => {
201
- /* Non-fatal: the Starter tab keeps its empty note. */
196
+ /* Non-fatal: the gallery keeps the scratch card. */
202
197
  });
203
198
  }
204
199
 
@@ -208,10 +203,17 @@ export function openNewProjectModal(options?: { tab?: "template" | "starter" }):
208
203
  });
209
204
  }
210
205
 
206
+ /**
207
+ * Dismiss the wizard. Available from every step, and from the underlay and Escape — a running
208
+ * import is aborted on the way out rather than trapping the user behind it.
209
+ */
211
210
  export function closeNewProjectModal() {
212
- if (!_handle || _creating || isImportRunning()) {
211
+ if (!_handle || _creating) {
213
212
  return;
214
213
  }
214
+ if (isImportRunning()) {
215
+ cancelImport(importCtxFor(renderModal));
216
+ }
215
217
  _handle.close();
216
218
  _handle = null;
217
219
  if (_resolve) {
@@ -233,6 +235,15 @@ function finish(result: { root: string; config: ProjectConfig }) {
233
235
  }
234
236
  }
235
237
 
238
+ /**
239
+ * Every path out of the wizard that produced a project: initialise version control for it, then
240
+ * hand it to the caller. A scaffold is not a repository, and Delete and Rename are one click away.
241
+ */
242
+ async function finishCreated(result: { root: string; config: ProjectConfig }) {
243
+ await initProjectRepo(result.root);
244
+ finish(result);
245
+ }
246
+
236
247
  function deriveSlug(name: string): string {
237
248
  return name
238
249
  .toLowerCase()
@@ -240,12 +251,9 @@ function deriveSlug(name: string): string {
240
251
  .replaceAll(/^-|-$/g, "");
241
252
  }
242
253
 
243
- /** The human label of the chosen source, shown as context on the Parameters step. */
254
+ /** The human label of the chosen source, shown as context on the Name step. */
244
255
  function sourceLabel(): string {
245
256
  switch (_tab) {
246
- case "starter": {
247
- return `Starter Site · ${_starters.find((s) => s.id === _starter)?.name ?? _starter}`;
248
- }
249
257
  case "import": {
250
258
  return "Import";
251
259
  }
@@ -253,54 +261,79 @@ function sourceLabel(): string {
253
261
  return "Agent";
254
262
  }
255
263
  default: {
256
- return `Template · ${PROJECT_TEMPLATES.find((t) => t.id === _template)?.name ?? _template}`;
264
+ return _starter
265
+ ? `Starter site · ${_starters.find((s) => s.id === _starter)?.name ?? _starter}`
266
+ : "Start from scratch";
257
267
  }
258
268
  }
259
269
  }
260
270
 
261
- function renderModal() {
262
- const platform = getPlatform();
263
- const importCtx: ImportTabCtx = {
271
+ /** The import tab's context object — rebuilt per render so it closes over current state. */
272
+ function importCtxFor(rerender: () => void): ImportTabCtx {
273
+ return {
264
274
  aiGateOpen,
265
275
  credsForm: credsForm(),
266
276
  form: _form,
267
277
  managedConnect: managedConnect(),
268
- onDone: finish,
269
- rerender: renderModal,
278
+ onDone: (result) => {
279
+ // See above: the discard is deliberate, and `void` as a statement is what says so.
280
+ void finishCreated(result);
281
+ },
282
+ rerender,
270
283
  resolveDestination: () => validateParams(),
271
284
  };
285
+ }
272
286
 
273
- const onInput =
274
- (field: "name" | "description" | "url" | "adapter" | "directory") => (e: Event) => {
275
- _form[field] = (e.target as HTMLInputElement).value;
276
- if (field === "name") {
277
- _nameError = "";
278
- }
279
- if (field === "name" && !_form.directory) {
280
- // Auto-derive directory slug from name while user hasn't manually typed one
287
+ /**
288
+ * Validate the Name step, returning the destination to create at (null when something is missing —
289
+ * the reason is already rendered inline and the modal re-drawn). The slug is derived from the name
290
+ * when left blank, but the destination itself is never guessed.
291
+ */
292
+ function validateParams(): CreateProjectDestination | null {
293
+ if (!_form.name.trim()) {
294
+ _nameError = "Project name is required";
295
+ renderModal();
296
+ focusNameField();
297
+ return null;
298
+ }
299
+ if (!_form.directory.trim()) {
300
+ _form.directory = deriveSlug(_form.name);
301
+ }
302
+ const destination = collectDestination(_form.directory);
303
+ if (!destination) {
304
+ _nameError = "";
305
+ renderModal();
306
+ focusNameField();
307
+ return null;
308
+ }
309
+ return destination;
310
+ }
311
+
312
+ function renderModal() {
313
+ const platform = getPlatform();
314
+ const importCtx = importCtxFor(renderModal);
315
+
316
+ const onInput = (field: "name" | "directory") => (e: Event) => {
317
+ _form[field] = (e.target as HTMLInputElement).value;
318
+ if (field === "name") {
319
+ _nameError = "";
320
+ // Auto-derive the directory slug from the name while the user hasn't typed one.
321
+ if (!_form.directory) {
281
322
  _dirDerived = true;
282
323
  }
283
- if (_dirDerived && field === "name") {
324
+ if (_dirDerived) {
284
325
  _form.directory = deriveSlug(_form.name);
285
326
  }
286
- if (field === "directory") {
287
- _dirDerived = false;
288
- }
289
- renderModal();
290
- };
291
-
292
- const onAdapterChange = (e: Event) => {
293
- _form.adapter = (e.target as HTMLInputElement).value;
294
- renderModal();
295
- };
296
-
297
- const selectTemplate = (id: string) => {
298
- _template = id;
327
+ } else {
328
+ _dirDerived = false;
329
+ }
299
330
  renderModal();
300
331
  };
301
332
 
302
333
  const selectStarter = (id: string) => {
303
334
  _starter = id;
335
+ _starterTouched = true;
336
+ _error = "";
304
337
  renderModal();
305
338
  };
306
339
 
@@ -316,11 +349,6 @@ function renderModal() {
316
349
  // ─── Step transitions ──────────────────────────────────────────────────────
317
350
 
318
351
  const goNext = () => {
319
- if (_tab === "starter" && !_starter) {
320
- _error = "Choose a starter site";
321
- renderModal();
322
- return;
323
- }
324
352
  if (_tab === "import" && !validateImportSource(importCtx)) {
325
353
  return;
326
354
  }
@@ -329,28 +357,6 @@ function renderModal() {
329
357
  renderModal();
330
358
  return;
331
359
  }
332
-
333
- // Seed the Parameters step from the chosen source — only when the source changed, so Back +
334
- // Next round-trips keep the user's edits.
335
- const seedKey = `${_tab}:${_template}:${_starter}`;
336
- if (_paramsSeededFor !== seedKey) {
337
- _paramsSeededFor = seedKey;
338
- if (_tab === "starter") {
339
- const meta = _starters.find((s) => s.id === _starter);
340
- if (meta && !_form.description.trim()) {
341
- _form.description = meta.tagline;
342
- }
343
- resetDesignFields({
344
- ...(meta?.accent ? { accent: meta.accent } : {}),
345
- mediaNote: "Leave empty to keep the starter's breakpoints.",
346
- });
347
- } else if (_tab === "template" || _tab === "agent") {
348
- const templateId = _tab === "agent" ? "blank" : _template;
349
- const meta = PROJECT_TEMPLATES.find((t) => t.id === templateId);
350
- resetDesignFields(meta ? { media: meta.media } : {});
351
- }
352
- }
353
-
354
360
  _step = "params";
355
361
  _error = "";
356
362
  renderModal();
@@ -368,32 +374,11 @@ function renderModal() {
368
374
 
369
375
  // ─── Submission ────────────────────────────────────────────────────────────
370
376
 
371
- /**
372
- * Validate the Parameters step, returning the destination to create at (null when something is
373
- * missing — the reason is already rendered inline and the modal re-drawn). The slug is derived
374
- * from the name when left blank, but the destination itself is never guessed.
375
- */
376
- const validateParams = (): CreateProjectDestination | null => {
377
- if (!_form.name.trim()) {
378
- _nameError = "Project name is required";
379
- renderModal();
380
- focusNameField();
381
- return null;
382
- }
383
- if (!_form.directory.trim()) {
384
- _form.directory = deriveSlug(_form.name);
385
- }
386
- const destination = collectDestination(_form.directory);
387
- if (!destination) {
388
- _nameError = "";
389
- renderModal();
390
- focusNameField();
391
- return null;
392
- }
393
- return destination;
394
- };
395
-
396
- const onSubmit = async () => {
377
+ /** Create the project from the chosen source, then run `after` with the result. */
378
+ const create = async (
379
+ source: { starter: string } | { template: string },
380
+ after: (result: { root: string; config: ProjectConfig }) => void | Promise<void>,
381
+ ) => {
397
382
  const destination = validateParams();
398
383
  if (!destination) {
399
384
  return;
@@ -405,14 +390,8 @@ function renderModal() {
405
390
  renderModal();
406
391
 
407
392
  try {
408
- const design = collectDesign();
409
- const result = await getPlatform().createProject({
410
- ..._form,
411
- destination,
412
- ...(_tab === "starter" && _starter ? { starter: _starter } : { template: _template }),
413
- ...(design ? { design } : {}),
414
- });
415
- finish(result);
393
+ const result = await getPlatform().createProject({ ..._form, destination, ...source });
394
+ await after(result);
416
395
  } catch (error) {
417
396
  _creating = false;
418
397
  captureError(error);
@@ -420,82 +399,54 @@ function renderModal() {
420
399
  }
421
400
  };
422
401
 
423
- const onAgentSubmit = async () => {
424
- const destination = validateParams();
425
- if (!destination) {
426
- return;
427
- }
428
-
429
- _creating = true;
430
- _error = "";
431
- _nameError = "";
432
- renderModal();
402
+ const onSubmit = () =>
403
+ create(_starter ? { starter: _starter } : { template: "blank" }, finishCreated);
433
404
 
434
- try {
435
- const design = collectDesign();
436
- const result = await getPlatform().createProject({
437
- ..._form,
438
- destination,
439
- template: "blank",
440
- ...(design ? { design } : {}),
441
- });
405
+ const onAgentSubmit = () =>
406
+ create({ template: "blank" }, async (result) => {
442
407
  // The window that opens the project consumes this and hands the prompt to the assistant.
443
408
  setPendingAgentPrompt(result.root, _agentPrompt.trim());
444
- finish(result);
445
- } catch (error) {
446
- _creating = false;
447
- captureError(error);
448
- renderModal();
449
- }
450
- };
409
+ await finishCreated(result);
410
+ });
451
411
 
452
412
  // ─── Step 1: source selection ──────────────────────────────────────────────
453
413
 
454
- const templateSourceTpl = () => html`
414
+ const starterSourceTpl = () => html`
415
+ <div class="new-project-tab-intro">
416
+ Every starter is a complete site copied in as plain files you own — open it, look at it, then
417
+ make it yours.
418
+ </div>
455
419
  <div class="new-project-templates">
456
- ${PROJECT_TEMPLATES.map(
457
- (t) => html`
420
+ ${_starters.map(
421
+ (s) => html`
458
422
  <button
459
423
  type="button"
460
- class="new-project-template ${_template === t.id ? "selected" : ""}"
461
- @click=${() => selectTemplate(t.id)}
462
- title=${t.tagline}
424
+ class="new-project-template ${_starter === s.id ? "selected" : ""}"
425
+ @click=${() => selectStarter(s.id)}
426
+ title=${s.description}
463
427
  >
464
- <div class="new-project-template-blank">${t.glyph}</div>
428
+ <img class="new-project-template-thumb" src=${s.thumbnail} alt="" />
465
429
  <div class="new-project-template-body">
466
- <div class="new-project-template-name">${t.name}</div>
467
- <div class="new-project-template-tag">${t.tagline}</div>
430
+ <div class="new-project-template-name">${s.name}</div>
431
+ <div class="new-project-template-tag">${s.tagline}</div>
468
432
  </div>
469
433
  </button>
470
434
  `,
471
435
  )}
436
+ <button
437
+ type="button"
438
+ class="new-project-template new-project-scratch ${_starter === "" ? "selected" : ""}"
439
+ @click=${() => selectStarter("")}
440
+ >
441
+ <div class="new-project-template-blank">+</div>
442
+ <div class="new-project-template-body">
443
+ <div class="new-project-template-name">Start from scratch</div>
444
+ <div class="new-project-template-tag">An empty site with one page</div>
445
+ </div>
446
+ </button>
472
447
  </div>
473
448
  `;
474
449
 
475
- const starterSourceTpl = () =>
476
- _starters.length > 0
477
- ? html`
478
- <div class="new-project-templates">
479
- ${_starters.map(
480
- (s) => html`
481
- <button
482
- type="button"
483
- class="new-project-template ${_starter === s.id ? "selected" : ""}"
484
- @click=${() => selectStarter(s.id)}
485
- title=${s.description}
486
- >
487
- <img class="new-project-template-thumb" src=${s.thumbnail} alt="" />
488
- <div class="new-project-template-body">
489
- <div class="new-project-template-name">${s.name}</div>
490
- <div class="new-project-template-tag">${s.tagline}</div>
491
- </div>
492
- </button>
493
- `,
494
- )}
495
- </div>
496
- `
497
- : html`<div class="new-project-tab-intro">No starter sites are available.</div>`;
498
-
499
450
  const agentSourceTpl = () => {
500
451
  if (!aiGateOpen()) {
501
452
  return html`
@@ -532,9 +483,6 @@ function renderModal() {
532
483
 
533
484
  const sourceBodyTpl = () => {
534
485
  switch (_tab) {
535
- case "starter": {
536
- return starterSourceTpl();
537
- }
538
486
  case "import": {
539
487
  return renderImportSource(importCtx);
540
488
  }
@@ -542,14 +490,15 @@ function renderModal() {
542
490
  return agentSourceTpl();
543
491
  }
544
492
  default: {
545
- return templateSourceTpl();
493
+ return starterSourceTpl();
546
494
  }
547
495
  }
548
496
  };
549
497
 
550
- // ─── Step 2: parameters ────────────────────────────────────────────────────
498
+ // ─── Step 2: name + location ───────────────────────────────────────────────
551
499
 
552
- const nameDirFieldsTpl = () => html`
500
+ const paramsBodyTpl = () => html`
501
+ <div class="new-project-step-context">${sourceLabel()}</div>
553
502
  <label class="new-project-field">
554
503
  <span class="new-project-label">Project Name *</span>
555
504
  <sp-textfield
@@ -573,47 +522,14 @@ function renderModal() {
573
522
  rerender: renderModal,
574
523
  slug: _form.directory,
575
524
  })}
576
- `;
577
-
578
- const paramsBodyTpl = () => html`
579
- <div class="new-project-step-context">${sourceLabel()}</div>
580
- <div class="new-project-step-heading">New Project Parameters</div>
581
- ${nameDirFieldsTpl()}
582
525
  ${
583
526
  _tab === "import"
584
527
  ? renderImportStatus()
585
528
  : html`
586
- <label class="new-project-field">
587
- <span class="new-project-label">Description</span>
588
- <sp-textfield
589
- placeholder="A short description of the site"
590
- .value=${_form.description}
591
- @input=${onInput("description")}
592
- style="width: 100%"
593
- ></sp-textfield>
594
- </label>
595
-
596
- <label class="new-project-field">
597
- <span class="new-project-label">Production URL</span>
598
- <sp-textfield
599
- placeholder="https://example.com"
600
- .value=${_form.url}
601
- @input=${onInput("url")}
602
- style="width: 100%"
603
- ></sp-textfield>
604
- </label>
605
-
606
- <label class="new-project-field">
607
- <span class="new-project-label">Deployment Adapter</span>
608
- <sp-picker label="Adapter" .value=${_form.adapter} @change=${onAdapterChange}>
609
- <sp-menu-item value="static">Static</sp-menu-item>
610
- <sp-menu-item value="cloudflare-pages">Cloudflare Pages</sp-menu-item>
611
- <sp-menu-item value="node">Node</sp-menu-item>
612
- <sp-menu-item value="bun">Bun</sp-menu-item>
613
- </sp-picker>
614
- </label>
615
-
616
- ${renderDesignFields({ rerender: renderModal })}
529
+ <div class="new-project-tab-intro">
530
+ The site's address, deployment target and design tokens are project settings — set
531
+ them from Settings once the project is open.
532
+ </div>
617
533
  `
618
534
  }
619
535
  `;
@@ -621,7 +537,14 @@ function renderModal() {
621
537
  // ─── Footer ────────────────────────────────────────────────────────────────
622
538
 
623
539
  const footerTpl = () => {
540
+ const cancel = html`
541
+ <sp-button variant="secondary" ?disabled=${_creating} @click=${closeNewProjectModal}>
542
+ Cancel
543
+ </sp-button>
544
+ `;
624
545
  if (isImportRunning()) {
546
+ // One cancel, not two: the run's own Cancel is the step's Cancel, and dismissing the modal
547
+ // (Escape, the underlay, the header ✕) aborts the run on the way out.
625
548
  return html`
626
549
  <sp-button variant="secondary" @click=${() => cancelImport(importCtx)}>
627
550
  Cancel Import
@@ -631,7 +554,7 @@ function renderModal() {
631
554
  if (_step === "source") {
632
555
  const gated = (_tab === "import" || _tab === "agent") && !aiGateOpen();
633
556
  return html`
634
- <sp-button variant="secondary" @click=${closeNewProjectModal}>Cancel</sp-button>
557
+ ${cancel}
635
558
  ${gated ? "" : html`<sp-button variant="accent" @click=${goNext}>Next</sp-button>`}
636
559
  `;
637
560
  }
@@ -654,6 +577,7 @@ function renderModal() {
654
577
  </sp-button>
655
578
  `;
656
579
  return html`
580
+ ${cancel}
657
581
  <sp-button variant="secondary" ?disabled=${_creating} @click=${goBack}>Back</sp-button>
658
582
  ${primary}
659
583
  `;
@@ -668,16 +592,11 @@ function renderModal() {
668
592
 
669
593
  const tpl = html`
670
594
  <sp-underlay open @close=${closeNewProjectModal}></sp-underlay>
671
- <div
672
- class="new-project-modal"
673
- @keydown=${(e: KeyboardEvent) => {
674
- if (e.key === "Escape") {
675
- closeNewProjectModal();
676
- }
677
- }}
678
- >
595
+ <div class="new-project-modal" data-jx-region="overlay.dialog:new-project">
679
596
  <div class="new-project-modal-header">
680
- <h2 class="new-project-modal-title">Start new project from:</h2>
597
+ <h2 class="new-project-modal-title">
598
+ ${_step === "source" ? "Choose a starting point" : "Name your project"}
599
+ </h2>
681
600
  <sp-action-button quiet size="s" @click=${closeNewProjectModal} title="Close">
682
601
  <sp-icon-close slot="icon"></sp-icon-close>
683
602
  </sp-action-button>
@@ -687,8 +606,7 @@ function renderModal() {
687
606
  ? html`
688
607
  <div class="new-project-tabs">
689
608
  <sp-tabs selected=${_tab} quiet @change=${onTabChange}>
690
- <sp-tab value="template" label="Template"></sp-tab>
691
- <sp-tab value="starter" label="Starter Site"></sp-tab>
609
+ <sp-tab value="starter" label="Starters"></sp-tab>
692
610
  ${platform.importSite ? html`<sp-tab value="import" label="Import"></sp-tab>` : ""}
693
611
  <sp-tab value="agent" label="Agent"></sp-tab>
694
612
  </sp-tabs>
@@ -716,10 +634,68 @@ function renderModal() {
716
634
  `;
717
635
 
718
636
  if (!_handle) {
719
- _handle = openModal(tpl);
637
+ _handle = openModal(tpl, { label: "New Project", onDismiss: closeNewProjectModal });
720
638
  } else {
721
639
  _handle.update(tpl);
722
640
  }
723
641
  }
724
642
 
725
643
  let _dirDerived = true;
644
+
645
+ // ─── Commands ─────────────────────────────────────────────────────────────────
646
+
647
+ /** The wizard's source tabs, in strip order — the declared values of `project.new`'s `tab`. */
648
+ export const NEW_PROJECT_TABS = ["starter", "import", "agent"] as const;
649
+
650
+ /**
651
+ * Open the New Project wizard.
652
+ *
653
+ * `tab` is declared as an enum rather than passed through, because the two non-default tabs are AI
654
+ * gated: naming one that does not exist would open the wizard on Starters and a shot would record
655
+ * that as "the Import tab". The wizard resolves the returned promise when the project is created or
656
+ * dismissed; the command deliberately does not await it — opening a modal is the action, and
657
+ * blocking `run` until a human finishes a wizard would hold the palette open behind it.
658
+ *
659
+ * @returns {AnyCommand[]}
660
+ */
661
+ export function newProjectCommands(): AnyCommand[] {
662
+ return [
663
+ {
664
+ args: {
665
+ additionalProperties: false,
666
+ properties: {
667
+ tab: enumProperty(NEW_PROJECT_TABS, "Which source tab to open on. Defaults to Starters."),
668
+ },
669
+ required: [],
670
+ type: "object",
671
+ },
672
+ category: "Project",
673
+ id: "project.new",
674
+ level: "application",
675
+ menus: ["commandbar/overflow", "palette"],
676
+ group: "1_file",
677
+ aiTool: {
678
+ description:
679
+ "Open the New Project wizard, optionally on the Starters, Import or Agent tab.",
680
+ name: "open_new_project",
681
+ },
682
+ run: (_commandCtx, args) => {
683
+ const tab =
684
+ (args as { tab?: unknown }).tab === undefined
685
+ ? undefined
686
+ : enumArg("project.new", args, "tab", NEW_PROJECT_TABS);
687
+ void openNewProjectModal(tab === undefined ? {} : { tab });
688
+ },
689
+ title: "New Project…",
690
+ },
691
+ ];
692
+ }
693
+
694
+ /**
695
+ * Register the New Project verb.
696
+ *
697
+ * @param {CommandRegistry} registry
698
+ */
699
+ export function registerNewProjectCommands(registry: CommandRegistry): void {
700
+ registry.registerAll(newProjectCommands());
701
+ }