@jxsuite/studio 5.7.0 → 6.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 (162) hide show
  1. package/dist/chunks/{2020-cs2sb9t1.js → 2020-nkxms0ay.js} +3 -3
  2. package/dist/chunks/adapter-m948ab5f.js +4 -0
  3. package/dist/chunks/{cf-account-picker-882a7ecm.js → cf-account-picker-n8hr38v7.js} +3 -3
  4. package/dist/chunks/cf-settings-356xdtne.js +4 -0
  5. package/dist/chunks/components-yk3t844e.js +4 -0
  6. package/dist/chunks/content-source-bawmyc09.js +4 -0
  7. package/dist/chunks/{convert-to-repeater-grfep2w8.js → convert-to-repeater-7wns8kf1.js} +3 -3
  8. package/dist/chunks/editor-6sc8nacq.js +4 -0
  9. package/dist/chunks/entry-commands-8ay9fh81.js +4 -0
  10. package/dist/chunks/{extension-sections-dp8endhx.js → extension-sections-na7rj3nr.js} +3 -3
  11. package/dist/chunks/file-ops-gye0qw56.js +4 -0
  12. package/dist/chunks/files-dmc2xvcq.js +4 -0
  13. package/dist/chunks/format-host-t76b53f8.js +4 -0
  14. package/dist/chunks/fs-events-cg4tpz06.js +4 -0
  15. package/dist/chunks/git-panel-yef7bshy.js +4 -0
  16. package/dist/chunks/{grid-view-5amhg604.js → grid-view-7tt4bxw1.js} +6 -6
  17. package/dist/chunks/iframe-host-tmz30gfp.js +4 -0
  18. package/dist/chunks/{index-9ydfe04y.js → index-gybvv7a6.js} +3 -3
  19. package/dist/chunks/index-nag78a9h.js +4 -0
  20. package/dist/chunks/{javascript-d9pqgc2p.js → javascript-rnvdp3k4.js} +2 -2
  21. package/dist/chunks/{jsonMode-800yvbem.js → jsonMode-zhwsprqk.js} +5 -5
  22. package/dist/chunks/jx-validate-3gbp5nkn.js +4 -0
  23. package/dist/chunks/layers-fvz99859.js +4 -0
  24. package/dist/chunks/layers-panel-gchmbf9p.js +4 -0
  25. package/dist/chunks/lit-html-9agdspkt.js +4 -0
  26. package/dist/chunks/media-picker-rhjvyy00.js +4 -0
  27. package/dist/chunks/menu-2e4y9j7b.js +4 -0
  28. package/dist/chunks/{monaco-binding-62dtmgbf.js → monaco-binding-2jnhjjvr.js} +3 -3
  29. package/dist/chunks/monaco-buffer-rp21359g.js +4 -0
  30. package/dist/chunks/{monaco-setup-tj13ndtm.js → monaco-setup-vhh7gayr.js} +3 -3
  31. package/dist/chunks/notify-h3www560.js +4 -0
  32. package/dist/chunks/platform-hz34wcd7.js +4 -0
  33. package/dist/chunks/publish-panel-exsmjzyj.js +4 -0
  34. package/dist/chunks/quick-search-pgaqeh0m.js +4 -0
  35. package/dist/chunks/references-2cgdxnb8.js +4 -0
  36. package/dist/chunks/right-panel-jf856w5w.js +4 -0
  37. package/dist/chunks/section-registry-jnf8r5gy.js +4 -0
  38. package/dist/chunks/studio-0f55h9ye.js +5 -0
  39. package/dist/chunks/{studio-hjbg8d5y.js → studio-0sqdhnew.js} +7 -7
  40. package/dist/chunks/{studio-1wvfxkyy.js → studio-0x3jyb8r.js} +4 -4
  41. package/dist/chunks/{studio-yr04mv2j.js → studio-19x1c75w.js} +4 -4
  42. package/dist/chunks/{studio-evwgp1ym.js → studio-2sqcndak.js} +4 -4
  43. package/dist/chunks/{studio-cvbkkfqb.js → studio-3aey7dd3.js} +4 -4
  44. package/dist/chunks/{studio-888knnkc.js → studio-3bpdkq2s.js} +4 -4
  45. package/dist/chunks/{studio-ceb34dxj.js → studio-47y6fbpf.js} +4 -4
  46. package/dist/chunks/{studio-c6w3tq7t.js → studio-4m3826v2.js} +4 -4
  47. package/dist/chunks/studio-4nx2dj1g.js +4 -0
  48. package/dist/chunks/{studio-p7c1agz0.js → studio-4xv5s47r.js} +5 -5
  49. package/dist/chunks/{studio-sxc0zvpt.js → studio-5g0ag7c0.js} +3 -3
  50. package/dist/chunks/{studio-semd1s8s.js → studio-5g4t8v6w.js} +4 -4
  51. package/dist/chunks/studio-5py436sf.js +5 -0
  52. package/dist/chunks/{studio-aje966s9.js → studio-77kt594t.js} +4 -4
  53. package/dist/chunks/{studio-56z7evx4.js → studio-8f87n763.js} +6 -6
  54. package/dist/chunks/studio-8mb00b84.js +5 -0
  55. package/dist/chunks/studio-8p51gq2h.js +6 -0
  56. package/dist/chunks/studio-954ds834.js +5 -0
  57. package/dist/chunks/{studio-xta2vxp6.js → studio-9ft45c83.js} +6 -6
  58. package/dist/chunks/studio-a2138x5k.js +7 -0
  59. package/dist/chunks/studio-a9yzstqj.js +5 -0
  60. package/dist/chunks/{studio-gp02nqnw.js → studio-acdksxx6.js} +4 -4
  61. package/dist/chunks/{studio-edbtvcd1.js → studio-anyrgzvb.js} +4 -4
  62. package/dist/chunks/{studio-dkn3x3hz.js → studio-ar8jcf2y.js} +4 -4
  63. package/dist/chunks/{studio-xry7tk3z.js → studio-bmn4mn9j.js} +5 -5
  64. package/dist/chunks/{studio-03wwfvyt.js → studio-cpxkgjrj.js} +4 -4
  65. package/dist/chunks/{studio-spf2skgc.js → studio-e7xxa0sj.js} +4 -4
  66. package/dist/chunks/{studio-01qza1n3.js → studio-ecaz25vk.js} +4 -4
  67. package/dist/chunks/{studio-5e9zsamh.js → studio-fzdywr6p.js} +6 -6
  68. package/dist/chunks/{studio-65fhspdd.js → studio-g5fmf0c1.js} +4 -4
  69. package/dist/chunks/studio-g7nse7zb.js +5 -0
  70. package/dist/chunks/studio-geq1ea6k.js +14 -0
  71. package/dist/chunks/{studio-n78gjfy8.js → studio-ghkb32r8.js} +4 -4
  72. package/dist/chunks/{studio-222rqyyc.js → studio-ghrhy2y7.js} +4 -4
  73. package/dist/chunks/{studio-a7xqhc3n.js → studio-grawhey1.js} +4 -4
  74. package/dist/chunks/{studio-eh0rac07.js → studio-h0xgswew.js} +4 -4
  75. package/dist/chunks/{studio-wma2c6g8.js → studio-h9c4p2tx.js} +4 -4
  76. package/dist/chunks/studio-jyzj5cxs.js +5 -0
  77. package/dist/chunks/{studio-x92tgnee.js → studio-mgx8yhnz.js} +31 -31
  78. package/dist/chunks/{studio-0scebvb9.js → studio-mpb04y1d.js} +5 -5
  79. package/dist/chunks/{studio-xy3tv3az.js → studio-n17mty64.js} +4 -4
  80. package/dist/chunks/{studio-gfn1kpxj.js → studio-nwh2eqsk.js} +5 -5
  81. package/dist/chunks/{studio-nt06yybj.js → studio-pxg4ap1e.js} +3 -3
  82. package/dist/chunks/{studio-zpnk016a.js → studio-q1h883bt.js} +5 -5
  83. package/dist/chunks/{studio-c6zdm25z.js → studio-q4tv82h7.js} +6 -6
  84. package/dist/chunks/{studio-st75kn99.js → studio-qbj760tx.js} +5 -5
  85. package/dist/chunks/{studio-y7jehjrp.js → studio-qpqa7vk3.js} +4 -4
  86. package/dist/chunks/{studio-j6gnhvaa.js → studio-qqz8e5gk.js} +4 -4
  87. package/dist/chunks/{studio-skzanb0k.js → studio-qvasen3f.js} +4 -4
  88. package/dist/chunks/studio-rggdb3hm.js +5 -0
  89. package/dist/chunks/{studio-4xppwncr.js → studio-sghy936h.js} +3 -3
  90. package/dist/chunks/{studio-9mqfamps.js → studio-skprc6ef.js} +4 -4
  91. package/dist/chunks/{studio-1x8x26k3.js → studio-tt2nactf.js} +4 -4
  92. package/dist/chunks/{studio-54g077x3.js → studio-txjjzrsm.js} +5 -5
  93. package/dist/chunks/{studio-f195h2hq.js → studio-tyasmc39.js} +4 -4
  94. package/dist/chunks/{studio-0bbxhhce.js → studio-v2bvmbjj.js} +4 -4
  95. package/dist/chunks/{studio-pwqfmt4p.js → studio-vk9ezads.js} +4 -4
  96. package/dist/chunks/{studio-fqq890ap.js → studio-vzacdfsc.js} +3 -3
  97. package/dist/chunks/{studio-zd4kayn2.js → studio-w4c0xmrw.js} +4 -4
  98. package/dist/chunks/{studio-tw7rre6s.js → studio-wfjqhhbk.js} +4 -4
  99. package/dist/chunks/{studio-tp642n5g.js → studio-wgh5kxpj.js} +4 -4
  100. package/dist/chunks/studio-xasj0796.js +5 -0
  101. package/dist/chunks/{studio-tzegmy4w.js → studio-y78s8r2n.js} +4 -4
  102. package/dist/chunks/{studio-7pgy3xaf.js → studio-z8w183sh.js} +4 -4
  103. package/dist/chunks/{studio-01kbbqg3.js → studio-zscd9ty4.js} +124 -124
  104. package/dist/chunks/{studio-mwxgyn9h.js → studio-zxp13sss.js} +6 -6
  105. package/dist/chunks/{studio-r3kaka4d.js → studio-zygnmm01.js} +4 -4
  106. package/dist/chunks/{tsMode-k9s9fcag.js → tsMode-gr0m4dzr.js} +5 -5
  107. package/dist/chunks/workspace-m15qtcqp.js +4 -0
  108. package/dist/chunks/{ws-client-0ja08haa.js → ws-client-nf61frr1.js} +3 -3
  109. package/dist/iframe-entry.js +3 -3
  110. package/dist/manifest.json +62 -62
  111. package/dist/studio.js +83 -83
  112. package/package.json +11 -11
  113. package/src/new-project/add-repo-modal.ts +459 -59
  114. package/src/platforms/cloud.ts +95 -18
  115. package/src/publish/pages-service.ts +7 -0
  116. package/src/publish/publish-panel.ts +16 -2
  117. package/src/surfaces/add-repo.json +721 -224
  118. package/src/surfaces/add-repo.ts +196 -31
  119. package/src/types.ts +42 -3
  120. package/src/ui/schema-form.ts +6 -2
  121. package/src/utils/project-dir.ts +24 -0
  122. package/dist/chunks/adapter-y5gdq19c.js +0 -4
  123. package/dist/chunks/cf-settings-20dznc3j.js +0 -4
  124. package/dist/chunks/components-q037dya7.js +0 -4
  125. package/dist/chunks/content-source-37x3vsap.js +0 -4
  126. package/dist/chunks/editor-j5sq149q.js +0 -4
  127. package/dist/chunks/entry-commands-c5h3nqck.js +0 -4
  128. package/dist/chunks/file-ops-937cvqk6.js +0 -4
  129. package/dist/chunks/files-tznwe1p1.js +0 -4
  130. package/dist/chunks/format-host-e2pjvd25.js +0 -4
  131. package/dist/chunks/fs-events-fv64ak3s.js +0 -4
  132. package/dist/chunks/git-panel-gb713ttc.js +0 -4
  133. package/dist/chunks/iframe-host-t819mcg5.js +0 -4
  134. package/dist/chunks/index-m0x5ztax.js +0 -4
  135. package/dist/chunks/jx-validate-zc9zp77s.js +0 -4
  136. package/dist/chunks/layers-4bsr82wz.js +0 -4
  137. package/dist/chunks/layers-panel-b6shnaax.js +0 -4
  138. package/dist/chunks/lit-html-cfn61dn7.js +0 -4
  139. package/dist/chunks/media-picker-ej0kdh1j.js +0 -4
  140. package/dist/chunks/menu-rtxf9chb.js +0 -4
  141. package/dist/chunks/monaco-buffer-nn4dtx27.js +0 -4
  142. package/dist/chunks/notify-ahs57a2z.js +0 -4
  143. package/dist/chunks/platform-v9xyfkbx.js +0 -4
  144. package/dist/chunks/publish-panel-bq36xcmj.js +0 -4
  145. package/dist/chunks/quick-search-zm00h9dh.js +0 -4
  146. package/dist/chunks/references-99ws0b0m.js +0 -4
  147. package/dist/chunks/right-panel-pq2f71q3.js +0 -4
  148. package/dist/chunks/section-registry-b2yzp9sw.js +0 -4
  149. package/dist/chunks/studio-0k9mvpgj.js +0 -14
  150. package/dist/chunks/studio-1a99w39s.js +0 -5
  151. package/dist/chunks/studio-693nhyky.js +0 -5
  152. package/dist/chunks/studio-aqmph6k4.js +0 -5
  153. package/dist/chunks/studio-c9d3pxf7.js +0 -5
  154. package/dist/chunks/studio-cxt150pj.js +0 -7
  155. package/dist/chunks/studio-e6re3rk1.js +0 -5
  156. package/dist/chunks/studio-frb0qbf7.js +0 -6
  157. package/dist/chunks/studio-gh97gngt.js +0 -5
  158. package/dist/chunks/studio-nbxqcgvf.js +0 -5
  159. package/dist/chunks/studio-t1eedsyx.js +0 -4
  160. package/dist/chunks/studio-vavmwshr.js +0 -5
  161. package/dist/chunks/studio-xk0z6msk.js +0 -5
  162. package/dist/chunks/workspace-b69mgm0e.js +0 -4
