conductor-oss 0.20.2 → 0.20.3

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 (131) hide show
  1. package/README.md +17 -5
  2. package/package.json +5 -5
  3. package/web/.next/standalone/packages/web/.next/BUILD_ID +1 -1
  4. package/web/.next/standalone/packages/web/.next/build-manifest.json +2 -2
  5. package/web/.next/standalone/packages/web/.next/prerender-manifest.json +3 -3
  6. package/web/.next/standalone/packages/web/.next/server/app/_global-error.html +2 -2
  7. package/web/.next/standalone/packages/web/.next/server/app/_global-error.rsc +1 -1
  8. package/web/.next/standalone/packages/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  9. package/web/.next/standalone/packages/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/web/.next/standalone/packages/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  11. package/web/.next/standalone/packages/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  12. package/web/.next/standalone/packages/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/web/.next/standalone/packages/web/.next/server/app/_not-found/page/server-reference-manifest.json +7 -7
  14. package/web/.next/standalone/packages/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  15. package/web/.next/standalone/packages/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  16. package/web/.next/standalone/packages/web/.next/server/app/_not-found.html +1 -1
  17. package/web/.next/standalone/packages/web/.next/server/app/_not-found.rsc +4 -4
  18. package/web/.next/standalone/packages/web/.next/server/app/_not-found.segments/_full.segment.rsc +4 -4
  19. package/web/.next/standalone/packages/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  20. package/web/.next/standalone/packages/web/.next/server/app/_not-found.segments/_index.segment.rsc +4 -4
  21. package/web/.next/standalone/packages/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  22. package/web/.next/standalone/packages/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  23. package/web/.next/standalone/packages/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  24. package/web/.next/standalone/packages/web/.next/server/app/api/access/route.js.nft.json +1 -1
  25. package/web/.next/standalone/packages/web/.next/server/app/api/agents/route.js.nft.json +1 -1
  26. package/web/.next/standalone/packages/web/.next/server/app/api/app-update/route.js.nft.json +1 -1
  27. package/web/.next/standalone/packages/web/.next/server/app/api/attachments/route.js.nft.json +1 -1
  28. package/web/.next/standalone/packages/web/.next/server/app/api/auth/session/route.js.nft.json +1 -1
  29. package/web/.next/standalone/packages/web/.next/server/app/api/boards/comments/route.js.nft.json +1 -1
  30. package/web/.next/standalone/packages/web/.next/server/app/api/boards/route.js.nft.json +1 -1
  31. package/web/.next/standalone/packages/web/.next/server/app/api/config/route.js.nft.json +1 -1
  32. package/web/.next/standalone/packages/web/.next/server/app/api/context-files/open/route.js.nft.json +1 -1
  33. package/web/.next/standalone/packages/web/.next/server/app/api/context-files/route.js.nft.json +1 -1
  34. package/web/.next/standalone/packages/web/.next/server/app/api/events/route.js.nft.json +1 -1
  35. package/web/.next/standalone/packages/web/.next/server/app/api/executor/health/route.js.nft.json +1 -1
  36. package/web/.next/standalone/packages/web/.next/server/app/api/filesystem/directory/route.js.nft.json +1 -1
  37. package/web/.next/standalone/packages/web/.next/server/app/api/filesystem/pick-directory/route.js.nft.json +1 -1
  38. package/web/.next/standalone/packages/web/.next/server/app/api/github/repos/route.js.nft.json +1 -1
  39. package/web/.next/standalone/packages/web/.next/server/app/api/github/webhook/route.js.nft.json +1 -1
  40. package/web/.next/standalone/packages/web/.next/server/app/api/health/boards/route.js.nft.json +1 -1
  41. package/web/.next/standalone/packages/web/.next/server/app/api/health/sessions/route.js.nft.json +1 -1
  42. package/web/.next/standalone/packages/web/.next/server/app/api/notifications/route.js.nft.json +1 -1
  43. package/web/.next/standalone/packages/web/.next/server/app/api/preferences/route.js.nft.json +1 -1
  44. package/web/.next/standalone/packages/web/.next/server/app/api/remote-access/route.js.nft.json +1 -1
  45. package/web/.next/standalone/packages/web/.next/server/app/api/repositories/[id]/route.js.nft.json +1 -1
  46. package/web/.next/standalone/packages/web/.next/server/app/api/repositories/route.js.nft.json +1 -1
  47. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/actions/route.js.nft.json +1 -1
  48. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/archive/route.js.nft.json +1 -1
  49. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/checks/route.js.nft.json +1 -1
  50. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/diff/route.js.nft.json +1 -1
  51. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/feed/route.js.nft.json +1 -1
  52. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/feedback/route.js.nft.json +1 -1
  53. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/files/route.js.nft.json +1 -1
  54. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/interrupt/route.js.nft.json +1 -1
  55. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/kill/route.js.nft.json +1 -1
  56. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/output/route.js.nft.json +1 -1
  57. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/preview/dom/route.js.nft.json +1 -1
  58. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/preview/route.js.nft.json +1 -1
  59. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/preview/screenshot/route.js.nft.json +1 -1
  60. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/restore/route.js.nft.json +1 -1
  61. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -1
  62. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/terminal/snapshot/route.js.nft.json +1 -1
  63. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/terminal/token/route.js.nft.json +1 -1
  64. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/terminal/ttyd/route.js.nft.json +1 -1
  65. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/terminal/ttyd/ws/route.js.nft.json +1 -1
  66. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/route.js.nft.json +1 -1
  67. package/web/.next/standalone/packages/web/.next/server/app/api/spawn/route.js.nft.json +1 -1
  68. package/web/.next/standalone/packages/web/.next/server/app/api/workspaces/branches/route.js.nft.json +1 -1
  69. package/web/.next/standalone/packages/web/.next/server/app/api/workspaces/route.js.nft.json +1 -1
  70. package/web/.next/standalone/packages/web/.next/server/app/page/react-loadable-manifest.json +1 -1
  71. package/web/.next/standalone/packages/web/.next/server/app/page/server-reference-manifest.json +7 -7
  72. package/web/.next/standalone/packages/web/.next/server/app/page.js.nft.json +1 -1
  73. package/web/.next/standalone/packages/web/.next/server/app/page_client-reference-manifest.js +1 -1
  74. package/web/.next/standalone/packages/web/.next/server/app/sessions/[id]/page/server-reference-manifest.json +7 -7
  75. package/web/.next/standalone/packages/web/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
  76. package/web/.next/standalone/packages/web/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
  77. package/web/.next/standalone/packages/web/.next/server/app/sign-in/[[...sign-in]]/page/server-reference-manifest.json +7 -7
  78. package/web/.next/standalone/packages/web/.next/server/app/sign-in/[[...sign-in]]/page.js.nft.json +1 -1
  79. package/web/.next/standalone/packages/web/.next/server/app/sign-in/[[...sign-in]]/page_client-reference-manifest.js +1 -1
  80. package/web/.next/standalone/packages/web/.next/server/app/unlock/page/server-reference-manifest.json +7 -7
  81. package/web/.next/standalone/packages/web/.next/server/app/unlock/page.js.nft.json +1 -1
  82. package/web/.next/standalone/packages/web/.next/server/app/unlock/page_client-reference-manifest.js +1 -1
  83. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/[root-of-the-server]__53bcdbc9._.js +1 -1
  84. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/{[root-of-the-server]__2f682641._.js → [root-of-the-server]__6246c581._.js} +2 -2
  85. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/[root-of-the-server]__c405048c._.js +1 -1
  86. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/{[root-of-the-server]__25555995._.js → [root-of-the-server]__c8eede2d._.js} +1 -1
  87. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/{[root-of-the-server]__ed31820a._.js → [root-of-the-server]__ffd8c5ab._.js} +1 -1
  88. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/_0e1412de._.js +1 -1
  89. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/_532f707d._.js +1 -1
  90. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/_69e05fca._.js +1 -1
  91. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/_80efe193._.js +1 -1
  92. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/_c0f0e227._.js +1 -1
  93. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/{_dd3f8405._.js → _c91b376d._.js} +2 -2
  94. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/_f36ddaa9._.js +1 -1
  95. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/{node_modules_@clerk_nextjs_dist_esm_app-router_e4fbd9e4._.js → node_modules_@clerk_nextjs_dist_esm_app-router_47572fce._.js} +2 -2
  96. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/node_modules_@clerk_nextjs_dist_esm_app-router_8b33e86b._.js +3 -0
  97. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/packages_web_src_features_dashboard_components_DashboardDialogs_tsx_32d3d858._.js +1 -1
  98. package/web/.next/standalone/packages/web/.next/server/pages/404.html +1 -1
  99. package/web/.next/standalone/packages/web/.next/server/pages/500.html +2 -2
  100. package/web/.next/standalone/packages/web/.next/server/server-reference-manifest.js +1 -1
  101. package/web/.next/standalone/packages/web/.next/server/server-reference-manifest.json +8 -8
  102. package/web/.next/{static/chunks/414abf6a0538296f.js → standalone/packages/web/.next/static/chunks/05535bfd4a15736a.js} +1 -1
  103. package/web/.next/standalone/packages/web/.next/static/chunks/0afb9504dd37f5a5.js +1 -0
  104. package/web/.next/standalone/packages/web/.next/static/chunks/198b3e8333e97319.css +4 -0
  105. package/web/.next/standalone/packages/web/.next/static/chunks/4c33b5df1b80b1aa.js +1 -0
  106. package/web/.next/standalone/packages/web/.next/static/chunks/{b1c03c2c194b81b8.js → 8ab6375ce994ee71.js} +1 -1
  107. package/web/.next/standalone/packages/web/.next/static/chunks/{f6e6ac9d96cf2151.js → d49e89e2e6c97f48.js} +1 -1
  108. package/web/.next/standalone/packages/web/.next/static/chunks/{54fbc02b98d56f4c.js → e366fc88feb421d9.js} +1 -1
  109. package/web/.next/standalone/packages/web/src/features/dashboard/components/DashboardDialogs.tsx +554 -419
  110. package/web/.next/standalone/packages/web/src/features/dashboard/githubRepos.test.ts +40 -0
  111. package/web/.next/standalone/packages/web/src/features/dashboard/githubRepos.ts +27 -0
  112. package/web/.next/{standalone/packages/web/.next/static/chunks/414abf6a0538296f.js → static/chunks/05535bfd4a15736a.js} +1 -1
  113. package/web/.next/static/chunks/0afb9504dd37f5a5.js +1 -0
  114. package/web/.next/static/chunks/198b3e8333e97319.css +4 -0
  115. package/web/.next/static/chunks/4c33b5df1b80b1aa.js +1 -0
  116. package/web/.next/static/chunks/{b1c03c2c194b81b8.js → 8ab6375ce994ee71.js} +1 -1
  117. package/web/.next/static/chunks/{f6e6ac9d96cf2151.js → d49e89e2e6c97f48.js} +1 -1
  118. package/web/.next/static/chunks/{54fbc02b98d56f4c.js → e366fc88feb421d9.js} +1 -1
  119. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/node_modules_@clerk_nextjs_dist_esm_app-router_c2a45392._.js +0 -3
  120. package/web/.next/standalone/packages/web/.next/static/chunks/4f912a60e3753a1f.css +0 -4
  121. package/web/.next/standalone/packages/web/.next/static/chunks/953e23f8cfbdc9bb.js +0 -1
  122. package/web/.next/standalone/packages/web/.next/static/chunks/9b1065a7ccd69a3e.js +0 -1
  123. package/web/.next/static/chunks/4f912a60e3753a1f.css +0 -4
  124. package/web/.next/static/chunks/953e23f8cfbdc9bb.js +0 -1
  125. package/web/.next/static/chunks/9b1065a7ccd69a3e.js +0 -1
  126. /package/web/.next/standalone/packages/web/.next/static/{4j_KriaHEW-_LlfrKXaOW → HfIcB-Vnu7gb6quPqshK5}/_buildManifest.js +0 -0
  127. /package/web/.next/standalone/packages/web/.next/static/{4j_KriaHEW-_LlfrKXaOW → HfIcB-Vnu7gb6quPqshK5}/_clientMiddlewareManifest.json +0 -0
  128. /package/web/.next/standalone/packages/web/.next/static/{4j_KriaHEW-_LlfrKXaOW → HfIcB-Vnu7gb6quPqshK5}/_ssgManifest.js +0 -0
  129. /package/web/.next/static/{4j_KriaHEW-_LlfrKXaOW → HfIcB-Vnu7gb6quPqshK5}/_buildManifest.js +0 -0
  130. /package/web/.next/static/{4j_KriaHEW-_LlfrKXaOW → HfIcB-Vnu7gb6quPqshK5}/_clientMiddlewareManifest.json +0 -0
  131. /package/web/.next/static/{4j_KriaHEW-_LlfrKXaOW → HfIcB-Vnu7gb6quPqshK5}/_ssgManifest.js +0 -0
