Merge #774 fix(clone): treat Clone Repo's custom folder as a parent, like Create New

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer committed 2026-11-06 05:37:14 +01:00
commit cd0bbc3e3a
6 files changed
+180 -16

No files matched your search

+3 -3
View File
@@ -3809,15 +3809,15 @@
</div>
<div class="form-row" id="cloneCustomPathToggleRow">
<label class="checkbox-row"><input type="checkbox" id="cloneCustomPathToggle" onchange="app.toggleCloneCustomPath()"> 📁 Clone into a custom folder</label>
<span class="form-hint">By default the clone goes under ~/codeman-cases. Choose a folder and the repository is cloned into that folder itself (an empty folder, or one that does not exist yet); it is listed like any other case, and deleting the case only unlinks it (your files stay).</span>
<span class="form-hint">By default the clone goes under ~/codeman-cases. Choose a folder instead and the repository is cloned into a subfolder of it named after the case, the same way it goes under ~/codeman-cases; it is listed like any other case, and deleting the case only unlinks it (your files stay).</span>
</div>
<div class="form-row" id="cloneCustomPathRow" style="display:none">
<label>Folder</label>
<label>Parent Folder</label>
<div class="path-input-group">
<input type="text" id="cloneCasePath" placeholder="~/projects" autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false" oninput="app.updateCloneCasePathPreview()">
<button type="button" class="btn path-input-browse" onclick="app.openCloneCasePathPicker()">Browse&hellip;</button>
</div>
<span class="form-hint" id="cloneCasePathPreview">Pick an empty folder, or one that does not exist yet, to clone into.</span>
<span class="form-hint" id="cloneCasePathPreview">Pick the folder the repository should be cloned inside. It goes into a subfolder named after the case.</span>
</div>
<div class="form-row">
<label>Branch or Tag (optional)</label>
+17 -8
View File
@@ -4271,30 +4271,34 @@ Object.assign(CodemanApp.prototype, {
const hint = document.getElementById('cloneCaseNameHint');
if (hint) {
hint.textContent = custom.checked
? 'Filled in from the URL. Names the case only: the repository is cloned into the folder chosen below.'
? 'Filled in from the URL. Names the case and the subfolder of the chosen folder the repository is cloned into.'
: 'Filled in from the URL. Cloned into ~/codeman-cases/<name>, so deleting the case deletes this working tree.';
}
this.updateCloneCasePathPreview();
},
/** The folder the repository is cloned into: exactly what the field holds (no case name is added). */
/** The folder the repository is cloned into: the parent field plus the case name, as Create New does. */
_cloneTargetPath() {
const raw = (document.getElementById('cloneCasePath')?.value || '').trim();
const rawParent = (document.getElementById('cloneCasePath')?.value || '').trim();
const name = (document.getElementById('cloneCaseName')?.value || '').trim();
if (!rawParent || !name) return '';
// Trailing slashes off, but `/` stays the root rather than becoming an empty path.
return raw.length > 1 ? raw.replace(/\/+$/, '') || '/' : raw;
return `${rawParent.replace(/\/+$/, '')}/${name}`;
},
updateCloneCasePathPreview() {
const hint = document.getElementById('cloneCasePathPreview');
if (!hint) return;
const target = this._cloneTargetPath();
hint.textContent = target ? `Will clone into: ${target}` : 'Pick an empty folder, or one that does not exist yet, to clone into.';
hint.textContent = target
? `Will clone into: ${target}`
: 'Pick the folder the repository should be cloned inside. It goes into a subfolder named after the case.';
},
openCloneCasePathPicker() {
const input = document.getElementById('cloneCasePath');
PathPicker.open({
title: 'Choose the folder to clone into',
title: 'Choose the parent folder for the clone',
initialPath: input.value.trim(),
directoriesOnly: true,
onSelect: (path) => {
@@ -4341,12 +4345,14 @@ Object.assign(CodemanApp.prototype, {
onCloneNameEdited() {
// Once the user types a name, autofill stops fighting them.
this._cloneNameEdited = !!document.getElementById('cloneCaseName')?.value.trim();
this.updateCloneCasePathPreview();
},
onCloneUrlInput() {
const url = document.getElementById('cloneRepoUrl')?.value.trim() || '';
const nameInput = document.getElementById('cloneCaseName');
if (nameInput && !this._cloneNameEdited) nameInput.value = this._repoNameFromUrl(url);
this.updateCloneCasePathPreview();
clearTimeout(this._clonePreflightTimer);
this._clonePreflightAbort?.abort();
this._clonePreflightAbort = null;
@@ -4401,7 +4407,10 @@ Object.assign(CodemanApp.prototype, {
// The server's suggestion wins over the local guess (it is the same function
// the case name is validated against), but never over a name the user typed.
const nameInput = document.getElementById('cloneCaseName');
if (nameInput && !this._cloneNameEdited && parse.suggestedName) nameInput.value = parse.suggestedName;
if (nameInput && !this._cloneNameEdited && parse.suggestedName) {
nameInput.value = parse.suggestedName;
this.updateCloneCasePathPreview();
}
const where = parse.owner ? `${parse.provider} ${parse.owner}/${parse.repo}` : `${parse.provider} ${parse.repo}`;
if (data.gitAvailable === false) {
@@ -4456,7 +4465,7 @@ Object.assign(CodemanApp.prototype, {
const customFolder = !!document.getElementById('cloneCustomPathToggle')?.checked;
if (customFolder && !(document.getElementById('cloneCasePath')?.value || '').trim()) {
this.showToast('Choose the folder to clone into', 'error');
this.showToast('Choose the parent folder for the clone', 'error');
return;
}