@@ -48,6 +48,7 @@ import type {
48
48
  ReferencesResult,
49
49
  RenameResult,
50
50
  RepoInfo,
51
+ RepoProjects,
51
52
  StarterInfo,
52
53
  StudioPlatform,
53
54
  } from "../types";
@@ -56,12 +57,21 @@ import type { ReadFilesRequest, ReadFilesResult, UploadResult } from "@jxsuite/p
56
57
  import { reportActionRequired } from "../account/action-required";
57
58
  import { offeredActions, retryHint } from "../platform-errors";
58
59
  import { createReadBatcher } from "./read-batcher";
60
+ import { normalizeProjectDir } from "../utils/project-dir";
59
61
  import type { BatchAnswer } from "./read-batcher";
60
62
 
63
+ // Re-exported for the cloud shell and its tests, which spell folders through this entry.
64
+ export { normalizeProjectDir } from "../utils/project-dir";
65
+
61
66
  export interface CloudProject {
62
67
  owner: string;
63
68
  repo: string;
64
69
  branch: string;
70
+ /**
71
+ * The folder holding the project's `project.json`, relative to the repository root and already
72
+ * normalized ({@link normalizeProjectDir}); absent or `""` when the project IS the repository.
73
+ */
74
+ dir?: string;
65
75
  }