@@ -45,6 +45,7 @@ import { normalizeAgentName } from "@/lib/agentUtils";
45
45
  import { getKnownAgent, KNOWN_AGENT_ORDER } from "@/lib/knownAgents";
46
46
  import { AgentTileIcon } from "@/components/AgentTileIcon";
47
47
  import { playNotificationSound } from "@/lib/notificationSounds";
48
+ import { filterGitHubRepos, type GitHubRepo } from "../githubRepos";
48
49
  import { normalizeModelAccessPreferences } from "@/lib/modelAccess";
49
50
  import {
50
51
  getRuntimeCatalogDefaultModelForAccess,
@@ -204,19 +205,6 @@ type LinkedBoardResponse = {
204
205
  }>;
205
206
  };
206
207
 
207
- type GitHubRepo = {
208
- name: string;
209
- fullName: string;
210
- httpsUrl: string;
211
- sshUrl: string;
212
- defaultBranch: string;
213
- private: boolean;
214
- description?: string | null;
215
- updatedAt?: string | null;
216
- ownerLogin?: string | null;
217
- permission?: string | null;
218
- };
219
-
220
208
  type DirectoryEntry = {
221
209
  name: string;
222
210
  path: string;
@@ -1023,10 +1011,12 @@ export function NewWorkspaceDialog({
1023
1011
  agentOptions: string[];
1024
1012
  }) {
1025
1013
  const [mode, setMode] = useState<"git" | "local">("git");
1014
+ const [step, setStep] = useState<"source" | "details">("source");
1026
1015
  const [projectId, setProjectId] = useState("");
1027
1016
  const [projectIdTouched, setProjectIdTouched] = useState(false);
1028
1017
  const [gitUrl, setGitUrl] = useState("");
1029
- const [path, setPath] = useState("");
1018
+ const [localPath, setLocalPath] = useState("");
1019
+ const [clonePath, setClonePath] = useState("");
1030
1020
  const [defaultBranch, setDefaultBranch] = useState("main");
1031
1021
  const [agent, setAgent] = useState(defaultAgent);
1032
1022
  const [useWorktree, setUseWorktree] = useState(false);
@@ -1046,10 +1036,12 @@ export function NewWorkspaceDialog({
1046
1036
  useEffect(() => {
1047
1037
  if (!open) return;
1048
1038
  setMode("git");
1039
+ setStep("source");
1049
1040
  setProjectId("");
1050
1041
  setProjectIdTouched(false);
1051
1042
  setGitUrl("");
1052
- setPath("");
1043
+ setLocalPath("");
1044
+ setClonePath("");
1053
1045
  setDefaultBranch("main");
1054
1046
  setInitializeGit(true);
1055
1047
  setUseWorktree(false);
@@ -1068,22 +1060,14 @@ export function NewWorkspaceDialog({
1068
1060
 
1069
1061
  useEffect(() => {
1070
1062
  if (!open) return;
1071
- if (mode !== "local") return;
1072
- if (path.trim().length > 0) return;
1073
- setFolderPickerTarget("local");
1074
- setFolderPickerOpen(true);
1075
- }, [mode, open, path]);
1063
+ const sourceReady = mode === "git" ? gitUrl.trim().length > 0 : localPath.trim().length > 0;
1064
+ if (step === "details" && !sourceReady) {
1065
+ setStep("source");
1066
+ }
1067
+ }, [gitUrl, localPath, mode, open, step]);
1076
1068
 
1077
1069
  const filteredGitHubRepos = useMemo(() => {
1078
- const query = githubRepoSearch.trim().toLowerCase();
1079
- const filtered = query.length === 0 ? githubRepos : githubRepos.filter((repo) => {
1080
- return repo.fullName.toLowerCase().includes(query)
1081
- || repo.name.toLowerCase().includes(query)
1082
- || (repo.ownerLogin ?? "").toLowerCase().includes(query)
1083
- || (repo.description ?? "").toLowerCase().includes(query)
1084
- || repo.defaultBranch.toLowerCase().includes(query);
1085
- });
1086
- return query.length === 0 ? filtered.slice(0, 10) : filtered.slice(0, 14);
1070
+ return filterGitHubRepos(githubRepos, githubRepoSearch);
1087
1071
  }, [githubRepoSearch, githubRepos]);
1088
1072
 
1089
1073
  const selectedGitHubRepoData = useMemo(() => {
@@ -1110,6 +1094,21 @@ export function NewWorkspaceDialog({
1110
1094
  if (!normalizedUrl) return false;
1111
1095
  return normalizedUrl.toLowerCase() !== gitUrl.trim().toLowerCase();
1112
1096
  }, [gitUrl, normalizedGitHubSearchUrl, normalizedManualGitUrl]);
1097
+ const sourceReady = mode === "git"
1098
+ ? gitUrl.trim().length > 0
1099
+ : localPath.trim().length > 0;
1100
+ const selectedSourceTitle = useMemo(() => {
1101
+ if (mode === "git") {
1102
+ return selectedGitHubRepoData?.fullName ?? gitUrl.trim();
1103
+ }
1104
+ return extractNameFromPath(localPath) ?? localPath.trim();
1105
+ }, [gitUrl, localPath, mode, selectedGitHubRepoData]);
1106
+ const selectedSourceSubtitle = useMemo(() => {
1107
+ if (mode === "git") {
1108
+ return selectedGitHubRepoData?.description?.trim() || gitUrl.trim();
1109
+ }
1110
+ return localPath.trim();
1111
+ }, [gitUrl, localPath, mode, selectedGitHubRepoData]);
1113
1112
 
1114
1113
  const orderedAgentOptions = useMemo(() => {
1115
1114
  const opts = [...new Set(agentOptions)];
@@ -1166,7 +1165,7 @@ export function NewWorkspaceDialog({
1166
1165
  sourceOverride?: { gitUrl?: string; path?: string },
1167
1166
  ) => {
1168
1167
  const effectiveGitUrl = sourceOverride?.gitUrl ?? (mode === "git" ? gitUrl.trim() : "");
1169
- const effectivePath = sourceOverride?.path ?? (mode === "local" ? path.trim() : "");
1168
+ const effectivePath = sourceOverride?.path ?? (mode === "local" ? localPath.trim() : "");
1170
1169
 
1171
1170
  if (effectiveGitUrl.length === 0 && effectivePath.length === 0) {
1172
1171
  setBranchesError(
@@ -1231,6 +1230,7 @@ export function NewWorkspaceDialog({
1231
1230
  }
1232
1231
 
1233
1232
  await handleDetectBranches({ gitUrl: selected.httpsUrl });
1233
+ setStep("details");
1234
1234
  };
1235
1235
 
1236
1236
  const handleUseSearchValueAsRepository = async () => {
@@ -1256,6 +1256,7 @@ export function NewWorkspaceDialog({
1256
1256
  }
1257
1257
 
1258
1258
  await handleDetectBranches({ gitUrl: normalizedUrl });
1259
+ setStep("details");
1259
1260
  };
1260
1261
 
1261
1262
  const openFolderPicker = (target: "clone" | "local") => {
@@ -1263,15 +1264,25 @@ export function NewWorkspaceDialog({
1263
1264
  setFolderPickerOpen(true);
1264
1265
  };
1265
1266
 
1267
+ const handleModeChange = (nextMode: "git" | "local") => {
1268
+ setMode(nextMode);
1269
+ setStep("source");
1270
+ setBranchOptions([]);
1271
+ setBranchesError(null);
1272
+ };
1273
+
1274
+ const handleContinueToDetails = () => {
1275
+ if (!sourceReady || creating) return;
1276
+ setStep("details");
1277
+ };
1278
+
1266
1279
  if (!open) return null;
1267
1280
 
1268
- const canSubmit = mode === "git"
1269
- ? gitUrl.trim().length > 0 && defaultBranch.trim().length > 0
1270
- : path.trim().length > 0 && defaultBranch.trim().length > 0;
1281
+ const canSubmit = sourceReady && defaultBranch.trim().length > 0;
1271
1282
 
1272
1283
  async function handleSubmit(event: FormEvent<HTMLFormElement>) {
1273
1284
  event.preventDefault();
1274
- if (!canSubmit || creating) return;
1285
+ if (step !== "details" || !canSubmit || creating) return;
1275
1286
 
1276
1287
  const payload: NewWorkspacePayload =
1277
1288
  mode === "git"
@@ -1282,7 +1293,7 @@ export function NewWorkspaceDialog({
1282
1293
  defaultBranch: defaultBranch.trim(),
1283
1294
  useWorktree,
1284
1295
  gitUrl: gitUrl.trim(),
1285
- path: path.trim() || undefined,
1296
+ path: clonePath.trim() || undefined,
1286
1297
  }
1287
1298
  : {
1288
1299
  mode,
@@ -1290,7 +1301,7 @@ export function NewWorkspaceDialog({
1290
1301
  agent,
1291
1302
  defaultBranch: defaultBranch.trim(),
1292
1303
  useWorktree,
1293
- path: path.trim(),
1304
+ path: localPath.trim(),
1294
1305
  initializeGit,
1295
1306
  };
1296
1307
 
@@ -1300,7 +1311,7 @@ export function NewWorkspaceDialog({
1300
1311
  return (
1301
1312
  <>
1302
1313
  <div
1303
- className="fixed inset-0 z-[80] flex items-start justify-center overflow-y-auto bg-black/65 px-3 py-3 sm:items-center sm:py-0"
1314
+ className="fixed inset-0 z-[80] flex items-end justify-center overflow-y-auto bg-black/65 px-0 py-0 sm:items-center sm:px-3 sm:py-3"
1304
1315
  onClick={() => {
1305
1316
  if (creating || folderPickerOpen) return;
1306
1317
  onClose();
@@ -1310,448 +1321,569 @@ export function NewWorkspaceDialog({
1310
1321
  <form
1311
1322
  onSubmit={handleSubmit}
1312
1323
  onClick={(event) => event.stopPropagation()}
1313
- className="flex max-h-[calc(100dvh-1.5rem)] w-full max-w-[760px] flex-col overflow-hidden rounded-[6px] border border-[var(--vk-border)] bg-[var(--vk-bg-panel)] shadow-[0_24px_80px_rgba(0,0,0,0.55)]"
1324
+ className="flex h-dvh w-full max-w-none flex-col overflow-hidden rounded-none border-x-0 border-b-0 border-t border-[var(--vk-border)] bg-[var(--vk-bg-panel)] shadow-[0_24px_80px_rgba(0,0,0,0.55)] sm:h-auto sm:max-h-[calc(100dvh-3rem)] sm:max-w-[860px] sm:rounded-[10px] sm:border"
1314
1325
  >
1315
- <header className="flex items-center border-b border-[var(--vk-border)] px-4 py-3">
1316
- <div>
1317
- <h2 className="text-[18px] leading-[22px] text-[var(--vk-text-strong)]">Add Workspace</h2>
1318
- <p className="pt-1 text-[12px] text-[var(--vk-text-muted)]">
1319
- Pick a GitHub repository or local folder, then confirm the branch.
1320
- </p>
1326
+ <header className="border-b border-[var(--vk-border)] bg-[color:color-mix(in_srgb,var(--vk-bg-panel)_92%,transparent)] px-4 py-3 backdrop-blur">
1327
+ <div className="mb-3 flex justify-center sm:hidden">
1328
+ <span className="h-1 w-10 rounded-full bg-[color:color-mix(in_srgb,var(--vk-text-muted)_36%,transparent)]" />
1321
1329
  </div>
1322
- <button
1323
- type="button"
1324
- onClick={onClose}
1325
- disabled={creating}
1326
- aria-label="Close dialog"
1327
- className="ml-auto inline-flex h-8 w-8 items-center justify-center rounded-[4px] text-[var(--vk-text-muted)] hover:bg-[var(--vk-bg-hover)] disabled:opacity-50"
1328
- >
1329
- <X className="h-4 w-4" />
1330
- </button>
1331
- </header>
1332
-
1333
- <div className="min-h-0 flex-1 space-y-4 overflow-y-auto px-4 py-4">
1334
- <div className="inline-flex rounded-[4px] border border-[var(--vk-border)] p-1">
1335
- <button
1336
- type="button"
1337
- onClick={() => setMode("git")}
1338
- className={`rounded-[3px] px-3 py-1.5 text-[13px] ${
1339
- mode === "git"
1340
- ? "bg-[var(--vk-bg-active)] text-[var(--vk-text-strong)]"
1341
- : "text-[var(--vk-text-muted)] hover:bg-[var(--vk-bg-hover)]"
1342
- }`}
1343
- >
1344
- GitHub
1345
- </button>
1330
+ <div className="flex items-start gap-3">
1331
+ <div className="min-w-0 flex-1">
1332
+ <div className="flex flex-wrap items-center gap-2">
1333
+ <h2 className="text-[18px] leading-[22px] text-[var(--vk-text-strong)]">Add Workspace</h2>
1334
+ <span className="inline-flex rounded-full border border-[var(--vk-border)] px-2 py-0.5 text-[11px] text-[var(--vk-text-muted)]">
1335
+ {step === "source" ? "Step 1 of 2" : "Step 2 of 2"}
1336
+ </span>
1337
+ </div>
1338
+ <p className="pt-1 text-[12px] text-[var(--vk-text-muted)]">
1339
+ {step === "source"
1340
+ ? "Start with the repository or folder you want Conductor to manage."
1341
+ : "Confirm the defaults Conductor should use when this workspace is created."}
1342
+ </p>
1343
+ <div className="mt-3 flex flex-wrap items-center gap-2">
1344
+ {[
1345
+ { id: "source", label: mode === "git" ? "Repository" : "Folder" },
1346
+ { id: "details", label: "Defaults" },
1347
+ ].map((item) => {
1348
+ const active = step === item.id;
1349
+ const complete = item.id === "source" && step === "details" && sourceReady;
1350
+ return (
1351
+ <div
1352
+ key={item.id}
1353
+ className={`inline-flex items-center gap-2 rounded-full border px-3 py-1 text-[11px] ${
1354
+ active
1355
+ ? "border-[var(--vk-orange)] bg-[color:color-mix(in_srgb,var(--vk-orange)_12%,transparent)] text-[var(--vk-text-strong)]"
1356
+ : "border-[var(--vk-border)] text-[var(--vk-text-muted)]"
1357
+ }`}
1358
+ >
1359
+ <span className={`inline-flex h-4 w-4 items-center justify-center rounded-full border text-[10px] ${
1360
+ complete
1361
+ ? "border-[var(--vk-orange)] bg-[var(--vk-orange)] text-black"
1362
+ : active
1363
+ ? "border-[var(--vk-orange)] text-[var(--vk-text-strong)]"
1364
+ : "border-[var(--vk-border)]"
1365
+ }`}>
1366
+ {complete ? <Check className="h-3 w-3" /> : item.id === "source" ? "1" : "2"}
1367
+ </span>
1368
+ <span>{item.label}</span>
1369
+ </div>
1370
+ );
1371
+ })}
1372
+ </div>
1373
+ </div>
1346
1374
  <button
1347
1375
  type="button"
1348
- onClick={() => setMode("local")}
1349
- className={`rounded-[3px] px-3 py-1.5 text-[13px] ${
1350
- mode === "local"
1351
- ? "bg-[var(--vk-bg-active)] text-[var(--vk-text-strong)]"
1352
- : "text-[var(--vk-text-muted)] hover:bg-[var(--vk-bg-hover)]"
1353
- }`}
1376
+ onClick={onClose}
1377
+ disabled={creating}
1378
+ aria-label="Close dialog"
1379
+ className="inline-flex h-8 w-8 items-center justify-center rounded-[4px] text-[var(--vk-text-muted)] hover:bg-[var(--vk-bg-hover)] disabled:opacity-50"
1354
1380
  >
1355
- Local Folder
1381
+ <X className="h-4 w-4" />
1356
1382
  </button>
1357
1383
  </div>
1384
+ </header>
1358
1385
 
1359
- {mode === "git" ? (
1360
- <>
1361
- <div className="rounded-[6px] border border-[var(--vk-border)] bg-[var(--vk-bg-main)] p-3">
1362
- <div className="flex items-start gap-3">
1363
- <span className="inline-flex h-9 w-9 items-center justify-center rounded-[5px] border border-[var(--vk-border)] bg-[var(--vk-bg-panel)] text-[var(--vk-text-strong)]">
1386
+ <div className="min-h-0 flex-1 overflow-y-auto px-4 py-4 sm:px-5 sm:py-5">
1387
+ {step === "source" ? (
1388
+ <div className="space-y-4">
1389
+ <div className="grid gap-2 sm:grid-cols-2">
1390
+ <button
1391
+ type="button"
1392
+ onClick={() => handleModeChange("git")}
1393
+ className={`rounded-[10px] border px-4 py-4 text-left transition ${
1394
+ mode === "git"
1395
+ ? "border-[var(--vk-orange)] bg-[color:color-mix(in_srgb,var(--vk-orange)_12%,transparent)]"
1396
+ : "border-[var(--vk-border)] bg-[var(--vk-bg-main)] hover:bg-[var(--vk-bg-hover)]"
1397
+ }`}
1398
+ >
1399
+ <span className="inline-flex h-10 w-10 items-center justify-center rounded-[8px] border border-[var(--vk-border)] bg-[var(--vk-bg-panel)] text-[var(--vk-text-strong)]">
1364
1400
  <MarkGithubIcon className="h-[18px] w-[18px]" />
1365
1401
  </span>
1366
- <div className="min-w-0 flex-1">
1367
- <p className="text-[13px] font-medium text-[var(--vk-text-strong)]">GitHub Repository</p>
1368
- <p className="pt-0.5 text-[12px] text-[var(--vk-text-muted)]">
1369
- Search accessible repositories or paste a repository URL. Conductor fills the branch and clone URL after selection.
1370
- </p>
1371
- </div>
1372
- <button
1373
- type="button"
1374
- onClick={() => {
1375
- setGithubReposLoaded(false);
1376
- void handleFetchGitHubRepos(true);
1377
- }}
1378
- disabled={githubReposLoading}
1379
- className="inline-flex h-8 items-center rounded-[4px] border border-[var(--vk-border)] px-2 text-[12px] text-[var(--vk-text-normal)] hover:bg-[var(--vk-bg-hover)] disabled:opacity-50"
1380
- >
1381
- {githubReposLoading ? (
1382
- <Loader2 className="h-3.5 w-3.5 animate-spin" />
1383
- ) : (
1384
- <>
1385
- <RefreshCcw className="mr-1.5 h-3.5 w-3.5" />
1386
- Refresh
1387
- </>
1388
- )}
1389
- </button>
1390
- </div>
1402
+ <p className="mt-3 text-[14px] font-medium text-[var(--vk-text-strong)]">GitHub repository</p>
1403
+ <p className="mt-1 text-[12px] leading-5 text-[var(--vk-text-muted)]">
1404
+ Search repos you already have access to, or paste a GitHub URL directly.
1405
+ </p>
1406
+ </button>
1391
1407
 
1392
- <div className="relative mt-3">
1393
- <MarkGithubIcon className="pointer-events-none absolute left-3 top-1/2 h-[16px] w-[16px] -translate-y-1/2 text-[var(--vk-text-muted)]" />
1394
- <input
1395
- value={githubRepoSearch}
1396
- onChange={(event) => setGithubRepoSearch(event.target.value)}
1397
- onKeyDown={(event) => {
1398
- if (event.key === "Enter" && showUseSearchValueAction) {
1399
- event.preventDefault();
1400
- void handleUseSearchValueAsRepository();
1401
- }
1402
- }}
1403
- placeholder="Search GitHub repos or paste a repository URL"
1404
- className="h-10 w-full rounded-[5px] border border-[var(--vk-border)] bg-transparent pl-10 pr-3 text-[14px] text-[var(--vk-text-normal)] outline-none focus:border-[var(--vk-orange)]"
1405
- />
1406
- </div>
1408
+ <button
1409
+ type="button"
1410
+ onClick={() => handleModeChange("local")}
1411
+ className={`rounded-[10px] border px-4 py-4 text-left transition ${
1412
+ mode === "local"
1413
+ ? "border-[var(--vk-orange)] bg-[color:color-mix(in_srgb,var(--vk-orange)_12%,transparent)]"
1414
+ : "border-[var(--vk-border)] bg-[var(--vk-bg-main)] hover:bg-[var(--vk-bg-hover)]"
1415
+ }`}
1416
+ >
1417
+ <span className="inline-flex h-10 w-10 items-center justify-center rounded-[8px] border border-[var(--vk-border)] bg-[var(--vk-bg-panel)] text-[var(--vk-text-strong)]">
1418
+ <FolderOpen className="h-5 w-5" />
1419
+ </span>
1420
+ <p className="mt-3 text-[14px] font-medium text-[var(--vk-text-strong)]">Local folder</p>
1421
+ <p className="mt-1 text-[12px] leading-5 text-[var(--vk-text-muted)]">
1422
+ Point Conductor at a repository already on this machine.
1423
+ </p>
1424
+ </button>
1425
+ </div>
1407
1426
 
1408
- {showUseSearchValueAction ? (
1409
- <button
1410
- type="button"
1411
- onClick={() => {
1412
- void handleUseSearchValueAsRepository();
1413
- }}
1414
- className="mt-3 inline-flex items-center gap-2 rounded-[5px] border border-[var(--vk-border)] bg-[var(--vk-bg-panel)] px-3 py-2 text-[12px] text-[var(--vk-text-normal)] hover:bg-[var(--vk-bg-hover)]"
1415
- >
1416
- <RepoIcon className="h-4 w-4 text-[var(--vk-text-strong)]" />
1417
- <span className="truncate">
1418
- Use {normalizedGitHubSearchUrl ? "this GitHub repository" : "pasted repository URL"}
1427
+ {mode === "git" ? (
1428
+ <div className="space-y-3 rounded-[10px] border border-[var(--vk-border)] bg-[var(--vk-bg-main)] p-3 sm:p-4">
1429
+ <div className="flex items-start gap-3">
1430
+ <span className="inline-flex h-10 w-10 items-center justify-center rounded-[8px] border border-[var(--vk-border)] bg-[var(--vk-bg-panel)] text-[var(--vk-text-strong)]">
1431
+ <RepoIcon className="h-4 w-4" />
1419
1432
  </span>
1420
- </button>
1421
- ) : null}
1422
-
1423
- {githubReposLoading && githubRepos.length === 0 ? (
1424
- <div className="mt-3 rounded-[5px] border border-[var(--vk-border)] px-3 py-3 text-[12px] text-[var(--vk-text-muted)]">
1425
- Loading accessible GitHub repositories...
1426
- </div>
1427
- ) : null}
1428
-
1429
- {githubReposError ? (
1430
- <div className="mt-3 rounded-[5px] border border-[var(--vk-red)]/40 bg-[var(--vk-bg-panel)] px-3 py-3 text-[12px] text-[var(--vk-red)]">
1431
- <p>{githubReposError}</p>
1433
+ <div className="min-w-0 flex-1">
1434
+ <p className="text-[14px] font-medium text-[var(--vk-text-strong)]">Choose repository</p>
1435
+ <p className="pt-0.5 text-[12px] leading-5 text-[var(--vk-text-muted)]">
1436
+ Select a repo first. Branch, workspace name, and runtime defaults come next.
1437
+ </p>
1438
+ </div>
1432
1439
  <button
1433
1440
  type="button"
1434
1441
  onClick={() => {
1435
1442
  setGithubReposLoaded(false);
1436
1443
  void handleFetchGitHubRepos(true);
1437
1444
  }}
1438
- className="mt-2 inline-flex items-center rounded-[4px] border border-[var(--vk-border)] px-2 py-1 text-[12px] text-[var(--vk-text-normal)] hover:bg-[var(--vk-bg-hover)]"
1445
+ disabled={githubReposLoading}
1446
+ className="inline-flex h-9 items-center rounded-[6px] border border-[var(--vk-border)] px-3 text-[12px] text-[var(--vk-text-normal)] hover:bg-[var(--vk-bg-hover)] disabled:opacity-50"
1439
1447
  >
1440
- Retry
1448
+ {githubReposLoading ? (
1449
+ <Loader2 className="h-3.5 w-3.5 animate-spin" />
1450
+ ) : (
1451
+ <>
1452
+ <RefreshCcw className="mr-1.5 h-3.5 w-3.5" />
1453
+ Refresh
1454
+ </>
1455
+ )}
1441
1456
  </button>
1442
1457
  </div>
1443
- ) : null}
1444
1458
 
1445
- {!githubReposError && filteredGitHubRepos.length > 0 ? (
1446
- <div className="mt-3 max-h-[260px] space-y-2 overflow-y-auto pr-1">
1447
- {filteredGitHubRepos.map((repo) => {
1448
- const repoUpdatedLabel = formatRepoUpdatedLabel(repo.updatedAt);
1449
- const selected = selectedGitHubRepoData?.httpsUrl === repo.httpsUrl;
1450
- return (
1451
- <button
1452
- key={repo.httpsUrl}
1453
- type="button"
1454
- onClick={() => {
1455
- void handleSelectGitHubRepo(repo.httpsUrl);
1456
- }}
1457
- className={`flex w-full items-start gap-3 rounded-[5px] border px-3 py-3 text-left transition ${
1458
- selected
1459
- ? "border-[var(--vk-orange)] bg-[var(--vk-bg-panel)]"
1460
- : "border-[var(--vk-border)] bg-[var(--vk-bg-panel)] hover:bg-[var(--vk-bg-hover)]"
1461
- }`}
1462
- >
1463
- <span className="mt-0.5 inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-[5px] border border-[var(--vk-border)] bg-[var(--vk-bg-main)] text-[var(--vk-text-strong)]">
1464
- <RepoIcon className="h-4 w-4" />
1465
- </span>
1466
- <span className="min-w-0 flex-1">
1467
- <span className="flex flex-wrap items-center gap-2">
1468
- <span className="truncate text-[13px] font-medium text-[var(--vk-text-strong)]">
1469
- {repo.fullName}
1470
- </span>
1471
- <span className="inline-flex items-center gap-1 rounded-full border border-[var(--vk-border)] px-2 py-0.5 text-[11px] text-[var(--vk-text-muted)]">
1472
- {repo.private ? <LockIcon className="h-3 w-3" /> : <MarkGithubIcon className="h-3 w-3" />}
1473
- {repo.private ? "Private" : "Public"}
1459
+ <div className="relative">
1460
+ <MarkGithubIcon className="pointer-events-none absolute left-3 top-1/2 h-[16px] w-[16px] -translate-y-1/2 text-[var(--vk-text-muted)]" />
1461
+ <input
1462
+ value={githubRepoSearch}
1463
+ onChange={(event) => setGithubRepoSearch(event.target.value)}
1464
+ onKeyDown={(event) => {
1465
+ if (event.key === "Enter" && showUseSearchValueAction) {
1466
+ event.preventDefault();
1467
+ void handleUseSearchValueAsRepository();
1468
+ }
1469
+ }}
1470
+ placeholder="Search GitHub repos or paste a repository URL"
1471
+ className="h-11 w-full rounded-[8px] border border-[var(--vk-border)] bg-transparent pl-10 pr-3 text-[14px] text-[var(--vk-text-normal)] outline-none focus:border-[var(--vk-orange)]"
1472
+ />
1473
+ </div>
1474
+
1475
+ {showUseSearchValueAction ? (
1476
+ <button
1477
+ type="button"
1478
+ onClick={() => {
1479
+ void handleUseSearchValueAsRepository();
1480
+ }}
1481
+ className="inline-flex w-full items-center justify-center gap-2 rounded-[8px] border border-[var(--vk-border)] bg-[var(--vk-bg-panel)] px-3 py-2.5 text-[13px] text-[var(--vk-text-normal)] hover:bg-[var(--vk-bg-hover)]"
1482
+ >
1483
+ <RepoIcon className="h-4 w-4 text-[var(--vk-text-strong)]" />
1484
+ <span className="truncate">
1485
+ Use {normalizedGitHubSearchUrl ? "this GitHub repository" : "pasted repository URL"}
1486
+ </span>
1487
+ </button>
1488
+ ) : null}
1489
+
1490
+ {githubReposLoading && githubRepos.length === 0 ? (
1491
+ <div className="rounded-[8px] border border-[var(--vk-border)] px-3 py-3 text-[12px] text-[var(--vk-text-muted)]">
1492
+ Loading accessible GitHub repositories...
1493
+ </div>
1494
+ ) : null}
1495
+
1496
+ {githubReposError ? (
1497
+ <div className="rounded-[8px] border border-[var(--vk-red)]/40 bg-[var(--vk-bg-panel)] px-3 py-3 text-[12px] text-[var(--vk-red)]">
1498
+ <p>{githubReposError}</p>
1499
+ <button
1500
+ type="button"
1501
+ onClick={() => {
1502
+ setGithubReposLoaded(false);
1503
+ void handleFetchGitHubRepos(true);
1504
+ }}
1505
+ className="mt-2 inline-flex items-center rounded-[6px] border border-[var(--vk-border)] px-2 py-1 text-[12px] text-[var(--vk-text-normal)] hover:bg-[var(--vk-bg-hover)]"
1506
+ >
1507
+ Retry
1508
+ </button>
1509
+ </div>
1510
+ ) : null}
1511
+
1512
+ {!githubReposError && filteredGitHubRepos.length > 0 ? (
1513
+ <div className="max-h-[min(42dvh,340px)] space-y-2 overflow-y-auto pr-1">
1514
+ {filteredGitHubRepos.map((repo) => {
1515
+ const repoUpdatedLabel = formatRepoUpdatedLabel(repo.updatedAt);
1516
+ const selected = selectedGitHubRepoData?.httpsUrl === repo.httpsUrl;
1517
+ return (
1518
+ <button
1519
+ key={repo.httpsUrl}
1520
+ type="button"
1521
+ onClick={() => {
1522
+ void handleSelectGitHubRepo(repo.httpsUrl);
1523
+ }}
1524
+ className={`flex w-full items-start gap-3 rounded-[8px] border px-3 py-3 text-left transition ${
1525
+ selected
1526
+ ? "border-[var(--vk-orange)] bg-[color:color-mix(in_srgb,var(--vk-orange)_10%,transparent)]"
1527
+ : "border-[var(--vk-border)] bg-[var(--vk-bg-panel)] hover:bg-[var(--vk-bg-hover)]"
1528
+ }`}
1529
+ >
1530
+ <span className="mt-0.5 inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-[8px] border border-[var(--vk-border)] bg-[var(--vk-bg-main)] text-[var(--vk-text-strong)]">
1531
+ <RepoIcon className="h-4 w-4" />
1532
+ </span>
1533
+ <span className="min-w-0 flex-1">
1534
+ <span className="flex flex-wrap items-center gap-2">
1535
+ <span className="truncate text-[13px] font-medium text-[var(--vk-text-strong)]">
1536
+ {repo.fullName}
1537
+ </span>
1538
+ <span className="inline-flex items-center gap-1 rounded-full border border-[var(--vk-border)] px-2 py-0.5 text-[11px] text-[var(--vk-text-muted)]">
1539
+ {repo.private ? <LockIcon className="h-3 w-3" /> : <MarkGithubIcon className="h-3 w-3" />}
1540
+ {repo.private ? "Private" : "Public"}
1541
+ </span>
1542
+ <span className="inline-flex items-center gap-1 rounded-full border border-[var(--vk-border)] px-2 py-0.5 text-[11px] text-[var(--vk-text-muted)]">
1543
+ <GitBranchIcon className="h-3 w-3" />
1544
+ {repo.defaultBranch}
1545
+ </span>
1474
1546
  </span>
1475
- <span className="inline-flex items-center gap-1 rounded-full border border-[var(--vk-border)] px-2 py-0.5 text-[11px] text-[var(--vk-text-muted)]">
1476
- <GitBranchIcon className="h-3 w-3" />
1477
- {repo.defaultBranch}
1547
+ {repo.description ? (
1548
+ <span className="mt-1 block line-clamp-2 text-[12px] leading-[17px] text-[var(--vk-text-muted)]">
1549
+ {repo.description}
1550
+ </span>
1551
+ ) : null}
1552
+ <span className="mt-1 flex flex-wrap gap-x-3 gap-y-1 text-[11px] text-[var(--vk-text-muted)]">
1553
+ {repo.ownerLogin ? <span>{repo.ownerLogin}</span> : null}
1554
+ {repoUpdatedLabel ? <span>{repoUpdatedLabel}</span> : null}
1555
+ {repo.permission ? <span>{repo.permission.toLowerCase()}</span> : null}
1478
1556
  </span>
1479
1557
  </span>
1480
- {repo.description ? (
1481
- <span className="mt-1 block line-clamp-2 text-[12px] leading-[17px] text-[var(--vk-text-muted)]">
1482
- {repo.description}
1483
- </span>
1484
- ) : null}
1485
- <span className="mt-1 flex flex-wrap gap-x-3 gap-y-1 text-[11px] text-[var(--vk-text-muted)]">
1486
- {repo.ownerLogin ? <span>{repo.ownerLogin}</span> : null}
1487
- {repoUpdatedLabel ? <span>{repoUpdatedLabel}</span> : null}
1488
- {repo.permission ? <span>{repo.permission.toLowerCase()}</span> : null}
1558
+ <span className="ml-auto inline-flex h-4 w-4 shrink-0 items-center justify-center text-[var(--vk-text-strong)]">
1559
+ {selected ? <Check className="h-4 w-4" /> : null}
1489
1560
  </span>
1490
- </span>
1491
- <span className="ml-auto inline-flex h-4 w-4 shrink-0 items-center justify-center text-[var(--vk-text-strong)]">
1492
- {selected ? <Check className="h-4 w-4" /> : null}
1493
- </span>
1494
- </button>
1495
- );
1496
- })}
1497
- </div>
1498
- ) : null}
1499
-
1500
- {!githubReposLoading && !githubReposError && filteredGitHubRepos.length === 0 ? (
1501
- <p className="mt-3 text-[12px] text-[var(--vk-text-muted)]">
1502
- {githubRepoSearch.trim().length > 0
1503
- ? "No matching repositories. Try another search or paste a repository URL."
1504
- : "No accessible GitHub repositories were found for this machine yet."}
1505
- </p>
1506
- ) : null}
1507
- </div>
1561
+ </button>
1562
+ );
1563
+ })}
1564
+ </div>
1565
+ ) : null}
1508
1566
 
1509
- {gitUrl.trim().length > 0 ? (
1510
- <div className="rounded-[6px] border border-[var(--vk-border)] bg-[var(--vk-bg-main)] p-3">
1567
+ {!githubReposLoading && !githubReposError && filteredGitHubRepos.length === 0 ? (
1568
+ <p className="text-[12px] text-[var(--vk-text-muted)]">
1569
+ {githubRepoSearch.trim().length > 0
1570
+ ? "No matching repositories. Try another search or paste a repository URL."
1571
+ : "No accessible GitHub repositories were found for this machine yet."}
1572
+ </p>
1573
+ ) : null}
1574
+ </div>
1575
+ ) : (
1576
+ <div className="space-y-3 rounded-[10px] border border-[var(--vk-border)] bg-[var(--vk-bg-main)] p-3 sm:p-4">
1511
1577
  <div className="flex items-start gap-3">
1512
- <span className="inline-flex h-9 w-9 items-center justify-center rounded-[5px] border border-[var(--vk-border)] bg-[var(--vk-bg-panel)] text-[var(--vk-text-strong)]">
1513
- <RepoIcon className="h-[18px] w-[18px]" />
1578
+ <span className="inline-flex h-10 w-10 items-center justify-center rounded-[8px] border border-[var(--vk-border)] bg-[var(--vk-bg-panel)] text-[var(--vk-text-strong)]">
1579
+ <FolderOpen className="h-5 w-5" />
1514
1580
  </span>
1515
1581
  <div className="min-w-0 flex-1">
1516
- <div className="flex flex-wrap items-center gap-2">
1517
- <span className="truncate text-[14px] font-medium text-[var(--vk-text-strong)]">
1518
- {selectedGitHubRepoData?.fullName ?? gitUrl}
1519
- </span>
1520
- <span className="inline-flex items-center gap-1 rounded-full border border-[var(--vk-border)] px-2 py-0.5 text-[11px] text-[var(--vk-text-muted)]">
1521
- {selectedGitHubRepoData ? (
1522
- selectedGitHubRepoData.private ? <LockIcon className="h-3 w-3" /> : <MarkGithubIcon className="h-3 w-3" />
1523
- ) : (
1524
- <MarkGithubIcon className="h-3 w-3" />
1525
- )}
1526
- {selectedGitHubRepoData ? (selectedGitHubRepoData.private ? "Private" : "Public") : "Manual URL"}
1527
- </span>
1528
- <span className="inline-flex items-center gap-1 rounded-full border border-[var(--vk-border)] px-2 py-0.5 text-[11px] text-[var(--vk-text-muted)]">
1529
- <GitBranchIcon className="h-3 w-3" />
1530
- {defaultBranch || "main"}
1531
- </span>
1532
- </div>
1533
- {selectedGitHubRepoData?.description ? (
1534
- <p className="mt-1 line-clamp-2 text-[12px] leading-[17px] text-[var(--vk-text-muted)]">
1535
- {selectedGitHubRepoData.description}
1536
- </p>
1537
- ) : (
1538
- <p className="mt-1 truncate text-[12px] text-[var(--vk-text-muted)]">{gitUrl}</p>
1539
- )}
1540
- <div className="mt-1 flex flex-wrap gap-x-3 gap-y-1 text-[11px] text-[var(--vk-text-muted)]">
1541
- {selectedGitHubRepoData?.ownerLogin ? <span>{selectedGitHubRepoData.ownerLogin}</span> : null}
1542
- {selectedRepoUpdatedLabel ? <span>{selectedRepoUpdatedLabel}</span> : null}
1543
- {selectedGitHubRepoData?.permission ? (
1544
- <span>{selectedGitHubRepoData.permission.toLowerCase()}</span>
1545
- ) : null}
1546
- </div>
1582
+ <p className="text-[14px] font-medium text-[var(--vk-text-strong)]">Choose folder</p>
1583
+ <p className="pt-0.5 text-[12px] leading-5 text-[var(--vk-text-muted)]">
1584
+ Pick the repository folder already on disk. You can adjust branch and runtime defaults next.
1585
+ </p>
1547
1586
  </div>
1548
1587
  </div>
1549
- </div>
1550
- ) : null}
1551
- </>
1552
- ) : (
1553
- <>
1554
- <label className="block">
1555
- <span className="mb-1.5 block text-[12px] text-[var(--vk-text-muted)]">Local Path</span>
1556
- <div className="flex items-center gap-2">
1557
- <input
1558
- value={path}
1559
- readOnly
1560
- onClick={() => openFolderPicker("local")}
1561
- placeholder="Use Browse to select a repository folder"
1562
- className="h-9 w-full cursor-pointer rounded-[4px] border border-[var(--vk-border)] bg-transparent px-2 text-[14px] text-[var(--vk-text-normal)] outline-none focus:border-[var(--vk-orange)]"
1563
- />
1588
+
1564
1589
  <button
1565
1590
  type="button"
1566
1591
  onClick={() => openFolderPicker("local")}
1567
- className="inline-flex h-9 items-center rounded-[4px] border border-[var(--vk-border)] px-2 text-[12px] text-[var(--vk-text-normal)] hover:bg-[var(--vk-bg-hover)]"
1568
- title="Browse folders"
1592
+ className="inline-flex h-11 w-full items-center justify-center gap-2 rounded-[8px] border border-[var(--vk-border)] bg-[var(--vk-bg-panel)] px-3 text-[13px] text-[var(--vk-text-normal)] hover:bg-[var(--vk-bg-hover)]"
1569
1593
  >
1570
1594
  <FolderOpen className="h-4 w-4" />
1595
+ {localPath.trim().length > 0 ? "Choose another folder" : "Choose folder"}
1571
1596
  </button>
1572
- </div>
1573
- </label>
1574
- <label className="flex items-center gap-2 text-[13px] text-[var(--vk-text-normal)]">
1575
- <input
1576
- type="checkbox"
1577
- checked={initializeGit}
1578
- onChange={(event) => setInitializeGit(event.target.checked)}
1579
- className="h-4 w-4 rounded border border-[var(--vk-border)] bg-transparent accent-[var(--vk-orange)]"
1580
- />
1581
- <span>Initialize git if this folder is non-git</span>
1582
- </label>
1583
- </>
1584
- )}
1585
1597
 
1586
- <label className="block">
1587
- <span className="mb-1.5 block text-[12px] text-[var(--vk-text-muted)]">Workspace Name (optional)</span>
1588
- <input
1589
- value={projectId}
1590
- onChange={(event) => {
1591
- setProjectId(event.target.value);
1592
- setProjectIdTouched(true);
1593
- }}
1594
- placeholder="auto-derived from the selected repository or folder"
1595
- className="h-9 w-full rounded-[4px] border border-[var(--vk-border)] bg-transparent px-2 text-[14px] text-[var(--vk-text-normal)] outline-none focus:border-[var(--vk-orange)]"
1596
- />
1597
- </label>
1598
-
1599
- <div className="grid grid-cols-1 gap-3 md:grid-cols-2">
1600
- <label className="block">
1601
- <span className="mb-1.5 block text-[12px] text-[var(--vk-text-muted)]">Branch</span>
1602
- <div className="flex items-center gap-2">
1603
- <input
1604
- value={defaultBranch}
1605
- onChange={(event) => setDefaultBranch(event.target.value)}
1606
- placeholder="Uses the repository default branch"
1607
- className="h-9 w-full rounded-[4px] border border-[var(--vk-border)] bg-transparent px-2 text-[14px] text-[var(--vk-text-normal)] outline-none focus:border-[var(--vk-orange)]"
1608
- />
1609
- <button
1610
- type="button"
1611
- onClick={() => {
1612
- void handleDetectBranches();
1613
- }}
1614
- disabled={branchesLoading}
1615
- className="inline-flex h-9 items-center rounded-[4px] border border-[var(--vk-border)] px-2 text-[12px] text-[var(--vk-text-normal)] hover:bg-[var(--vk-bg-hover)] disabled:opacity-50"
1616
- title="Detect branches"
1617
- >
1618
- {branchesLoading ? (
1619
- <Loader2 className="h-3.5 w-3.5 animate-spin" />
1598
+ {localPath.trim().length > 0 ? (
1599
+ <div className="rounded-[8px] border border-[var(--vk-border)] bg-[var(--vk-bg-panel)] px-3 py-3">
1600
+ <p className="text-[12px] font-medium text-[var(--vk-text-strong)]">Selected folder</p>
1601
+ <p className="mt-1 break-all text-[12px] leading-5 text-[var(--vk-text-muted)]">{localPath}</p>
1602
+ </div>
1620
1603
  ) : (
1621
- <RefreshCcw className="h-3.5 w-3.5" />
1604
+ <p className="text-[12px] text-[var(--vk-text-muted)]">
1605
+ The folder picker stays separate so you do not lose your place in this setup flow on mobile.
1606
+ </p>
1622
1607
  )}
1623
- </button>
1624
- </div>
1625
- {branchOptions.length > 0 && (
1626
- <select
1627
- value={defaultBranch}
1628
- onChange={(event) => setDefaultBranch(event.target.value)}
1629
- className="mt-2 h-8 w-full rounded-[4px] border border-[var(--vk-border)] bg-[var(--vk-bg-panel)] px-2 text-[12px] text-[var(--vk-text-normal)] outline-none focus:border-[var(--vk-orange)]"
1630
- >
1631
- {branchOptions.map((branch) => (
1632
- <option key={branch} value={branch}>
1633
- {branch}
1634
- </option>
1635
- ))}
1636
- </select>
1637
- )}
1638
- {branchesError && (
1639
- <p className="mt-1 text-[11px] text-[var(--vk-red)]">{branchesError}</p>
1608
+ </div>
1640
1609
  )}
1641
- </label>
1642
-
1643
- <label className="block">
1644
- <span className="mb-1.5 block text-[12px] text-[var(--vk-text-muted)]">Agent</span>
1645
- <select
1646
- value={agent}
1647
- onChange={(event) => setAgent(event.target.value)}
1648
- className="h-9 w-full rounded-[4px] border border-[var(--vk-border)] bg-[var(--vk-bg-panel)] px-2 text-[14px] text-[var(--vk-text-normal)] outline-none focus:border-[var(--vk-orange)]"
1649
- >
1650
- {orderedAgentOptions.map((item) => (
1651
- <option key={item} value={item} className="bg-[var(--vk-bg-panel)] text-[var(--vk-text-normal)]">
1652
- {getAgentLabel(item)}
1653
- </option>
1654
- ))}
1655
- </select>
1656
- </label>
1657
- </div>
1658
1610
 
1659
- {mode === "git" ? (
1660
- <details className="rounded-[4px] border border-[var(--vk-border)] bg-[var(--vk-bg-main)]">
1661
- <summary className="cursor-pointer list-none px-3 py-2 text-[13px] text-[var(--vk-text-normal)] marker:hidden">
1662
- <span className="inline-flex items-center gap-2">
1663
- <ChevronDown className="h-3.5 w-3.5 text-[var(--vk-text-muted)]" />
1664
- Advanced options
1665
- </span>
1666
- </summary>
1667
- <div className="space-y-3 border-t border-[var(--vk-border)] px-3 py-3">
1611
+ {error ? <p className="text-[12px] text-[var(--vk-red)]">{error}</p> : null}
1612
+ </div>
1613
+ ) : (
1614
+ <div className="space-y-4">
1615
+ <div className="rounded-[10px] border border-[var(--vk-border)] bg-[var(--vk-bg-main)] p-3 sm:p-4">
1616
+ <div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
1617
+ <div className="min-w-0 flex-1">
1618
+ <p className="text-[12px] uppercase tracking-[0.08em] text-[var(--vk-text-muted)]">
1619
+ {mode === "git" ? "Selected repository" : "Selected folder"}
1620
+ </p>
1621
+ <p className="mt-1 break-words text-[15px] font-medium text-[var(--vk-text-strong)]">
1622
+ {selectedSourceTitle}
1623
+ </p>
1624
+ <p className="mt-1 break-all text-[12px] leading-5 text-[var(--vk-text-muted)]">
1625
+ {selectedSourceSubtitle}
1626
+ </p>
1627
+ <div className="mt-2 flex flex-wrap gap-2">
1628
+ {mode === "git" ? (
1629
+ <>
1630
+ <span className="inline-flex items-center gap-1 rounded-full border border-[var(--vk-border)] px-2 py-0.5 text-[11px] text-[var(--vk-text-muted)]">
1631
+ {selectedGitHubRepoData?.private
1632
+ ? <LockIcon className="h-3 w-3" />
1633
+ : <MarkGithubIcon className="h-3 w-3" />}
1634
+ {selectedGitHubRepoData
1635
+ ? (selectedGitHubRepoData.private ? "Private" : "Public")
1636
+ : "Manual URL"}
1637
+ </span>
1638
+ <span className="inline-flex items-center gap-1 rounded-full border border-[var(--vk-border)] px-2 py-0.5 text-[11px] text-[var(--vk-text-muted)]">
1639
+ <GitBranchIcon className="h-3 w-3" />
1640
+ {defaultBranch || "main"}
1641
+ </span>
1642
+ {selectedGitHubRepoData?.ownerLogin ? (
1643
+ <span className="inline-flex rounded-full border border-[var(--vk-border)] px-2 py-0.5 text-[11px] text-[var(--vk-text-muted)]">
1644
+ {selectedGitHubRepoData.ownerLogin}
1645
+ </span>
1646
+ ) : null}
1647
+ {selectedRepoUpdatedLabel ? (
1648
+ <span className="inline-flex rounded-full border border-[var(--vk-border)] px-2 py-0.5 text-[11px] text-[var(--vk-text-muted)]">
1649
+ {selectedRepoUpdatedLabel}
1650
+ </span>
1651
+ ) : null}
1652
+ </>
1653
+ ) : (
1654
+ <span className="inline-flex rounded-full border border-[var(--vk-border)] px-2 py-0.5 text-[11px] text-[var(--vk-text-muted)]">
1655
+ Local repository
1656
+ </span>
1657
+ )}
1658
+ </div>
1659
+ </div>
1660
+
1661
+ <button
1662
+ type="button"
1663
+ onClick={() => setStep("source")}
1664
+ className="inline-flex h-9 items-center justify-center rounded-[6px] border border-[var(--vk-border)] px-3 text-[12px] text-[var(--vk-text-normal)] hover:bg-[var(--vk-bg-hover)]"
1665
+ >
1666
+ Change
1667
+ </button>
1668
+ </div>
1669
+ </div>
1670
+
1671
+ <div className="grid grid-cols-1 gap-3 lg:grid-cols-2">
1668
1672
  <label className="block">
1669
- <span className="mb-1.5 block text-[12px] text-[var(--vk-text-muted)]">
1670
- Local Copy Location (optional)
1671
- </span>
1673
+ <span className="mb-1.5 block text-[12px] text-[var(--vk-text-muted)]">Workspace Name (optional)</span>
1674
+ <input
1675
+ value={projectId}
1676
+ onChange={(event) => {
1677
+ setProjectId(event.target.value);
1678
+ setProjectIdTouched(true);
1679
+ }}
1680
+ placeholder="auto-derived from the selected repository or folder"
1681
+ className="h-10 w-full rounded-[8px] border border-[var(--vk-border)] bg-transparent px-3 text-[14px] text-[var(--vk-text-normal)] outline-none focus:border-[var(--vk-orange)]"
1682
+ />
1683
+ </label>
1684
+
1685
+ <label className="block">
1686
+ <span className="mb-1.5 block text-[12px] text-[var(--vk-text-muted)]">Branch</span>
1672
1687
  <div className="flex items-center gap-2">
1673
1688
  <input
1674
- value={path}
1675
- readOnly
1676
- onClick={() => openFolderPicker("clone")}
1677
- placeholder="Choose a folder only if you want a specific clone location"
1678
- className="h-9 w-full cursor-pointer rounded-[4px] border border-[var(--vk-border)] bg-transparent px-2 text-[14px] text-[var(--vk-text-normal)] outline-none focus:border-[var(--vk-orange)]"
1689
+ value={defaultBranch}
1690
+ onChange={(event) => setDefaultBranch(event.target.value)}
1691
+ placeholder="Uses the repository default branch"
1692
+ className="h-10 w-full rounded-[8px] border border-[var(--vk-border)] bg-transparent px-3 text-[14px] text-[var(--vk-text-normal)] outline-none focus:border-[var(--vk-orange)]"
1679
1693
  />
1680
1694
  <button
1681
1695
  type="button"
1682
- onClick={() => openFolderPicker("clone")}
1683
- className="inline-flex h-9 items-center rounded-[4px] border border-[var(--vk-border)] px-2 text-[12px] text-[var(--vk-text-normal)] hover:bg-[var(--vk-bg-hover)]"
1684
- title="Browse folders"
1696
+ onClick={() => {
1697
+ void handleDetectBranches();
1698
+ }}
1699
+ disabled={branchesLoading}
1700
+ className="inline-flex h-10 items-center rounded-[8px] border border-[var(--vk-border)] px-3 text-[12px] text-[var(--vk-text-normal)] hover:bg-[var(--vk-bg-hover)] disabled:opacity-50"
1701
+ title="Detect branches"
1685
1702
  >
1686
- <FolderOpen className="h-4 w-4" />
1703
+ {branchesLoading ? (
1704
+ <Loader2 className="h-3.5 w-3.5 animate-spin" />
1705
+ ) : (
1706
+ <RefreshCcw className="h-3.5 w-3.5" />
1707
+ )}
1687
1708
  </button>
1688
1709
  </div>
1710
+ {branchOptions.length > 0 ? (
1711
+ <select
1712
+ value={defaultBranch}
1713
+ onChange={(event) => setDefaultBranch(event.target.value)}
1714
+ className="mt-2 h-9 w-full rounded-[8px] border border-[var(--vk-border)] bg-[var(--vk-bg-panel)] px-3 text-[12px] text-[var(--vk-text-normal)] outline-none focus:border-[var(--vk-orange)]"
1715
+ >
1716
+ {branchOptions.map((branch) => (
1717
+ <option key={branch} value={branch}>
1718
+ {branch}
1719
+ </option>
1720
+ ))}
1721
+ </select>
1722
+ ) : null}
1723
+ {branchesError ? <p className="mt-1 text-[11px] text-[var(--vk-red)]">{branchesError}</p> : null}
1689
1724
  </label>
1725
+ </div>
1690
1726
 
1691
- <label className="flex items-start gap-2 rounded-[4px] border border-[var(--vk-border)] px-2 py-2 text-[13px] text-[var(--vk-text-normal)]">
1692
- <input
1693
- type="checkbox"
1694
- checked={useWorktree}
1695
- onChange={(event) => setUseWorktree(event.target.checked)}
1696
- className="mt-0.5 h-4 w-4 rounded border border-[var(--vk-border)] bg-transparent accent-[var(--vk-orange)]"
1697
- />
1698
- <span>
1699
- Keep work isolated in a new worktree
1700
- <span className="block text-[11px] text-[var(--vk-text-muted)]">
1701
- Turn this off only if you want sessions to run directly in the selected branch.
1702
- </span>
1727
+ <div className="flex flex-wrap gap-2 text-[11px] text-[var(--vk-text-muted)]">
1728
+ <span className="inline-flex rounded-full border border-[var(--vk-border)] px-2 py-0.5">
1729
+ Agent: {getAgentLabel(agent)}
1730
+ </span>
1731
+ <span className="inline-flex rounded-full border border-[var(--vk-border)] px-2 py-0.5">
1732
+ {useWorktree ? "Worktree isolation on" : "Runs on selected branch"}
1733
+ </span>
1734
+ {mode === "local" ? (
1735
+ <span className="inline-flex rounded-full border border-[var(--vk-border)] px-2 py-0.5">
1736
+ {initializeGit ? "Auto-init git if needed" : "Do not init git"}
1703
1737
  </span>
1704
- </label>
1738
+ ) : null}
1705
1739
  </div>
1706
- </details>
1707
- ) : (
1708
- <label className="flex items-start gap-2 rounded-[4px] border border-[var(--vk-border)] px-2 py-2 text-[13px] text-[var(--vk-text-normal)]">
1709
- <input
1710
- type="checkbox"
1711
- checked={useWorktree}
1712
- onChange={(event) => setUseWorktree(event.target.checked)}
1713
- className="mt-0.5 h-4 w-4 rounded border border-[var(--vk-border)] bg-transparent accent-[var(--vk-orange)]"
1714
- />
1715
- <span>
1716
- Keep work isolated in a new worktree
1717
- <span className="block text-[11px] text-[var(--vk-text-muted)]">
1718
- Turn this off only if you want sessions to run directly in the selected branch.
1719
- </span>
1720
- </span>
1721
- </label>
1722
- )}
1723
1740
 
1724
- {error && <p className="text-[12px] text-[var(--vk-red)]">{error}</p>}
1741
+ <details className="rounded-[10px] border border-[var(--vk-border)] bg-[var(--vk-bg-main)]">
1742
+ <summary className="cursor-pointer list-none px-3 py-3 text-[13px] text-[var(--vk-text-normal)] marker:hidden">
1743
+ <span className="inline-flex items-center gap-2">
1744
+ <ChevronDown className="h-3.5 w-3.5 text-[var(--vk-text-muted)]" />
1745
+ Runtime and repository options
1746
+ </span>
1747
+ </summary>
1748
+ <div className="space-y-3 border-t border-[var(--vk-border)] px-3 py-3">
1749
+ <label className="block">
1750
+ <span className="mb-1.5 block text-[12px] text-[var(--vk-text-muted)]">Agent</span>
1751
+ <select
1752
+ value={agent}
1753
+ onChange={(event) => setAgent(event.target.value)}
1754
+ className="h-10 w-full rounded-[8px] border border-[var(--vk-border)] bg-[var(--vk-bg-panel)] px-3 text-[14px] text-[var(--vk-text-normal)] outline-none focus:border-[var(--vk-orange)]"
1755
+ >
1756
+ {orderedAgentOptions.map((item) => (
1757
+ <option key={item} value={item} className="bg-[var(--vk-bg-panel)] text-[var(--vk-text-normal)]">
1758
+ {getAgentLabel(item)}
1759
+ </option>
1760
+ ))}
1761
+ </select>
1762
+ </label>
1763
+
1764
+ <div className="rounded-[10px] border border-[var(--vk-border)] bg-[var(--vk-bg-panel)] px-3 py-3">
1765
+ <label className="flex items-start gap-3 text-[13px] text-[var(--vk-text-normal)]">
1766
+ <input
1767
+ type="checkbox"
1768
+ checked={useWorktree}
1769
+ onChange={(event) => setUseWorktree(event.target.checked)}
1770
+ className="mt-0.5 h-4 w-4 rounded border border-[var(--vk-border)] bg-transparent accent-[var(--vk-orange)]"
1771
+ />
1772
+ <span>
1773
+ Keep work isolated in a new worktree
1774
+ <span className="mt-1 block text-[11px] leading-5 text-[var(--vk-text-muted)]">
1775
+ Recommended. Turn this off only if sessions should run directly on the selected branch.
1776
+ </span>
1777
+ </span>
1778
+ </label>
1779
+ </div>
1780
+
1781
+ {mode === "local" ? (
1782
+ <div className="rounded-[10px] border border-[var(--vk-border)] bg-[var(--vk-bg-panel)] px-3 py-3">
1783
+ <label className="flex items-start gap-3 text-[13px] text-[var(--vk-text-normal)]">
1784
+ <input
1785
+ type="checkbox"
1786
+ checked={initializeGit}
1787
+ onChange={(event) => setInitializeGit(event.target.checked)}
1788
+ className="mt-0.5 h-4 w-4 rounded border border-[var(--vk-border)] bg-transparent accent-[var(--vk-orange)]"
1789
+ />
1790
+ <span>
1791
+ Initialize git if this folder is not already a repository
1792
+ <span className="mt-1 block text-[11px] leading-5 text-[var(--vk-text-muted)]">
1793
+ Leave this on unless you know the folder should stay non-git.
1794
+ </span>
1795
+ </span>
1796
+ </label>
1797
+ </div>
1798
+ ) : (
1799
+ <label className="block">
1800
+ <span className="mb-1.5 block text-[12px] text-[var(--vk-text-muted)]">
1801
+ Clone into a specific folder
1802
+ </span>
1803
+ <div className="flex items-center gap-2">
1804
+ <input
1805
+ value={clonePath}
1806
+ readOnly
1807
+ onClick={() => openFolderPicker("clone")}
1808
+ placeholder="Leave empty to use Conductor's default workspace location"
1809
+ className="h-10 w-full cursor-pointer rounded-[8px] border border-[var(--vk-border)] bg-transparent px-3 text-[14px] text-[var(--vk-text-normal)] outline-none focus:border-[var(--vk-orange)]"
1810
+ />
1811
+ <button
1812
+ type="button"
1813
+ onClick={() => openFolderPicker("clone")}
1814
+ className="inline-flex h-10 items-center rounded-[8px] border border-[var(--vk-border)] px-3 text-[12px] text-[var(--vk-text-normal)] hover:bg-[var(--vk-bg-hover)]"
1815
+ title="Browse folders"
1816
+ >
1817
+ <FolderOpen className="h-4 w-4" />
1818
+ </button>
1819
+ </div>
1820
+ </label>
1821
+ )}
1822
+ </div>
1823
+ </details>
1824
+
1825
+ {error ? <p className="text-[12px] text-[var(--vk-red)]">{error}</p> : null}
1826
+ </div>
1827
+ )}
1725
1828
  </div>
1726
1829
 
1727
- <footer className="flex flex-wrap items-center justify-end gap-2 border-t border-[var(--vk-border)] px-4 py-3">
1728
- <button
1729
- type="button"
1730
- onClick={onClose}
1731
- disabled={creating}
1732
- className="inline-flex h-9 items-center rounded-[4px] border border-[var(--vk-border)] px-3 text-[13px] text-[var(--vk-text-normal)] hover:bg-[var(--vk-bg-hover)] disabled:opacity-50"
1733
- >
1734
- Cancel
1735
- </button>
1736
- <button
1737
- type="submit"
1738
- disabled={!canSubmit || creating}
1739
- className="inline-flex h-9 items-center rounded-[4px] bg-[var(--vk-bg-active)] px-3 text-[13px] text-[var(--vk-text-strong)] hover:bg-[var(--vk-bg-hover)] disabled:opacity-50"
1740
- >
1741
- {creating ? (
1742
- <>
1743
- <Loader2 className="mr-1.5 h-3.5 w-3.5 animate-spin" />
1744
- Adding...
1745
- </>
1746
- ) : "Add Workspace"}
1747
- </button>
1748
- </footer>
1830
+ <footer className="border-t border-[var(--vk-border)] bg-[color:color-mix(in_srgb,var(--vk-bg-panel)_92%,transparent)] px-4 py-3 pb-[calc(0.75rem+env(safe-area-inset-bottom))] backdrop-blur">
1831
+ {step === "source" ? (
1832
+ <div className="flex flex-col-reverse gap-2 sm:flex-row sm:items-center sm:justify-between">
1833
+ <button
1834
+ type="button"
1835
+ onClick={onClose}
1836
+ disabled={creating}
1837
+ className="inline-flex h-10 items-center justify-center rounded-[8px] border border-[var(--vk-border)] px-4 text-[13px] text-[var(--vk-text-normal)] hover:bg-[var(--vk-bg-hover)] disabled:opacity-50"
1838
+ >
1839
+ Cancel
1840
+ </button>
1841
+ <button
1842
+ type="button"
1843
+ onClick={() => {
1844
+ if (mode === "local" && !sourceReady) {
1845
+ openFolderPicker("local");
1846
+ return;
1847
+ }
1848
+ handleContinueToDetails();
1849
+ }}
1850
+ disabled={(mode === "git" && !sourceReady) || creating}
1851
+ className="inline-flex h-10 items-center justify-center rounded-[8px] bg-[var(--vk-bg-active)] px-4 text-[13px] text-[var(--vk-text-strong)] hover:bg-[var(--vk-bg-hover)] disabled:opacity-50"
1852
+ >
1853
+ {mode === "local" && !sourceReady ? "Choose folder" : "Continue"}
1854
+ </button>
1855
+ </div>
1856
+ ) : (
1857
+ <div className="flex flex-col-reverse gap-2 sm:flex-row sm:items-center sm:justify-between">
1858
+ <button
1859
+ type="button"
1860
+ onClick={() => setStep("source")}
1861
+ disabled={creating}
1862
+ className="inline-flex h-10 items-center justify-center rounded-[8px] border border-[var(--vk-border)] px-4 text-[13px] text-[var(--vk-text-normal)] hover:bg-[var(--vk-bg-hover)] disabled:opacity-50"
1863
+ >
1864
+ Back
1865
+ </button>
1866
+ <button
1867
+ type="submit"
1868
+ disabled={!canSubmit || creating}
1869
+ className="inline-flex h-10 items-center justify-center rounded-[8px] bg-[var(--vk-bg-active)] px-4 text-[13px] text-[var(--vk-text-strong)] hover:bg-[var(--vk-bg-hover)] disabled:opacity-50"
1870
+ >
1871
+ {creating ? (
1872
+ <>
1873
+ <Loader2 className="mr-1.5 h-3.5 w-3.5 animate-spin" />
1874
+ Adding...
1875
+ </>
1876
+ ) : "Add Workspace"}
1877
+ </button>
1878
+ </div>
1879
+ )}
1880
+ </footer>
1749
1881
  </form>
1750
1882
  </div>
1751
1883
 
1752
1884
  <FolderPickerDialog
1753
1885
  open={folderPickerOpen}
1754
- initialPath={path}
1886
+ initialPath={folderPickerTarget === "clone" ? clonePath : localPath}
1755
1887
  title={folderPickerTarget === "local" ? "Select Local Repository" : "Select Clone Target Folder"}
1756
1888
  description={folderPickerTarget === "local"
1757
1889
  ? "Choose the local repository folder."
@@ -1760,16 +1892,19 @@ export function NewWorkspaceDialog({
1760
1892
  onSelect={(selectedPath) => {
1761
1893
  setFolderPickerOpen(false);
1762
1894
  if (!selectedPath) return;
1763
- setPath(selectedPath);
1764
- if ((mode === "local" || folderPickerTarget === "local") && !projectIdTouched) {
1895
+ if (folderPickerTarget === "clone") {
1896
+ setClonePath(selectedPath);
1897
+ return;
1898
+ }
1899
+ setLocalPath(selectedPath);
1900
+ if (!projectIdTouched) {
1765
1901
  const folderName = extractNameFromPath(selectedPath);
1766
1902
  if (folderName) {
1767
1903
  setProjectId(suggestWorkspaceId(folderName));
1768
1904
  }
1769
1905
  }
1770
- if (mode === "local" || folderPickerTarget === "local") {
1771
- void handleDetectBranches({ path: selectedPath });
1772
- }
1906
+ void handleDetectBranches({ path: selectedPath });
1907
+ setStep("details");
1773
1908
  }}
1774
1909
  />
1775
1910
  </>