@jxsuite/studio 2.0.0 → 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-0hzentwq.js → components-2bpqp4mq.js} +5 -7
  7. package/dist/chunks/{components-0hzentwq.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-2c188n41.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-97qcgvdn.js → studio-adn0z1k3.js} +14 -8
  74. package/dist/chunks/{studio-97qcgvdn.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-hhj92gcr.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-ygzav7z5.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-n9zmpxjg.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 +23711 -35185
  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 +34 -9
  214. package/src/new-project/location-fields.ts +9 -1
  215. package/src/new-project/new-project-modal.ts +270 -262
  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 -120
  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 +75 -2
  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 +113 -0
  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-qct0gt8f.js +0 -531
  363. package/dist/chunks/extension-sections-qct0gt8f.js.map +0 -12
  364. package/dist/chunks/files-02zn5y24.js +0 -50
  365. package/dist/chunks/files-02zn5y24.js.map +0 -9
  366. package/dist/chunks/git-panel-mbb0y0hz.js +0 -28
  367. package/dist/chunks/git-panel-mbb0y0hz.js.map +0 -9
  368. package/dist/chunks/layers-panel-9qekmqk1.js +0 -26
  369. package/dist/chunks/layers-panel-9qekmqk1.js.map +0 -9
  370. package/dist/chunks/settings-modal-gj0hrah4.js +0 -29
  371. package/dist/chunks/settings-modal-gj0hrah4.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-4k0m4fmp.js.map +0 -13
  381. package/dist/chunks/studio-568nx4t5.js +0 -469
  382. package/dist/chunks/studio-568nx4t5.js.map +0 -14
  383. package/dist/chunks/studio-9smfzqhc.js +0 -19
  384. package/dist/chunks/studio-9smfzqhc.js.map +0 -10
  385. package/dist/chunks/studio-asvf0j9e.js +0 -429
  386. package/dist/chunks/studio-asvf0j9e.js.map +0 -13
  387. package/dist/chunks/studio-d77qm3pk.js +0 -3427
  388. package/dist/chunks/studio-d77qm3pk.js.map +0 -26
  389. package/dist/chunks/studio-j6gqyjf4.js +0 -1854
  390. package/dist/chunks/studio-j6gqyjf4.js.map +0 -18
  391. package/dist/chunks/studio-n9zmpxjg.js.map +0 -115
  392. package/dist/chunks/studio-qkvt37c0.js.map +0 -9
  393. package/dist/chunks/studio-rq0vxb19.js +0 -3744
  394. package/dist/chunks/studio-rq0vxb19.js.map +0 -22
  395. package/dist/chunks/studio-wd6ewdvd.js +0 -853
  396. package/dist/chunks/studio-wd6ewdvd.js.map +0 -12
  397. package/dist/chunks/studio-xq0t7n5t.js +0 -140
  398. package/dist/chunks/studio-xq0t7n5t.js.map +0 -10
  399. package/dist/chunks/studio-xze4zrs1.js.map +0 -10
  400. package/dist/chunks/studio-ygzav7z5.js.map +0 -46
  401. package/dist/chunks/studio-zmy7241e.js +0 -399
  402. package/dist/chunks/studio-zmy7241e.js.map +0 -12
  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-2c188n41.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-hhj92gcr.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,24 +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.
8
4
  *
9
- * Import and Agent are gated behind the AI credentials form until a key is stored.
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.
14
+ *
15
+ * Import and Agent need working AI, so both are gated until credentials exist — a key stored in
16
+ * this browser, or a backend that holds its own (managed cloud platforms, env-keyed dev servers).
17
+ * While gated they offer the same two paths as the assistant sidebar: connect Cloudflare for
18
+ * Workers AI, or bring a key.
19
+ *
20
+ * @docs studio/projects/create
10
21
  */
11
22
 
12
23
  import { html } from "lit-html";
13
24
  import { errorMessage } from "@jxsuite/schema/parse";
14
25
  import { openModal } from "../ui/layers";
26
+ import { enumArg, enumProperty } from "../commands/command-args";
27
+ import type { AnyCommand, CommandRegistry } from "../commands/registry";
15
28
  import { getPlatform } from "../platform";
16
29
  import { installUrlOf } from "../platform-errors";
17
- import { hasOpenAiKey } from "../services/ai-settings";
30
+ import { hasAiCredentials } from "../services/ai-models";
18
31
  import { setPendingAgentPrompt } from "../services/agent-seed";
32
+ import { initProjectRepo } from "../files/files";
19
33
  import { createAiCredentialsForm } from "../ui/ai-credentials-form";
20
- import { PROJECT_TEMPLATES } from "./templates";
21
- import { collectDesign, renderDesignFields, resetDesignFields } from "./design-fields";
34
+ import { createManagedConnect } from "../ui/ai-managed-connect";
22
35
  import {
23
36
  collectDestination,
24
37
  loadLocationOptions,
@@ -40,28 +53,21 @@ import type { ProjectConfig } from "@jxsuite/schema/types";
40
53
  import type { CreateProjectDestination, StarterInfo } from "../types";
41
54
  import type { ImportTabCtx } from "./import-tab";
42
55
 
43
- type NewProjectTab = "template" | "starter" | "import" | "agent";
56
+ type NewProjectTab = (typeof NEW_PROJECT_TABS)[number];
44
57
  type WizardStep = "source" | "params";
45
58
 
46
59
  let _handle: ReturnType<typeof openModal> | null = null;
47
60
 
48
- let _form = {
49
- adapter: "static",
50
- description: "",
51
- directory: "",
52
- name: "",
53
- url: "",
54
- };
61
+ let _form = { directory: "", name: "" };
55
62
 
56
- let _tab: NewProjectTab = "template";
63
+ let _tab: NewProjectTab = "starter";
57
64
  let _step: WizardStep = "source";
58
- let _template = "blank";
65
+ /** The step-1 choice on the Starters tab: a starter id, or `""` for "Start from scratch". */
59
66
  let _starter = "";
67
+ /** Set once the user picks a card, so an arriving starter list stops overriding their choice. */
68
+ let _starterTouched = false;
60
69
  let _agentPrompt = "";
61
70
 
62
- /** Which source the Parameters step was last seeded for — Back/Next keeps user edits intact. */
63
- let _paramsSeededFor = "";
64
-
65
71
  let _error = "";
66
72
 
67
73
  /**
@@ -89,7 +95,7 @@ function focusNameField() {
89
95
 
90
96
  let _creating = false;
91
97
 
92
- /** 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). */
93
99
  let _starters: StarterInfo[] = [];
94
100
 
95
101
  let _resolve: ((result: { root: string; config: ProjectConfig } | null) => void) | null = null;
@@ -98,48 +104,64 @@ let _resolve: ((result: { root: string; config: ProjectConfig } | null) => void)
98
104
  // Before a platform is registered (the form fetches models through the platform on edit).
99
105
  let _credsForm: ReturnType<typeof createAiCredentialsForm> | null = null;
100
106
 
107
+ function rerenderIfOpen() {
108
+ if (_handle) {
109
+ renderModal();
110
+ }
111
+ }
112
+
101
113
  function credsForm() {
102
114
  _credsForm ??= createAiCredentialsForm({
103
- onSaved: () => {
104
- if (_handle) {
105
- renderModal();
106
- }
107
- },
108
- requestRender: () => {
109
- if (_handle) {
110
- renderModal();
111
- }
112
- },
115
+ onSaved: rerenderIfOpen,
116
+ requestRender: rerenderIfOpen,
113
117
  });
114
118
  return _credsForm;
115
119
  }
116
120
 
121
+ /**
122
+ * The keyless "Connect Cloudflare" option shown beside the key form on managed platforms — the same
123
+ * controller the assistant sidebar uses. Lazy for the same reason as the form above.
124
+ */
125
+ let _managedConnect: ReturnType<typeof createManagedConnect> | null = null;
126
+
127
+ function managedConnect() {
128
+ _managedConnect ??= createManagedConnect({ requestRender: rerenderIfOpen });
129
+ return _managedConnect;
130
+ }
131
+
132
+ /**
133
+ * The Import and Agent tabs need working AI, which a managed platform can supply without any local
134
+ * key. Probe on every gated render so the Cloudflare option appears as soon as the backend
135
+ * answers.
136
+ */
137
+ function aiGateOpen(): boolean {
138
+ if (hasAiCredentials()) {
139
+ return true;
140
+ }
141
+ managedConnect().ensureProbe();
142
+ return false;
143
+ }
144
+
117
145
  /**
118
146
  * Open the New Project modal. Returns a promise that resolves with the created project info (or
119
147
  * null if cancelled).
120
148
  *
149
+ * @param {{ tab?: NewProjectTab }} [options] Which source tab to open on; defaults to Starters.
121
150
  * @returns {Promise<{ root: string; config: object } | null>}
122
151
  */
123
- export function openNewProjectModal(options?: { tab?: "template" | "starter" }): Promise<{
152
+ export function openNewProjectModal(options?: { tab?: NewProjectTab }): Promise<{
124
153
  root: string;
125
154
  config: ProjectConfig;
126
155
  } | null> {
127
156
  if (_handle) {
128
157
  return Promise.resolve(null);
129
158
  }
130
- _form = {
131
- adapter: "static",
132
- description: "",
133
- directory: "",
134
- name: "",
135
- url: "",
136
- };
137
- _tab = options?.tab ?? "template";
159
+ _form = { directory: "", name: "" };
160
+ _tab = options?.tab ?? "starter";
138
161
  _step = "source";
139
- _template = "blank";
140
162
  _starter = "";
163
+ _starterTouched = false;
141
164
  _agentPrompt = "";
142
- _paramsSeededFor = "";
143
165
  _error = "";
144
166
  _nameError = "";
145
167
  _errorInstallUrl = "";
@@ -147,12 +169,10 @@ export function openNewProjectModal(options?: { tab?: "template" | "starter" }):
147
169
  _starters = [];
148
170
  _dirDerived = true;
149
171
  resetImportTab();
150
- resetDesignFields();
151
172
  resetLocationFields();
152
173
 
153
174
  // Load the destination options (repo-mode owners) and starter templates in the background,
154
- // Re-rendering when they arrive. Platforms without starters leave the Starter Site tab showing
155
- // Its empty note.
175
+ // Re-rendering when they arrive. A platform without starters shows the scratch card alone.
156
176
  const platform = getPlatform();
157
177
  loadLocationOptions(() => {
158
178
  if (_handle) {
@@ -164,7 +184,8 @@ export function openNewProjectModal(options?: { tab?: "template" | "starter" }):
164
184
  .listStarters()
165
185
  .then((starters) => {
166
186
  _starters = starters;
167
- if (!_starter) {
187
+ // Starters lead: the gallery opens with a real site selected, not an empty scaffold.
188
+ if (!_starterTouched) {
168
189
  _starter = starters[0]?.id ?? "";
169
190
  }
170
191
  if (_handle) {
@@ -172,7 +193,7 @@ export function openNewProjectModal(options?: { tab?: "template" | "starter" }):
172
193
  }
173
194
  })
174
195
  .catch(() => {
175
- /* Non-fatal: the Starter tab keeps its empty note. */
196
+ /* Non-fatal: the gallery keeps the scratch card. */
176
197
  });
177
198
  }
178
199
 
@@ -182,10 +203,17 @@ export function openNewProjectModal(options?: { tab?: "template" | "starter" }):
182
203
  });
183
204
  }
184
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
+ */
185
210
  export function closeNewProjectModal() {
186
- if (!_handle || _creating || isImportRunning()) {
211
+ if (!_handle || _creating) {
187
212
  return;
188
213
  }
214
+ if (isImportRunning()) {
215
+ cancelImport(importCtxFor(renderModal));
216
+ }
189
217
  _handle.close();
190
218
  _handle = null;
191
219
  if (_resolve) {
@@ -207,6 +235,15 @@ function finish(result: { root: string; config: ProjectConfig }) {
207
235
  }
208
236
  }
209
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
+
210
247
  function deriveSlug(name: string): string {
211
248
  return name
212
249
  .toLowerCase()
@@ -214,12 +251,9 @@ function deriveSlug(name: string): string {
214
251
  .replaceAll(/^-|-$/g, "");
215
252
  }
216
253
 
217
- /** 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. */
218
255
  function sourceLabel(): string {
219
256
  switch (_tab) {
220
- case "starter": {
221
- return `Starter Site · ${_starters.find((s) => s.id === _starter)?.name ?? _starter}`;
222
- }
223
257
  case "import": {
224
258
  return "Import";
225
259
  }
@@ -227,52 +261,79 @@ function sourceLabel(): string {
227
261
  return "Agent";
228
262
  }
229
263
  default: {
230
- 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";
231
267
  }
232
268
  }
233
269
  }
234
270
 
235
- function renderModal() {
236
- const platform = getPlatform();
237
- 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 {
274
+ aiGateOpen,
238
275
  credsForm: credsForm(),
239
276
  form: _form,
240
- onDone: finish,
241
- rerender: renderModal,
277
+ managedConnect: managedConnect(),
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,
242
283
  resolveDestination: () => validateParams(),
243
284
  };
285
+ }
244
286
 
245
- const onInput =
246
- (field: "name" | "description" | "url" | "adapter" | "directory") => (e: Event) => {
247
- _form[field] = (e.target as HTMLInputElement).value;
248
- if (field === "name") {
249
- _nameError = "";
250
- }
251
- if (field === "name" && !_form.directory) {
252
- // 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) {
253
322
  _dirDerived = true;
254
323
  }
255
- if (_dirDerived && field === "name") {
324
+ if (_dirDerived) {
256
325
  _form.directory = deriveSlug(_form.name);
257
326
  }
258
- if (field === "directory") {
259
- _dirDerived = false;
260
- }
261
- renderModal();
262
- };
263
-
264
- const onAdapterChange = (e: Event) => {
265
- _form.adapter = (e.target as HTMLInputElement).value;
266
- renderModal();
267
- };
268
-
269
- const selectTemplate = (id: string) => {
270
- _template = id;
327
+ } else {
328
+ _dirDerived = false;
329
+ }
271
330
  renderModal();
272
331
  };
273
332
 
274
333
  const selectStarter = (id: string) => {
275
334
  _starter = id;
335
+ _starterTouched = true;
336
+ _error = "";
276
337
  renderModal();
277
338
  };
278
339
 
@@ -288,11 +349,6 @@ function renderModal() {
288
349
  // ─── Step transitions ──────────────────────────────────────────────────────
289
350
 
290
351
  const goNext = () => {
291
- if (_tab === "starter" && !_starter) {
292
- _error = "Choose a starter site";
293
- renderModal();
294
- return;
295
- }
296
352
  if (_tab === "import" && !validateImportSource(importCtx)) {
297
353
  return;
298
354
  }
@@ -301,28 +357,6 @@ function renderModal() {
301
357
  renderModal();
302
358
  return;
303
359
  }
304
-
305
- // Seed the Parameters step from the chosen source — only when the source changed, so Back +
306
- // Next round-trips keep the user's edits.
307
- const seedKey = `${_tab}:${_template}:${_starter}`;
308
- if (_paramsSeededFor !== seedKey) {
309
- _paramsSeededFor = seedKey;
310
- if (_tab === "starter") {
311
- const meta = _starters.find((s) => s.id === _starter);
312
- if (meta && !_form.description.trim()) {
313
- _form.description = meta.tagline;
314
- }
315
- resetDesignFields({
316
- ...(meta?.accent ? { accent: meta.accent } : {}),
317
- mediaNote: "Leave empty to keep the starter's breakpoints.",
318
- });
319
- } else if (_tab === "template" || _tab === "agent") {
320
- const templateId = _tab === "agent" ? "blank" : _template;
321
- const meta = PROJECT_TEMPLATES.find((t) => t.id === templateId);
322
- resetDesignFields(meta ? { media: meta.media } : {});
323
- }
324
- }
325
-
326
360
  _step = "params";
327
361
  _error = "";
328
362
  renderModal();
@@ -340,32 +374,11 @@ function renderModal() {
340
374
 
341
375
  // ─── Submission ────────────────────────────────────────────────────────────
342
376
 
343
- /**
344
- * Validate the Parameters step, returning the destination to create at (null when something is
345
- * missing — the reason is already rendered inline and the modal re-drawn). The slug is derived
346
- * from the name when left blank, but the destination itself is never guessed.
347
- */
348
- const validateParams = (): CreateProjectDestination | null => {
349
- if (!_form.name.trim()) {
350
- _nameError = "Project name is required";
351
- renderModal();
352
- focusNameField();
353
- return null;
354
- }
355
- if (!_form.directory.trim()) {
356
- _form.directory = deriveSlug(_form.name);
357
- }
358
- const destination = collectDestination(_form.directory);
359
- if (!destination) {
360
- _nameError = "";
361
- renderModal();
362
- focusNameField();
363
- return null;
364
- }
365
- return destination;
366
- };
367
-
368
- 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
+ ) => {
369
382
  const destination = validateParams();
370
383
  if (!destination) {
371
384
  return;
@@ -377,14 +390,8 @@ function renderModal() {
377
390
  renderModal();
378
391
 
379
392
  try {
380
- const design = collectDesign();
381
- const result = await getPlatform().createProject({
382
- ..._form,
383
- destination,
384
- ...(_tab === "starter" && _starter ? { starter: _starter } : { template: _template }),
385
- ...(design ? { design } : {}),
386
- });
387
- finish(result);
393
+ const result = await getPlatform().createProject({ ..._form, destination, ...source });
394
+ await after(result);
388
395
  } catch (error) {
389
396
  _creating = false;
390
397
  captureError(error);
@@ -392,90 +399,66 @@ function renderModal() {
392
399
  }
393
400
  };
394
401
 
395
- const onAgentSubmit = async () => {
396
- const destination = validateParams();
397
- if (!destination) {
398
- return;
399
- }
400
-
401
- _creating = true;
402
- _error = "";
403
- _nameError = "";
404
- renderModal();
402
+ const onSubmit = () =>
403
+ create(_starter ? { starter: _starter } : { template: "blank" }, finishCreated);
405
404
 
406
- try {
407
- const design = collectDesign();
408
- const result = await getPlatform().createProject({
409
- ..._form,
410
- destination,
411
- template: "blank",
412
- ...(design ? { design } : {}),
413
- });
405
+ const onAgentSubmit = () =>
406
+ create({ template: "blank" }, async (result) => {
414
407
  // The window that opens the project consumes this and hands the prompt to the assistant.
415
408
  setPendingAgentPrompt(result.root, _agentPrompt.trim());
416
- finish(result);
417
- } catch (error) {
418
- _creating = false;
419
- captureError(error);
420
- renderModal();
421
- }
422
- };
409
+ await finishCreated(result);
410
+ });
423
411
 
424
412
  // ─── Step 1: source selection ──────────────────────────────────────────────
425
413
 
426
- 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>
427
419
  <div class="new-project-templates">
428
- ${PROJECT_TEMPLATES.map(
429
- (t) => html`
420
+ ${_starters.map(
421
+ (s) => html`
430
422
  <button
431
423
  type="button"
432
- class="new-project-template ${_template === t.id ? "selected" : ""}"
433
- @click=${() => selectTemplate(t.id)}
434
- title=${t.tagline}
424
+ class="new-project-template ${_starter === s.id ? "selected" : ""}"
425
+ @click=${() => selectStarter(s.id)}
426
+ title=${s.description}
435
427
  >
436
- <div class="new-project-template-blank">${t.glyph}</div>
428
+ <img class="new-project-template-thumb" src=${s.thumbnail} alt="" />
437
429
  <div class="new-project-template-body">
438
- <div class="new-project-template-name">${t.name}</div>
439
- <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>
440
432
  </div>
441
433
  </button>
442
434
  `,
443
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>
444
447
  </div>
445
448
  `;
446
449
 
447
- const starterSourceTpl = () =>
448
- _starters.length > 0
449
- ? html`
450
- <div class="new-project-templates">
451
- ${_starters.map(
452
- (s) => html`
453
- <button
454
- type="button"
455
- class="new-project-template ${_starter === s.id ? "selected" : ""}"
456
- @click=${() => selectStarter(s.id)}
457
- title=${s.description}
458
- >
459
- <img class="new-project-template-thumb" src=${s.thumbnail} alt="" />
460
- <div class="new-project-template-body">
461
- <div class="new-project-template-name">${s.name}</div>
462
- <div class="new-project-template-tag">${s.tagline}</div>
463
- </div>
464
- </button>
465
- `,
466
- )}
467
- </div>
468
- `
469
- : html`<div class="new-project-tab-intro">No starter sites are available.</div>`;
470
-
471
450
  const agentSourceTpl = () => {
472
- if (!hasOpenAiKey()) {
451
+ if (!aiGateOpen()) {
473
452
  return html`
474
453
  <div class="new-project-tab-intro">
475
- The agent uses your AI provider to build the site. Add an OpenAI-compatible API key to
476
- continue.
454
+ The agent uses your AI provider to build the site.
455
+ ${
456
+ managedConnect().canOffer()
457
+ ? "Connect Cloudflare, or add an OpenAI-compatible API key, to continue."
458
+ : "Add an OpenAI-compatible API key to continue."
459
+ }
477
460
  </div>
478
- <div class="new-project-creds">${credsForm().render()}</div>
461
+ <div class="new-project-creds">${managedConnect().render()} ${credsForm().render()}</div>
479
462
  `;
480
463
  }
481
464
  return html`
@@ -500,9 +483,6 @@ function renderModal() {
500
483
 
501
484
  const sourceBodyTpl = () => {
502
485
  switch (_tab) {
503
- case "starter": {
504
- return starterSourceTpl();
505
- }
506
486
  case "import": {
507
487
  return renderImportSource(importCtx);
508
488
  }
@@ -510,14 +490,15 @@ function renderModal() {
510
490
  return agentSourceTpl();
511
491
  }
512
492
  default: {
513
- return templateSourceTpl();
493
+ return starterSourceTpl();
514
494
  }
515
495
  }
516
496
  };
517
497
 
518
- // ─── Step 2: parameters ────────────────────────────────────────────────────
498
+ // ─── Step 2: name + location ───────────────────────────────────────────────
519
499
 
520
- const nameDirFieldsTpl = () => html`
500
+ const paramsBodyTpl = () => html`
501
+ <div class="new-project-step-context">${sourceLabel()}</div>
521
502
  <label class="new-project-field">
522
503
  <span class="new-project-label">Project Name *</span>
523
504
  <sp-textfield
@@ -541,47 +522,14 @@ function renderModal() {
541
522
  rerender: renderModal,
542
523
  slug: _form.directory,
543
524
  })}
544
- `;
545
-
546
- const paramsBodyTpl = () => html`
547
- <div class="new-project-step-context">${sourceLabel()}</div>
548
- <div class="new-project-step-heading">New Project Parameters</div>
549
- ${nameDirFieldsTpl()}
550
525
  ${
551
526
  _tab === "import"
552
527
  ? renderImportStatus()
553
528
  : html`
554
- <label class="new-project-field">
555
- <span class="new-project-label">Description</span>
556
- <sp-textfield
557
- placeholder="A short description of the site"
558
- .value=${_form.description}
559
- @input=${onInput("description")}
560
- style="width: 100%"
561
- ></sp-textfield>
562
- </label>
563
-
564
- <label class="new-project-field">
565
- <span class="new-project-label">Production URL</span>
566
- <sp-textfield
567
- placeholder="https://example.com"
568
- .value=${_form.url}
569
- @input=${onInput("url")}
570
- style="width: 100%"
571
- ></sp-textfield>
572
- </label>
573
-
574
- <label class="new-project-field">
575
- <span class="new-project-label">Deployment Adapter</span>
576
- <sp-picker label="Adapter" .value=${_form.adapter} @change=${onAdapterChange}>
577
- <sp-menu-item value="static">Static</sp-menu-item>
578
- <sp-menu-item value="cloudflare-pages">Cloudflare Pages</sp-menu-item>
579
- <sp-menu-item value="node">Node</sp-menu-item>
580
- <sp-menu-item value="bun">Bun</sp-menu-item>
581
- </sp-picker>
582
- </label>
583
-
584
- ${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>
585
533
  `
586
534
  }
587
535
  `;
@@ -589,7 +537,14 @@ function renderModal() {
589
537
  // ─── Footer ────────────────────────────────────────────────────────────────
590
538
 
591
539
  const footerTpl = () => {
540
+ const cancel = html`
541
+ <sp-button variant="secondary" ?disabled=${_creating} @click=${closeNewProjectModal}>
542
+ Cancel
543
+ </sp-button>
544
+ `;
592
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.
593
548
  return html`
594
549
  <sp-button variant="secondary" @click=${() => cancelImport(importCtx)}>
595
550
  Cancel Import
@@ -597,9 +552,9 @@ function renderModal() {
597
552
  `;
598
553
  }
599
554
  if (_step === "source") {
600
- const gated = (_tab === "import" || _tab === "agent") && !hasOpenAiKey();
555
+ const gated = (_tab === "import" || _tab === "agent") && !aiGateOpen();
601
556
  return html`
602
- <sp-button variant="secondary" @click=${closeNewProjectModal}>Cancel</sp-button>
557
+ ${cancel}
603
558
  ${gated ? "" : html`<sp-button variant="accent" @click=${goNext}>Next</sp-button>`}
604
559
  `;
605
560
  }
@@ -622,6 +577,7 @@ function renderModal() {
622
577
  </sp-button>
623
578
  `;
624
579
  return html`
580
+ ${cancel}
625
581
  <sp-button variant="secondary" ?disabled=${_creating} @click=${goBack}>Back</sp-button>
626
582
  ${primary}
627
583
  `;
@@ -636,16 +592,11 @@ function renderModal() {
636
592
 
637
593
  const tpl = html`
638
594
  <sp-underlay open @close=${closeNewProjectModal}></sp-underlay>
639
- <div
640
- class="new-project-modal"
641
- @keydown=${(e: KeyboardEvent) => {
642
- if (e.key === "Escape") {
643
- closeNewProjectModal();
644
- }
645
- }}
646
- >
595
+ <div class="new-project-modal" data-jx-region="overlay.dialog:new-project">
647
596
  <div class="new-project-modal-header">
648
- <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>
649
600
  <sp-action-button quiet size="s" @click=${closeNewProjectModal} title="Close">
650
601
  <sp-icon-close slot="icon"></sp-icon-close>
651
602
  </sp-action-button>
@@ -655,8 +606,7 @@ function renderModal() {
655
606
  ? html`
656
607
  <div class="new-project-tabs">
657
608
  <sp-tabs selected=${_tab} quiet @change=${onTabChange}>
658
- <sp-tab value="template" label="Template"></sp-tab>
659
- <sp-tab value="starter" label="Starter Site"></sp-tab>
609
+ <sp-tab value="starter" label="Starters"></sp-tab>
660
610
  ${platform.importSite ? html`<sp-tab value="import" label="Import"></sp-tab>` : ""}
661
611
  <sp-tab value="agent" label="Agent"></sp-tab>
662
612
  </sp-tabs>
@@ -684,10 +634,68 @@ function renderModal() {
684
634
  `;
685
635
 
686
636
  if (!_handle) {
687
- _handle = openModal(tpl);
637
+ _handle = openModal(tpl, { label: "New Project", onDismiss: closeNewProjectModal });
688
638
  } else {
689
639
  _handle.update(tpl);
690
640
  }
691
641
  }
692
642
 
693
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
+ }