66
76
 
67
77
  interface ProjectInfoWire {
@@ -213,12 +223,45 @@ async function reportRefusal(res: Response): Promise<void> {
213
223
  });
214
224
  }
215
225
 
216
- /** Editor URL for a project session (mirrors the shell's route). */
226
+ /**
227
+ * The revision a session reads: the branch, then `:` and the project folder when there is one.
228
+ *
229
+ * Git's own `<rev>:<path>` notation, and unambiguous for the reason git can use it: `:` is one of
230
+ * the characters a ref name may never contain (git-check-ref-format), so the FIRST colon always
231
+ * ends the branch, however many slashes the branch or the folder holds.
232
+ */
233
+ export function revisionSpec(project: CloudProject): string {
234
+ return project.dir ? `${project.branch}:${project.dir}` : project.branch;
235
+ }
236
+
237
+ /** Split a {@link revisionSpec} back into its branch and folder; null when either half is unusable. */
238
+ function parseRevisionSpec(spec: string): { branch: string; dir?: string } | null {
239
+ const colon = spec.indexOf(":");
240
+ const branch = colon === -1 ? spec : spec.slice(0, colon);
241
+ const dir = colon === -1 ? "" : normalizeProjectDir(spec.slice(colon + 1));
242
+ if (!branch || dir === null) {
243
+ return null;
244
+ }
245
+ return dir ? { branch, dir } : { branch };
246
+ }
247
+
248
+ /**
249
+ * Editor URL for a project session (mirrors the shell's route):
250
+ * `/edit/<owner>/<repo>@<branch>[:<folder>]`. The branch is one encoded component, so its slashes
251
+ * travel as `%2F`; the folder keeps its slashes, so a subfolder project's address reads as the path
252
+ * it is.
253
+ */
217
254
  export function editUrl(project: CloudProject): string {
218
- return `/edit/${project.owner}/${project.repo}@${encodeURIComponent(project.branch)}`;
255
+ const folder = project.dir
256
+ ? `:${project.dir
257
+ .split("/")
258
+ .map((segment) => encodeURIComponent(segment))
259
+ .join("/")}`
260
+ : "";
261
+ return `/edit/${project.owner}/${project.repo}@${encodeURIComponent(project.branch)}${folder}`;
219
262
  }
220
263
 
221
- /** Parse an /edit/:owner/:repo@:branch path (editUrl's inverse); null when it is not one. */
264
+ /** Parse an /edit/:owner/:repo@:branch[:dir] path (editUrl's inverse); null when it is not one. */
222
265
  export function parseEditPath(pathname: string): CloudProject | null {
223
266
  /* Asset routers may normalize "@" to "%40", so decode the whole path first
224
267
  (branch slashes survive: `.+` spans them). */
@@ -232,22 +275,27 @@ export function parseEditPath(pathname: string): CloudProject | null {
232
275
  if (!match) {
233
276
  return null;
234
277
  }
235
- const [, owner, repo, branch] = match;
236
- if (!owner || !repo || !branch) {
278
+ const [, owner, repo, spec] = match;
279
+ const revision = spec ? parseRevisionSpec(spec) : null;
280
+ if (!owner || !repo || !revision) {
237
281
  return null;
238
282
  }
239
- return { owner, repo, branch };
283
+ return { owner, repo, ...revision };
240
284
  }
241
285
 
242
- /** Gateway base path for a project session. */
286
+ /**
287
+ * Gateway base path for a project session. The revision spec — branch and folder — is ONE encoded
288
+ * segment, so the gateway's route shape is the same for a subfolder project as for a repository,
289
+ * and the folder's slashes cannot be mistaken for the session's own sub-path.
290
+ */
243
291
  export function sessionBase(project: CloudProject): string {
244
- const { owner, repo, branch } = project;
245
- return `/api/v1/p/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/${encodeURIComponent(branch)}/studio`;
292
+ const { owner, repo } = project;
293
+ return `/api/v1/p/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/${encodeURIComponent(revisionSpec(project))}/studio`;
246
294
  }
247
295
 
248
- /** Catalogue/recents root key for a project: "owner/repo@branch". */
296
+ /** Catalogue/recents root key for a project: "owner/repo@branch", plus ":dir" for a subfolder. */
249
297
  export function projectRootKey(project: CloudProject): string {
250
- return `${project.owner}/${project.repo}@${project.branch}`;
298
+ return `${project.owner}/${project.repo}@${revisionSpec(project)}`;
251
299
  }
252
300
 
253
301
  /**
@@ -559,17 +607,18 @@ function awaitAction(popup: Window): Promise<ActionOutcome> {
559
607
  });
560
608
  }
561
609
 
562
- /** Parse an "owner/repo@branch" root key; null when malformed. */
610
+ /** Parse an "owner/repo@branch[:dir]" root key; null when malformed. */
563
611
  export function parseRootKey(root: string): CloudProject | null {
564
612
  const match = /^([^/@]+)\/([^/@]+)@(.+)$/.exec(root);
565
613
  if (!match) {
566
614
  return null;
567
615
  }
568
- const [, owner, repo, branch] = match;
569
- if (!owner || !repo || !branch) {
616
+ const [, owner, repo, spec] = match;
617
+ const revision = spec ? parseRevisionSpec(spec) : null;
618
+ if (!owner || !repo || !revision) {
570
619
  return null;
571
620
  }
572
- return { owner, repo, branch };
621
+ return { owner, repo, ...revision };
573
622
  }
574
623
 
575
624
  /** Every project this account can open, straight off the wire; [] when the catalogue is unreachable. */
@@ -1558,13 +1607,40 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
1558
1607
  }));
1559
1608
  },
1560
1609
 
1561
- /** Adopt an existing repo as a Jx project; resolves to its catalogue root key. */
1562
- async importProject(opts: { owner: string; name: string }) {
1610
+ /**
1611
+ * The folders of a repository that hold a project.json, read from its default branch. The
1612
+ * platform drops vendored folders and other tools' `project.json` (Nx's), and says when the
1613
+ * repository was too large to list in full.
1614
+ */
1615
+ async listRepoProjects(repo: { owner: string; name: string }): Promise<RepoProjects> {
1616
+ const res = await fetch(
1617
+ `/api/v1/repos/${encodeURIComponent(repo.owner)}/${encodeURIComponent(repo.name)}/projects`,
1618
+ { credentials: "include" },
1619
+ );
1620
+ const found = await okJson<RepoProjects>(res, "Failed to look for projects");
1621
+ /* Canonical on the way in, so a location the reader picks is spelled the way the root key,
1622
+ the editor URL and the session will spell it; anything that climbs out is not offered. */
1623
+ const locations: RepoProjects["locations"] = [];
1624
+ for (const location of found.locations) {
1625
+ const dir = normalizeProjectDir(location.dir);
1626
+ if (dir !== null) {
1627
+ locations.push(location.name ? { dir, name: location.name } : { dir });
1628
+ }
1629
+ }
1630
+ return { branch: found.branch, locations, truncated: found.truncated === true };
1631
+ },
1632
+
1633
+ /** Adopt an existing repo (or one folder of it) as a Jx project; resolves to its root key. */
1634
+ async importProject(opts: { owner: string; name: string; dir?: string }) {
1635
+ const dir = normalizeProjectDir(opts.dir ?? "");
1636
+ if (dir === null) {
1637
+ throw new Error("The project folder must stay inside the repository.");
1638
+ }
1563
1639
  const res = await fetch("/api/v1/projects/import", {
1564
1640
  method: "POST",
1565
1641
  credentials: "include",
1566
1642
  headers: { "Content-Type": "application/json" },
1567
- body: JSON.stringify(opts),
1643
+ body: JSON.stringify({ owner: opts.owner, name: opts.name, ...(dir ? { dir } : {}) }),
1568
1644
  });
1569
1645
  const imported = await okJson<{ owner: string; name: string; defaultBranch: string }>(
1570
1646
  res,
@@ -1575,6 +1651,7 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
1575
1651
  owner: imported.owner,
1576
1652
  repo: imported.name,
1577
1653
  branch: imported.defaultBranch,
1654
+ ...(dir ? { dir } : {}),
1578
1655
  }),
1579
1656
  };
1580
1657
  },
@@ -36,6 +36,12 @@ export interface ConnectOptions {
36
36
  productionBranch: string;
37
37
  owner: string;
38
38
  repo: string;
39
+ /**
40
+ * The folder of the repository the project lives in, when it is not the root (a cloud project
41
+ * opened from a subfolder). Pages builds there, so `bunx jx build` finds that project's
42
+ * `project.json` and `dist` is that project's output.
43
+ */
44
+ rootDir?: string;
39
45
  }
40
46
 
41
47
  /** True when the active platform can reach the Cloudflare API. */
@@ -106,6 +112,7 @@ export async function createPagesProject(opts: ConnectOptions): Promise<PagesPro
106
112
  build_config: {
107
113
  build_command: "bunx jx build",
108
114
  destination_dir: "dist",
115
+ ...(opts.rootDir ? { root_dir: opts.rootDir } : {}),
109
116
  },
110
117
  source: {
111
118
  type: "github",
@@ -19,12 +19,12 @@
19
19
  * HTML, in the accessibility tree and in every DOM dump. It is gone, and the document is now the
20
20
  * thing that makes it impossible rather than the thing that remembers not to — the field binds no
21
21
  * `value` at all, and the draft lives in the adapter's closure until {@link saveToken} reads it. A
22
- * stored token is reported as *stored*, the field is only ever drawn for a REPLACEMENT the reader
22
+ * stored token is reported as _stored_, the field is only ever drawn for a REPLACEMENT the reader
23
23
  * asked for, and revoking lives where every other credential's does — Preferences › Accounts
24
24
  * (`studio.md` §15 rule 1: a surface never prints the secret it describes).
25
25
  *
26
- * @docs studio/publish/cloudflare
27
26
  * @license MIT
27
+ * @docs studio/publish/cloudflare
28
28
  */
29
29
 
30
30
  import type { DeployConfig, ProjectConfig } from "@jxsuite/schema/types";
@@ -36,6 +36,7 @@ import { resetModelCache } from "../services/ai-models";
36
36
  import { projectState } from "../store";
37
37
  import type { CfConnection } from "../types";
38
38
  import { layerHost } from "../ui/layers";
39
+ import { normalizeProjectDir } from "../utils/project-dir";
39
40
  import { openPublishSurface } from "../surfaces/publish";
40
41
  import type { PublishForm, PublishSurfaceHandle, PublishView } from "../surfaces/publish";
41
42
  import type { CfAccount, PagesDeploymentInfo } from "./pages-service";
@@ -82,6 +83,17 @@ function prefillRepo(): { owner: string; repo: string } {
82
83
  return match ? { owner: match[1]!, repo: match[2]! } : { owner: "", repo: "" };
83
84
  }
84
85
 
86
+ /**
87
+ * The repository folder a cloud project lives in ("owner/repo@branch:sites/blog"), or "" for one
88
+ * that is the whole repository and for every non-cloud root. Not a form field: the folder is the
89
+ * project's identity rather than a choice, and a Pages project that built anywhere else would build
90
+ * a different project, or none.
91
+ */
92
+ function projectFolder(): string {
93
+ const spec = /^[\w.-]+\/[\w.-]+@[^:]+:(.+)$/.exec(getPlatform().projectRoot)?.[1] ?? "";
94
+ return normalizeProjectDir(spec) ?? "";
95
+ }
96
+
85
97
  /** The connected Pages project, as the surface takes it: strings, never a config object. */
86
98
  function deployView(): { projectName: string; productionUrl: string } | null {
87
99
  const deploy: DeployConfig | undefined = currentDeploy();
@@ -257,12 +269,14 @@ async function submitConnect(): Promise<void> {
257
269
  _error = "";
258
270
  paint();
259
271
  try {
272
+ const rootDir = projectFolder();
260
273
  const deploy = await connectDeploy(config, {
261
274
  accountId: _form.accountId,
262
275
  owner: _form.owner,
263
276
  productionBranch: _form.branch || "main",
264
277
  projectName: _form.projectName,
265
278
  repo: _form.repo,
279
+ ...(rootDir ? { rootDir } : {}),
266
280
  });
267
281
  _deployment = await latestDeployment(deploy).catch(() => null);
268
282
  } catch (error) {