@co0ontty/wand 1.25.5 → 1.26.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.
- package/dist/git-quick-commit.d.ts +17 -1
- package/dist/git-quick-commit.js +248 -32
- package/dist/server-session-routes.js +58 -1
- package/dist/types.d.ts +32 -0
- package/dist/web-ui/content/scripts.js +499 -47
- package/dist/web-ui/content/styles.css +285 -0
- package/package.json +1 -1
|
@@ -195,7 +195,21 @@
|
|
|
195
195
|
quickCommitSubmitting: false,
|
|
196
196
|
quickCommitGenerating: false,
|
|
197
197
|
quickCommitError: "",
|
|
198
|
-
|
|
198
|
+
// primaryAction: "commit" (commit only, no push) or "commit-push" (commit then push).
|
|
199
|
+
// Whether the commit is also tagged is controlled by `makeTag` independently —
|
|
200
|
+
// that keeps "commit + tag, push later" possible.
|
|
201
|
+
quickCommitForm: { customMessage: "", makeTag: false, tag: "", primaryAction: "commit" },
|
|
202
|
+
quickCommitActionMenuOpen: false,
|
|
203
|
+
// Secondary "tag the existing HEAD" panel (inline drawer).
|
|
204
|
+
quickCommitTagHeadOpen: false,
|
|
205
|
+
quickCommitTagHeadForm: { tag: "", push: false },
|
|
206
|
+
quickCommitTagHeadSubmitting: false,
|
|
207
|
+
quickCommitTagHeadGenerating: false,
|
|
208
|
+
quickCommitTagHeadError: "",
|
|
209
|
+
// Secondary "push only" controls.
|
|
210
|
+
quickCommitPushMenuOpen: false,
|
|
211
|
+
quickCommitPushing: false,
|
|
212
|
+
quickCommitPushError: "",
|
|
199
213
|
// Telegram 风格的"贴底"状态:true = 用户当前贴在底部,新消息会自然出现;
|
|
200
214
|
// false = 用户向上滚了,未读会累积到气泡里,不会自动滚他们的视图。
|
|
201
215
|
chatStickToBottom: true,
|
|
@@ -1777,13 +1791,40 @@
|
|
|
1777
1791
|
}
|
|
1778
1792
|
|
|
1779
1793
|
var quickCommitEscHandler = null;
|
|
1794
|
+
var quickCommitDocClickHandler = null;
|
|
1795
|
+
|
|
1796
|
+
// Restore the user's last "primary action" choice so the split button feels sticky.
|
|
1797
|
+
function readSavedPrimaryAction() {
|
|
1798
|
+
try {
|
|
1799
|
+
var v = localStorage.getItem("wand.quickCommit.primaryAction");
|
|
1800
|
+
if (v === "commit" || v === "commit-push") return v;
|
|
1801
|
+
} catch (e) { /* localStorage may be blocked */ }
|
|
1802
|
+
return "commit";
|
|
1803
|
+
}
|
|
1804
|
+
function savePrimaryAction(value) {
|
|
1805
|
+
try { localStorage.setItem("wand.quickCommit.primaryAction", value); } catch (e) { /* no-op */ }
|
|
1806
|
+
}
|
|
1780
1807
|
|
|
1781
1808
|
function openQuickCommitModal() {
|
|
1782
1809
|
if (!state.selectedId) return;
|
|
1783
1810
|
state.quickCommitOpen = true;
|
|
1784
1811
|
state.quickCommitSubmitting = false;
|
|
1785
1812
|
state.quickCommitError = "";
|
|
1786
|
-
state.quickCommitForm = {
|
|
1813
|
+
state.quickCommitForm = {
|
|
1814
|
+
customMessage: "",
|
|
1815
|
+
makeTag: false,
|
|
1816
|
+
tag: "",
|
|
1817
|
+
primaryAction: readSavedPrimaryAction(),
|
|
1818
|
+
};
|
|
1819
|
+
state.quickCommitActionMenuOpen = false;
|
|
1820
|
+
state.quickCommitTagHeadOpen = false;
|
|
1821
|
+
state.quickCommitTagHeadForm = { tag: "", push: false };
|
|
1822
|
+
state.quickCommitTagHeadSubmitting = false;
|
|
1823
|
+
state.quickCommitTagHeadGenerating = false;
|
|
1824
|
+
state.quickCommitTagHeadError = "";
|
|
1825
|
+
state.quickCommitPushMenuOpen = false;
|
|
1826
|
+
state.quickCommitPushing = false;
|
|
1827
|
+
state.quickCommitPushError = "";
|
|
1787
1828
|
closeWorktreeMergeModal();
|
|
1788
1829
|
closeSessionModal();
|
|
1789
1830
|
closeSettingsModal();
|
|
@@ -1796,11 +1837,37 @@
|
|
|
1796
1837
|
}
|
|
1797
1838
|
if (quickCommitEscHandler) document.removeEventListener("keydown", quickCommitEscHandler);
|
|
1798
1839
|
quickCommitEscHandler = function(e) {
|
|
1799
|
-
if (e.key === "Escape" && state.quickCommitOpen && !state.quickCommitSubmitting) {
|
|
1840
|
+
if (e.key === "Escape" && state.quickCommitOpen && !state.quickCommitSubmitting && !state.quickCommitTagHeadSubmitting && !state.quickCommitPushing) {
|
|
1841
|
+
// First Esc closes any open dropdown; second closes the modal.
|
|
1842
|
+
if (state.quickCommitActionMenuOpen || state.quickCommitPushMenuOpen || state.quickCommitTagHeadOpen) {
|
|
1843
|
+
state.quickCommitActionMenuOpen = false;
|
|
1844
|
+
state.quickCommitPushMenuOpen = false;
|
|
1845
|
+
state.quickCommitTagHeadOpen = false;
|
|
1846
|
+
rerenderQuickCommitModal();
|
|
1847
|
+
return;
|
|
1848
|
+
}
|
|
1800
1849
|
closeQuickCommitModal();
|
|
1801
1850
|
}
|
|
1802
1851
|
};
|
|
1803
1852
|
document.addEventListener("keydown", quickCommitEscHandler);
|
|
1853
|
+
if (quickCommitDocClickHandler) document.removeEventListener("click", quickCommitDocClickHandler, true);
|
|
1854
|
+
quickCommitDocClickHandler = function(e) {
|
|
1855
|
+
if (!state.quickCommitOpen) return;
|
|
1856
|
+
var modalEl = document.getElementById("quick-commit-modal");
|
|
1857
|
+
if (!modalEl) return;
|
|
1858
|
+
var anyMenuOpen = state.quickCommitActionMenuOpen || state.quickCommitPushMenuOpen;
|
|
1859
|
+
if (!anyMenuOpen) return;
|
|
1860
|
+
// Click on the toggle itself? leave it to the toggle handler.
|
|
1861
|
+
var t = e.target;
|
|
1862
|
+
while (t && t !== modalEl) {
|
|
1863
|
+
if (t.dataset && (t.dataset.qcDropdownToggle || t.dataset.qcDropdownMenu)) return;
|
|
1864
|
+
t = t.parentNode;
|
|
1865
|
+
}
|
|
1866
|
+
state.quickCommitActionMenuOpen = false;
|
|
1867
|
+
state.quickCommitPushMenuOpen = false;
|
|
1868
|
+
rerenderQuickCommitModal();
|
|
1869
|
+
};
|
|
1870
|
+
document.addEventListener("click", quickCommitDocClickHandler, true);
|
|
1804
1871
|
loadGitStatus(state.selectedId, { force: true }).then(function() {
|
|
1805
1872
|
if (!state.quickCommitOpen) return;
|
|
1806
1873
|
rerenderQuickCommitModal();
|
|
@@ -1811,6 +1878,9 @@
|
|
|
1811
1878
|
state.quickCommitOpen = false;
|
|
1812
1879
|
state.quickCommitSubmitting = false;
|
|
1813
1880
|
state.quickCommitError = "";
|
|
1881
|
+
state.quickCommitActionMenuOpen = false;
|
|
1882
|
+
state.quickCommitPushMenuOpen = false;
|
|
1883
|
+
state.quickCommitTagHeadOpen = false;
|
|
1814
1884
|
var modal = document.getElementById("quick-commit-modal");
|
|
1815
1885
|
if (modal) modal.classList.add("hidden");
|
|
1816
1886
|
if (focusTrapHandler) {
|
|
@@ -1821,6 +1891,10 @@
|
|
|
1821
1891
|
document.removeEventListener("keydown", quickCommitEscHandler);
|
|
1822
1892
|
quickCommitEscHandler = null;
|
|
1823
1893
|
}
|
|
1894
|
+
if (quickCommitDocClickHandler) {
|
|
1895
|
+
document.removeEventListener("click", quickCommitDocClickHandler, true);
|
|
1896
|
+
quickCommitDocClickHandler = null;
|
|
1897
|
+
}
|
|
1824
1898
|
if (lastFocusedElement && typeof lastFocusedElement.focus === "function") {
|
|
1825
1899
|
lastFocusedElement.focus();
|
|
1826
1900
|
}
|
|
@@ -1843,6 +1917,8 @@
|
|
|
1843
1917
|
if (closeBtn) closeBtn.addEventListener("click", closeQuickCommitModal);
|
|
1844
1918
|
var cancelBtn = document.getElementById("quick-commit-cancel-btn");
|
|
1845
1919
|
if (cancelBtn) cancelBtn.addEventListener("click", closeQuickCommitModal);
|
|
1920
|
+
|
|
1921
|
+
// ── Section 1: commit form ──
|
|
1846
1922
|
var submitBtn = document.getElementById("quick-commit-submit-btn");
|
|
1847
1923
|
if (submitBtn) submitBtn.addEventListener("click", submitQuickCommit);
|
|
1848
1924
|
var aiBtn = document.getElementById("quick-commit-ai-btn");
|
|
@@ -1861,10 +1937,102 @@
|
|
|
1861
1937
|
if (tagInput) tagInput.addEventListener("input", function() {
|
|
1862
1938
|
state.quickCommitForm.tag = tagInput.value;
|
|
1863
1939
|
});
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1940
|
+
|
|
1941
|
+
// ── Primary action split-button (caret + dropdown) ──
|
|
1942
|
+
var actionCaret = document.getElementById("quick-commit-action-caret");
|
|
1943
|
+
if (actionCaret) actionCaret.addEventListener("click", function(e) {
|
|
1944
|
+
e.stopPropagation();
|
|
1945
|
+
state.quickCommitActionMenuOpen = !state.quickCommitActionMenuOpen;
|
|
1946
|
+
state.quickCommitPushMenuOpen = false;
|
|
1947
|
+
rerenderQuickCommitModal();
|
|
1948
|
+
});
|
|
1949
|
+
var actionMenu = document.getElementById("quick-commit-action-menu");
|
|
1950
|
+
if (actionMenu) {
|
|
1951
|
+
var actionItems = actionMenu.querySelectorAll("[data-qc-action]");
|
|
1952
|
+
for (var i = 0; i < actionItems.length; i++) {
|
|
1953
|
+
(function(btn) {
|
|
1954
|
+
btn.addEventListener("click", function() {
|
|
1955
|
+
var value = btn.getAttribute("data-qc-action");
|
|
1956
|
+
if (value === "commit" || value === "commit-push") {
|
|
1957
|
+
state.quickCommitForm.primaryAction = value;
|
|
1958
|
+
savePrimaryAction(value);
|
|
1959
|
+
}
|
|
1960
|
+
state.quickCommitActionMenuOpen = false;
|
|
1961
|
+
rerenderQuickCommitModal();
|
|
1962
|
+
});
|
|
1963
|
+
})(actionItems[i]);
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
// ── Section 2: Tag HEAD inline panel ──
|
|
1968
|
+
var tagHeadToggle = document.getElementById("quick-commit-tag-head-toggle");
|
|
1969
|
+
if (tagHeadToggle) tagHeadToggle.addEventListener("click", function() {
|
|
1970
|
+
state.quickCommitTagHeadOpen = !state.quickCommitTagHeadOpen;
|
|
1971
|
+
if (state.quickCommitTagHeadOpen) {
|
|
1972
|
+
state.quickCommitTagHeadError = "";
|
|
1973
|
+
}
|
|
1974
|
+
rerenderQuickCommitModal();
|
|
1975
|
+
if (state.quickCommitTagHeadOpen) {
|
|
1976
|
+
var inp = document.getElementById("quick-commit-tag-head-input");
|
|
1977
|
+
if (inp) inp.focus();
|
|
1978
|
+
}
|
|
1979
|
+
});
|
|
1980
|
+
var tagHeadCancel = document.getElementById("quick-commit-tag-head-cancel");
|
|
1981
|
+
if (tagHeadCancel) tagHeadCancel.addEventListener("click", function() {
|
|
1982
|
+
state.quickCommitTagHeadOpen = false;
|
|
1983
|
+
state.quickCommitTagHeadError = "";
|
|
1984
|
+
rerenderQuickCommitModal();
|
|
1985
|
+
});
|
|
1986
|
+
var tagHeadInput = document.getElementById("quick-commit-tag-head-input");
|
|
1987
|
+
if (tagHeadInput) {
|
|
1988
|
+
tagHeadInput.addEventListener("input", function() {
|
|
1989
|
+
state.quickCommitTagHeadForm.tag = tagHeadInput.value;
|
|
1990
|
+
});
|
|
1991
|
+
tagHeadInput.addEventListener("keydown", function(e) {
|
|
1992
|
+
if (e.key === "Enter") {
|
|
1993
|
+
e.preventDefault();
|
|
1994
|
+
submitTagHead(false);
|
|
1995
|
+
}
|
|
1996
|
+
});
|
|
1997
|
+
}
|
|
1998
|
+
var tagHeadAi = document.getElementById("quick-commit-tag-head-ai");
|
|
1999
|
+
if (tagHeadAi) tagHeadAi.addEventListener("click", generateTagHeadAI);
|
|
2000
|
+
var tagHeadPushCb = document.getElementById("quick-commit-tag-head-push");
|
|
2001
|
+
if (tagHeadPushCb) tagHeadPushCb.addEventListener("change", function() {
|
|
2002
|
+
state.quickCommitTagHeadForm.push = tagHeadPushCb.checked;
|
|
2003
|
+
});
|
|
2004
|
+
var tagHeadSubmit = document.getElementById("quick-commit-tag-head-submit");
|
|
2005
|
+
if (tagHeadSubmit) tagHeadSubmit.addEventListener("click", function() {
|
|
2006
|
+
submitTagHead(false);
|
|
2007
|
+
});
|
|
2008
|
+
|
|
2009
|
+
// ── Section 2: Push split-button ──
|
|
2010
|
+
var pushBtn = document.getElementById("quick-commit-push-btn");
|
|
2011
|
+
if (pushBtn) pushBtn.addEventListener("click", function() {
|
|
2012
|
+
submitPushOnly({ pushCommits: true, pushTags: false });
|
|
2013
|
+
});
|
|
2014
|
+
var pushCaret = document.getElementById("quick-commit-push-caret");
|
|
2015
|
+
if (pushCaret) pushCaret.addEventListener("click", function(e) {
|
|
2016
|
+
e.stopPropagation();
|
|
2017
|
+
state.quickCommitPushMenuOpen = !state.quickCommitPushMenuOpen;
|
|
2018
|
+
state.quickCommitActionMenuOpen = false;
|
|
2019
|
+
rerenderQuickCommitModal();
|
|
1867
2020
|
});
|
|
2021
|
+
var pushMenu = document.getElementById("quick-commit-push-menu");
|
|
2022
|
+
if (pushMenu) {
|
|
2023
|
+
var pushItems = pushMenu.querySelectorAll("[data-qc-push]");
|
|
2024
|
+
for (var j = 0; j < pushItems.length; j++) {
|
|
2025
|
+
(function(btn) {
|
|
2026
|
+
btn.addEventListener("click", function() {
|
|
2027
|
+
var value = btn.getAttribute("data-qc-push");
|
|
2028
|
+
state.quickCommitPushMenuOpen = false;
|
|
2029
|
+
if (value === "commits") submitPushOnly({ pushCommits: true, pushTags: false });
|
|
2030
|
+
else if (value === "tags") submitPushOnly({ pushCommits: false, pushTags: true });
|
|
2031
|
+
else if (value === "both") submitPushOnly({ pushCommits: true, pushTags: true });
|
|
2032
|
+
});
|
|
2033
|
+
})(pushItems[j]);
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
1868
2036
|
}
|
|
1869
2037
|
|
|
1870
2038
|
function generateCommitMessageAI() {
|
|
@@ -1925,13 +2093,14 @@
|
|
|
1925
2093
|
rerenderQuickCommitModal();
|
|
1926
2094
|
return;
|
|
1927
2095
|
}
|
|
2096
|
+
var willPush = form.primaryAction === "commit-push";
|
|
1928
2097
|
// 开了 tag 开关但没填 → 由后端在提交时调 AI 生成
|
|
1929
2098
|
var payload = {
|
|
1930
2099
|
autoMessage: false,
|
|
1931
2100
|
customMessage: message,
|
|
1932
2101
|
tag: userTag,
|
|
1933
2102
|
autoTag: !!(form.makeTag && !userTag),
|
|
1934
|
-
push:
|
|
2103
|
+
push: willPush
|
|
1935
2104
|
};
|
|
1936
2105
|
state.quickCommitSubmitting = true;
|
|
1937
2106
|
state.quickCommitError = "";
|
|
@@ -1951,8 +2120,7 @@
|
|
|
1951
2120
|
var hash = data.commit && data.commit.hash ? data.commit.hash.substring(0, 7) : "";
|
|
1952
2121
|
var tagName = data.tag && data.tag.name ? data.tag.name : "";
|
|
1953
2122
|
var base = "已提交" + (hash ? " " + hash : "") + (tagName ? ",已打 tag " + tagName : "");
|
|
1954
|
-
|
|
1955
|
-
if (pushRequested && data.pushError) {
|
|
2123
|
+
if (willPush && data.pushError) {
|
|
1956
2124
|
var msg = base + ";push 失败:" + data.pushError;
|
|
1957
2125
|
if (typeof showToast === "function") showToast(msg, "error");
|
|
1958
2126
|
} else {
|
|
@@ -1971,13 +2139,137 @@
|
|
|
1971
2139
|
});
|
|
1972
2140
|
}
|
|
1973
2141
|
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
var
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
2142
|
+
// ── AI generate a tag for the existing HEAD (no commit involved) ──
|
|
2143
|
+
function generateTagHeadAI() {
|
|
2144
|
+
if (!state.selectedId || state.quickCommitTagHeadGenerating) return;
|
|
2145
|
+
var inp = document.getElementById("quick-commit-tag-head-input");
|
|
2146
|
+
if (inp) state.quickCommitTagHeadForm.tag = inp.value;
|
|
2147
|
+
state.quickCommitTagHeadGenerating = true;
|
|
2148
|
+
state.quickCommitTagHeadError = "";
|
|
2149
|
+
rerenderQuickCommitModal();
|
|
2150
|
+
// Reuse the existing generator — it stages and asks for {message, tag}.
|
|
2151
|
+
// We only consume `suggestedTag` here.
|
|
2152
|
+
fetch("/api/sessions/" + encodeURIComponent(state.selectedId) + "/generate-commit-message", {
|
|
2153
|
+
method: "POST",
|
|
2154
|
+
credentials: "same-origin",
|
|
2155
|
+
headers: { "Content-Type": "application/json" },
|
|
2156
|
+
body: JSON.stringify({})
|
|
2157
|
+
})
|
|
2158
|
+
.then(function(res) { return res.json().then(function(data) { return { ok: res.ok, data: data }; }); })
|
|
2159
|
+
.then(function(result) {
|
|
2160
|
+
if (!result.ok) throw new Error((result.data && result.data.error) || "AI 生成失败。");
|
|
2161
|
+
var aiTag = (result.data && typeof result.data.suggestedTag === "string") ? result.data.suggestedTag.trim() : "";
|
|
2162
|
+
var currentTag = (state.quickCommitTagHeadForm.tag || "").trim();
|
|
2163
|
+
if (!currentTag && aiTag) {
|
|
2164
|
+
state.quickCommitTagHeadForm.tag = aiTag;
|
|
2165
|
+
} else if (!aiTag) {
|
|
2166
|
+
throw new Error("AI 没有给出 tag 建议。");
|
|
2167
|
+
}
|
|
2168
|
+
})
|
|
2169
|
+
.catch(function(error) {
|
|
2170
|
+
state.quickCommitTagHeadError = (error && error.message) || "AI 生成失败。";
|
|
2171
|
+
})
|
|
2172
|
+
.finally(function() {
|
|
2173
|
+
state.quickCommitTagHeadGenerating = false;
|
|
2174
|
+
if (state.quickCommitOpen) rerenderQuickCommitModal();
|
|
2175
|
+
});
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
// Tag the existing HEAD without making a new commit.
|
|
2179
|
+
function submitTagHead(silent) {
|
|
2180
|
+
if (!state.selectedId || state.quickCommitTagHeadSubmitting) return;
|
|
2181
|
+
var inp = document.getElementById("quick-commit-tag-head-input");
|
|
2182
|
+
if (inp) state.quickCommitTagHeadForm.tag = inp.value;
|
|
2183
|
+
var tag = (state.quickCommitTagHeadForm.tag || "").trim();
|
|
2184
|
+
if (!tag) {
|
|
2185
|
+
state.quickCommitTagHeadError = "请填写 tag 名称,或点击 AI 建议。";
|
|
2186
|
+
rerenderQuickCommitModal();
|
|
2187
|
+
return;
|
|
2188
|
+
}
|
|
2189
|
+
state.quickCommitTagHeadSubmitting = true;
|
|
2190
|
+
state.quickCommitTagHeadError = "";
|
|
2191
|
+
rerenderQuickCommitModal();
|
|
2192
|
+
fetch("/api/sessions/" + encodeURIComponent(state.selectedId) + "/git/tag-head", {
|
|
2193
|
+
method: "POST",
|
|
2194
|
+
credentials: "same-origin",
|
|
2195
|
+
headers: { "Content-Type": "application/json" },
|
|
2196
|
+
body: JSON.stringify({ tag: tag, push: !!state.quickCommitTagHeadForm.push })
|
|
2197
|
+
})
|
|
2198
|
+
.then(function(res) { return res.json().then(function(data) { return { ok: res.ok, data: data }; }); })
|
|
2199
|
+
.then(function(result) {
|
|
2200
|
+
if (!result.ok) throw new Error((result.data && result.data.error) || "打 tag 失败。");
|
|
2201
|
+
var data = result.data || {};
|
|
2202
|
+
var name = data.tag && data.tag.name ? data.tag.name : tag;
|
|
2203
|
+
var pushed = !!data.pushed;
|
|
2204
|
+
var pushErr = data.pushError;
|
|
2205
|
+
var base = "已为 HEAD 打 tag " + name;
|
|
2206
|
+
if (state.quickCommitTagHeadForm.push && pushErr) {
|
|
2207
|
+
if (typeof showToast === "function") showToast(base + ";push tag 失败:" + pushErr, "error");
|
|
2208
|
+
} else {
|
|
2209
|
+
if (typeof showToast === "function") showToast(base + (pushed ? ",已 push" : ""), "success");
|
|
2210
|
+
}
|
|
2211
|
+
state.quickCommitTagHeadOpen = false;
|
|
2212
|
+
state.quickCommitTagHeadForm = { tag: "", push: false };
|
|
2213
|
+
if (state.selectedId) loadGitStatus(state.selectedId, { force: true }).then(function() {
|
|
2214
|
+
if (state.quickCommitOpen) rerenderQuickCommitModal();
|
|
2215
|
+
});
|
|
2216
|
+
})
|
|
2217
|
+
.catch(function(error) {
|
|
2218
|
+
state.quickCommitTagHeadError = (error && error.message) || "打 tag 失败。";
|
|
2219
|
+
})
|
|
2220
|
+
.finally(function() {
|
|
2221
|
+
state.quickCommitTagHeadSubmitting = false;
|
|
2222
|
+
if (state.quickCommitOpen) rerenderQuickCommitModal();
|
|
2223
|
+
});
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
// Push without committing.
|
|
2227
|
+
function submitPushOnly(opts) {
|
|
2228
|
+
if (!state.selectedId || state.quickCommitPushing) return;
|
|
2229
|
+
var pushCommits = !!(opts && opts.pushCommits);
|
|
2230
|
+
var pushTags = !!(opts && opts.pushTags);
|
|
2231
|
+
if (!pushCommits && !pushTags) return;
|
|
2232
|
+
state.quickCommitPushing = true;
|
|
2233
|
+
state.quickCommitPushError = "";
|
|
2234
|
+
rerenderQuickCommitModal();
|
|
2235
|
+
fetch("/api/sessions/" + encodeURIComponent(state.selectedId) + "/git/push", {
|
|
2236
|
+
method: "POST",
|
|
2237
|
+
credentials: "same-origin",
|
|
2238
|
+
headers: { "Content-Type": "application/json" },
|
|
2239
|
+
body: JSON.stringify({ pushCommits: pushCommits, pushTags: pushTags })
|
|
2240
|
+
})
|
|
2241
|
+
.then(function(res) { return res.json().then(function(data) { return { ok: res.ok, data: data }; }); })
|
|
2242
|
+
.then(function(result) {
|
|
2243
|
+
var data = result.data || {};
|
|
2244
|
+
if (!result.ok) throw new Error((data && data.error) || "推送失败。");
|
|
2245
|
+
// Backend marks `ok: false` on partial failure but still returns 200 — surface error toast.
|
|
2246
|
+
if (data.error) {
|
|
2247
|
+
state.quickCommitPushError = data.error;
|
|
2248
|
+
if (typeof showToast === "function") showToast("推送失败:" + data.error, "error");
|
|
2249
|
+
return;
|
|
2250
|
+
}
|
|
2251
|
+
var parts = [];
|
|
2252
|
+
if (data.pushedCommits) parts.push("commits");
|
|
2253
|
+
if (data.pushedTags) parts.push("tags");
|
|
2254
|
+
var label = parts.length ? parts.join(" 和 ") : "(无内容)";
|
|
2255
|
+
if (typeof showToast === "function") showToast("已推送 " + label, "success");
|
|
2256
|
+
if (state.selectedId) loadGitStatus(state.selectedId, { force: true }).then(function() {
|
|
2257
|
+
if (state.quickCommitOpen) rerenderQuickCommitModal();
|
|
2258
|
+
});
|
|
2259
|
+
})
|
|
2260
|
+
.catch(function(error) {
|
|
2261
|
+
state.quickCommitPushError = (error && error.message) || "推送失败。";
|
|
2262
|
+
if (typeof showToast === "function") showToast(state.quickCommitPushError, "error");
|
|
2263
|
+
})
|
|
2264
|
+
.finally(function() {
|
|
2265
|
+
state.quickCommitPushing = false;
|
|
2266
|
+
if (state.quickCommitOpen) rerenderQuickCommitModal();
|
|
2267
|
+
});
|
|
2268
|
+
}
|
|
2269
|
+
|
|
2270
|
+
// Build the file list portion of the modal.
|
|
2271
|
+
function renderQuickCommitFileRows(files) {
|
|
2272
|
+
var rows = files.map(function(item) {
|
|
1981
2273
|
var status = (item.status || " ").substring(0, 2);
|
|
1982
2274
|
var flag = status.trim() || "?";
|
|
1983
2275
|
var cls = "qc-flag";
|
|
@@ -1998,48 +2290,208 @@
|
|
|
1998
2290
|
}
|
|
1999
2291
|
return '<div class="qc-file-row"><span class="' + cls + '">' + escapeHtml(status) + '</span><span class="qc-file-path">' + escapeHtml(item.path || "") + '</span>' + subBadge + '</div>';
|
|
2000
2292
|
}).join("");
|
|
2001
|
-
|
|
2293
|
+
return rows || '<div class="qc-empty">没有可提交的改动。</div>';
|
|
2294
|
+
}
|
|
2295
|
+
|
|
2296
|
+
// Primary action button label / icon.
|
|
2297
|
+
function renderQuickCommitPrimary(hasChanges) {
|
|
2298
|
+
var f = state.quickCommitForm;
|
|
2299
|
+
var label;
|
|
2300
|
+
if (state.quickCommitSubmitting) label = "提交中…";
|
|
2301
|
+
else if (f.primaryAction === "commit-push") label = "提交并 push";
|
|
2302
|
+
else label = "仅提交";
|
|
2303
|
+
var disabled = !hasChanges || state.quickCommitSubmitting || state.quickCommitTagHeadSubmitting || state.quickCommitPushing;
|
|
2304
|
+
var caretActive = state.quickCommitActionMenuOpen ? " is-active" : "";
|
|
2305
|
+
var menuItems = [
|
|
2306
|
+
{ value: "commit", label: "仅提交", desc: "只 commit,不 push" },
|
|
2307
|
+
{ value: "commit-push", label: "提交并 push", desc: "commit 后立即 push 到远端" }
|
|
2308
|
+
];
|
|
2309
|
+
var menuHtml = menuItems.map(function(item) {
|
|
2310
|
+
var sel = f.primaryAction === item.value ? " is-selected" : "";
|
|
2311
|
+
return '<button type="button" class="qc-dropdown-item' + sel + '" data-qc-action="' + item.value + '">' +
|
|
2312
|
+
'<span class="qc-dropdown-item-title">' + escapeHtml(item.label) + '</span>' +
|
|
2313
|
+
'<span class="qc-dropdown-item-desc">' + escapeHtml(item.desc) + '</span>' +
|
|
2314
|
+
'</button>';
|
|
2315
|
+
}).join("");
|
|
2316
|
+
return '<div class="qc-split-button">' +
|
|
2317
|
+
'<button id="quick-commit-submit-btn" class="btn btn-primary qc-split-main" type="button"' + (disabled ? ' disabled' : '') + '>' +
|
|
2318
|
+
escapeHtml(label) +
|
|
2319
|
+
'</button>' +
|
|
2320
|
+
'<button id="quick-commit-action-caret" class="btn btn-primary qc-split-caret' + caretActive + '" type="button" data-qc-dropdown-toggle="action"' + (disabled ? ' disabled' : '') + ' aria-haspopup="menu" aria-expanded="' + (state.quickCommitActionMenuOpen ? 'true' : 'false') + '" aria-label="更多提交方式">' +
|
|
2321
|
+
'<svg viewBox="0 0 12 12" width="10" height="10" aria-hidden="true"><path d="M2 4l4 4 4-4" stroke="currentColor" stroke-width="1.6" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>' +
|
|
2322
|
+
'</button>' +
|
|
2323
|
+
(state.quickCommitActionMenuOpen ?
|
|
2324
|
+
'<div id="quick-commit-action-menu" class="qc-dropdown-menu" data-qc-dropdown-menu="action" role="menu">' + menuHtml + '</div>' : '') +
|
|
2325
|
+
'</div>';
|
|
2326
|
+
}
|
|
2327
|
+
|
|
2328
|
+
// Status chips (ahead/behind/unpushed tags) under the HEAD card.
|
|
2329
|
+
function renderQuickCommitStatusChips(s) {
|
|
2330
|
+
var chips = [];
|
|
2331
|
+
if (typeof s.ahead === "number" && s.ahead > 0) {
|
|
2332
|
+
chips.push('<span class="qc-chip qc-chip--ahead" title="本地领先 ' + s.ahead + ' 个 commit">↑ ' + s.ahead + ' 待推送</span>');
|
|
2333
|
+
}
|
|
2334
|
+
if (typeof s.behind === "number" && s.behind > 0) {
|
|
2335
|
+
chips.push('<span class="qc-chip qc-chip--behind" title="远端领先 ' + s.behind + ' 个 commit">↓ ' + s.behind + ' 待拉取</span>');
|
|
2336
|
+
}
|
|
2337
|
+
if (typeof s.unpushedTagCount === "number" && s.unpushedTagCount > 0) {
|
|
2338
|
+
chips.push('<span class="qc-chip qc-chip--tag" title="' + s.unpushedTagCount + ' 个本地 tag 未推送">🏷 ' + s.unpushedTagCount + ' 待推送</span>');
|
|
2339
|
+
}
|
|
2340
|
+
if (s.hasUpstream === false) {
|
|
2341
|
+
chips.push('<span class="qc-chip qc-chip--warn" title="当前分支没有 upstream,将首次推送时自动设置">无 upstream</span>');
|
|
2342
|
+
}
|
|
2343
|
+
if (!chips.length && s.hasUpstream !== false) {
|
|
2344
|
+
chips.push('<span class="qc-chip qc-chip--clean">与远端同步</span>');
|
|
2345
|
+
}
|
|
2346
|
+
return '<div class="qc-status-chips">' + chips.join("") + '</div>';
|
|
2347
|
+
}
|
|
2348
|
+
|
|
2349
|
+
// Inline "tag HEAD" drawer rendering.
|
|
2350
|
+
function renderQuickCommitTagHeadPanel() {
|
|
2351
|
+
var thf = state.quickCommitTagHeadForm || { tag: "", push: false };
|
|
2352
|
+
var submitting = state.quickCommitTagHeadSubmitting;
|
|
2353
|
+
var generating = state.quickCommitTagHeadGenerating;
|
|
2354
|
+
return '<div class="qc-tag-head-panel">' +
|
|
2355
|
+
'<div class="qc-tag-head-row">' +
|
|
2356
|
+
'<input type="text" id="quick-commit-tag-head-input" class="field-input" placeholder="输入 tag 名(例如 v1.2.0),或点 AI 建议" value="' + escapeHtml(thf.tag || "") + '"' + (submitting ? ' disabled' : '') + '>' +
|
|
2357
|
+
'<button type="button" id="quick-commit-tag-head-ai" class="btn btn-ghost btn-sm"' + (generating || submitting ? ' disabled' : '') + '>' + (generating ? '建议中…' : 'AI 建议') + '</button>' +
|
|
2358
|
+
'</div>' +
|
|
2359
|
+
'<label class="qc-tag-head-push">' +
|
|
2360
|
+
'<input type="checkbox" id="quick-commit-tag-head-push"' + (thf.push ? ' checked' : '') + (submitting ? ' disabled' : '') + '>' +
|
|
2361
|
+
'<span>打完 tag 立即推送这个 tag</span>' +
|
|
2362
|
+
'</label>' +
|
|
2363
|
+
(state.quickCommitTagHeadError ? '<p class="error-message">' + escapeHtml(state.quickCommitTagHeadError) + '</p>' : '') +
|
|
2364
|
+
'<div class="qc-tag-head-actions">' +
|
|
2365
|
+
'<button type="button" id="quick-commit-tag-head-cancel" class="btn btn-ghost btn-sm"' + (submitting ? ' disabled' : '') + '>收起</button>' +
|
|
2366
|
+
'<button type="button" id="quick-commit-tag-head-submit" class="btn btn-secondary btn-sm"' + (submitting ? ' disabled' : '') + '>' + (submitting ? '打 tag 中…' : '为 HEAD 打 tag') + '</button>' +
|
|
2367
|
+
'</div>' +
|
|
2368
|
+
'</div>';
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
// Push split-button on the secondary row.
|
|
2372
|
+
function renderQuickCommitPushButton(s) {
|
|
2373
|
+
var ahead = typeof s.ahead === "number" ? s.ahead : 0;
|
|
2374
|
+
var unpushedTags = typeof s.unpushedTagCount === "number" ? s.unpushedTagCount : 0;
|
|
2375
|
+
var canPushCommits = ahead > 0 || s.hasUpstream === false; // first-push lets you set upstream
|
|
2376
|
+
var canPushTags = unpushedTags > 0;
|
|
2377
|
+
// Allow pushing commits even without ahead info (e.g. ls-remote unreachable) — backend will report.
|
|
2378
|
+
if (typeof s.ahead !== "number" && s.hasUpstream !== false) canPushCommits = true;
|
|
2379
|
+
if (typeof s.unpushedTagCount !== "number") canPushTags = true; // unknown — let user try
|
|
2380
|
+
var disabled = state.quickCommitPushing || state.quickCommitSubmitting || state.quickCommitTagHeadSubmitting || (!canPushCommits && !canPushTags);
|
|
2381
|
+
var mainLabel = state.quickCommitPushing ? "推送中…" : "推送";
|
|
2382
|
+
var caretActive = state.quickCommitPushMenuOpen ? " is-active" : "";
|
|
2383
|
+
var items = [];
|
|
2384
|
+
items.push({ value: "commits", label: "推送 commits", desc: ahead ? "推送 " + ahead + " 个 commit" : "推送当前分支", disabled: !canPushCommits });
|
|
2385
|
+
items.push({ value: "tags", label: "推送 tags", desc: unpushedTags ? "推送 " + unpushedTags + " 个本地 tag" : "推送所有本地 tag", disabled: !canPushTags });
|
|
2386
|
+
items.push({ value: "both", label: "推送 commits 和 tags", desc: "二合一", disabled: !(canPushCommits || canPushTags) });
|
|
2387
|
+
var menuHtml = items.map(function(it) {
|
|
2388
|
+
var dis = it.disabled ? " is-disabled" : "";
|
|
2389
|
+
return '<button type="button" class="qc-dropdown-item' + dis + '" data-qc-push="' + it.value + '"' + (it.disabled ? ' disabled' : '') + '>' +
|
|
2390
|
+
'<span class="qc-dropdown-item-title">' + escapeHtml(it.label) + '</span>' +
|
|
2391
|
+
'<span class="qc-dropdown-item-desc">' + escapeHtml(it.desc) + '</span>' +
|
|
2392
|
+
'</button>';
|
|
2393
|
+
}).join("");
|
|
2394
|
+
return '<div class="qc-split-button qc-split-button--secondary">' +
|
|
2395
|
+
'<button id="quick-commit-push-btn" class="btn btn-secondary qc-split-main" type="button"' + (disabled ? ' disabled' : '') + '>' +
|
|
2396
|
+
escapeHtml(mainLabel) +
|
|
2397
|
+
'</button>' +
|
|
2398
|
+
'<button id="quick-commit-push-caret" class="btn btn-secondary qc-split-caret' + caretActive + '" type="button" data-qc-dropdown-toggle="push"' + (disabled ? ' disabled' : '') + ' aria-haspopup="menu" aria-expanded="' + (state.quickCommitPushMenuOpen ? 'true' : 'false') + '" aria-label="更多推送方式">' +
|
|
2399
|
+
'<svg viewBox="0 0 12 12" width="10" height="10" aria-hidden="true"><path d="M2 4l4 4 4-4" stroke="currentColor" stroke-width="1.6" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>' +
|
|
2400
|
+
'</button>' +
|
|
2401
|
+
(state.quickCommitPushMenuOpen ?
|
|
2402
|
+
'<div id="quick-commit-push-menu" class="qc-dropdown-menu qc-dropdown-menu--right" data-qc-dropdown-menu="push" role="menu">' + menuHtml + '</div>' : '') +
|
|
2403
|
+
'</div>';
|
|
2404
|
+
}
|
|
2405
|
+
|
|
2406
|
+
function renderQuickCommitModal() {
|
|
2407
|
+
var s = state.gitStatus || {};
|
|
2408
|
+
var f = state.quickCommitForm || { customMessage: "", makeTag: false, tag: "", primaryAction: "commit" };
|
|
2002
2409
|
var hasChanges = (s.modifiedCount || 0) > 0;
|
|
2410
|
+
var files = Array.isArray(s.files) ? s.files : [];
|
|
2411
|
+
var fileRows = renderQuickCommitFileRows(files);
|
|
2412
|
+
|
|
2413
|
+
// Subtitle: branch · N 改动 · ↑X ↓Y
|
|
2414
|
+
var subParts = [];
|
|
2415
|
+
subParts.push(s.branch || "(no branch)");
|
|
2416
|
+
subParts.push((s.modifiedCount || 0) + " 个改动");
|
|
2417
|
+
if (typeof s.ahead === "number" && s.ahead > 0) subParts.push("↑" + s.ahead);
|
|
2418
|
+
if (typeof s.behind === "number" && s.behind > 0) subParts.push("↓" + s.behind);
|
|
2419
|
+
|
|
2420
|
+
// Section 1: changes + commit form (only when there are changes)
|
|
2421
|
+
var section1 = "";
|
|
2422
|
+
if (hasChanges) {
|
|
2423
|
+
section1 = '<section class="qc-section qc-section--changes">' +
|
|
2424
|
+
'<div class="qc-section-head"><span class="qc-section-title">暂存改动</span><span class="qc-section-meta">' + escapeHtml(s.modifiedCount + " 个文件") + '</span></div>' +
|
|
2425
|
+
'<div class="qc-files-wrap">' + fileRows + '</div>' +
|
|
2426
|
+
'<div class="qc-message-row" id="quick-commit-message-row">' +
|
|
2427
|
+
'<div class="qc-message-header"><label class="field-label" for="quick-commit-message">commit message</label>' +
|
|
2428
|
+
'<button type="button" id="quick-commit-ai-btn" class="btn btn-ghost btn-sm"' + (state.quickCommitGenerating ? ' disabled' : '') + '>' + (state.quickCommitGenerating ? '生成中…' : 'AI 生成') + '</button>' +
|
|
2429
|
+
'</div>' +
|
|
2430
|
+
'<textarea id="quick-commit-message" class="field-input" rows="2" placeholder="输入 commit message 或点击 AI 生成"' + (state.quickCommitSubmitting ? ' disabled' : '') + '>' + escapeHtml(f.customMessage || "") + '</textarea>' +
|
|
2431
|
+
'</div>' +
|
|
2432
|
+
'<div class="qc-checkbox-row">' +
|
|
2433
|
+
'<label class="qc-checkbox-label" for="quick-commit-make-tag">同时为本次 commit 打 tag</label>' +
|
|
2434
|
+
'<label class="qc-switch">' +
|
|
2435
|
+
'<input type="checkbox" id="quick-commit-make-tag" class="switch-toggle"' + (f.makeTag ? ' checked' : '') + '>' +
|
|
2436
|
+
'<span class="switch-slider"></span>' +
|
|
2437
|
+
'</label>' +
|
|
2438
|
+
'</div>' +
|
|
2439
|
+
'<div class="qc-tag-row' + (f.makeTag ? '' : ' hidden') + '" id="quick-commit-tag-row">' +
|
|
2440
|
+
'<input type="text" id="quick-commit-tag" class="field-input" placeholder="输入 tag 名称;留空将由 AI 在提交时自动生成" value="' + escapeHtml(f.tag || "") + '"' + (state.quickCommitSubmitting ? ' disabled' : '') + '>' +
|
|
2441
|
+
'</div>' +
|
|
2442
|
+
(state.quickCommitError ? '<p class="error-message">' + escapeHtml(state.quickCommitError) + '</p>' : '') +
|
|
2443
|
+
'<div class="qc-section-actions">' +
|
|
2444
|
+
'<button id="quick-commit-cancel-btn" class="btn btn-ghost btn-sm" type="button">取消</button>' +
|
|
2445
|
+
renderQuickCommitPrimary(hasChanges) +
|
|
2446
|
+
'</div>' +
|
|
2447
|
+
'</section>';
|
|
2448
|
+
} else {
|
|
2449
|
+
section1 = '<section class="qc-section qc-section--empty">' +
|
|
2450
|
+
'<div class="qc-empty-state">' +
|
|
2451
|
+
'<span class="qc-empty-icon">✓</span>' +
|
|
2452
|
+
'<div><div class="qc-empty-title">工作区干净</div><div class="qc-empty-sub">没有暂存改动可提交。仍可在下方为 HEAD 打 tag 或推送。</div></div>' +
|
|
2453
|
+
'</div>' +
|
|
2454
|
+
'</section>';
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2457
|
+
// Section 2: repo status + secondary actions (always show when there's at least one commit)
|
|
2458
|
+
var section2 = "";
|
|
2459
|
+
if (!s.initialCommit && s.isGit !== false) {
|
|
2460
|
+
var lc = s.lastCommit || {};
|
|
2461
|
+
var headLine = lc.shortHash ? lc.shortHash + " · " + (lc.subject || "") : (s.head ? s.head.substring(0, 7) : "(no commit)");
|
|
2462
|
+
var upstreamLine = s.upstream ? escapeHtml(s.branch || "") + " → " + escapeHtml(s.upstream) : escapeHtml(s.branch || "(no branch)") + " · 无 upstream";
|
|
2463
|
+
section2 = '<section class="qc-section qc-section--repo">' +
|
|
2464
|
+
'<div class="qc-section-head"><span class="qc-section-title">仓库状态</span><span class="qc-section-meta">' + upstreamLine + '</span></div>' +
|
|
2465
|
+
'<div class="qc-head-card">' +
|
|
2466
|
+
'<span class="qc-head-label">HEAD</span>' +
|
|
2467
|
+
'<code class="qc-head-text">' + escapeHtml(headLine) + '</code>' +
|
|
2468
|
+
'</div>' +
|
|
2469
|
+
renderQuickCommitStatusChips(s) +
|
|
2470
|
+
(state.quickCommitTagHeadOpen ? renderQuickCommitTagHeadPanel() : '') +
|
|
2471
|
+
'<div class="qc-section-actions qc-section-actions--secondary">' +
|
|
2472
|
+
'<button id="quick-commit-tag-head-toggle" class="btn btn-ghost btn-sm" type="button"' + (state.quickCommitPushing ? ' disabled' : '') + '>' +
|
|
2473
|
+
'<svg viewBox="0 0 16 16" width="14" height="14" aria-hidden="true" style="vertical-align:-2px;margin-right:4px;"><path d="M2 2h6.5l5 5-5.5 5.5L2 7.5V2z" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linejoin="round"/><circle cx="5" cy="5" r="1" fill="currentColor"/></svg>' +
|
|
2474
|
+
(state.quickCommitTagHeadOpen ? '收起' : '为 HEAD 打 tag') +
|
|
2475
|
+
'</button>' +
|
|
2476
|
+
renderQuickCommitPushButton(s) +
|
|
2477
|
+
'</div>' +
|
|
2478
|
+
'</section>';
|
|
2479
|
+
}
|
|
2480
|
+
|
|
2481
|
+
var subtitleHtml = subParts.map(escapeHtml).join(" · ");
|
|
2003
2482
|
|
|
2004
2483
|
return '<section id="quick-commit-modal" class="modal-backdrop' + (state.quickCommitOpen ? '' : ' hidden') + '">' +
|
|
2005
2484
|
'<div class="modal quick-commit-modal" role="dialog" aria-labelledby="quick-commit-title">' +
|
|
2006
2485
|
'<div class="modal-header">' +
|
|
2007
2486
|
'<div>' +
|
|
2008
2487
|
'<h2 id="quick-commit-title" class="modal-title">快捷提交</h2>' +
|
|
2009
|
-
'<p class="modal-subtitle">' +
|
|
2488
|
+
'<p class="modal-subtitle">' + subtitleHtml + '</p>' +
|
|
2010
2489
|
'</div>' +
|
|
2011
2490
|
'<button id="quick-commit-close-btn" class="btn btn-ghost btn-icon modal-close-btn" type="button" aria-label="关闭"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" aria-hidden="true"><line x1="6" y1="6" x2="18" y2="18"/><line x1="18" y1="6" x2="6" y2="18"/></svg></button>' +
|
|
2012
2491
|
'</div>' +
|
|
2013
2492
|
'<div class="modal-body">' +
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
'<div class="qc-message-header"><label class="field-label" for="quick-commit-message">commit message</label>' +
|
|
2017
|
-
'<button type="button" id="quick-commit-ai-btn" class="btn btn-ghost btn-sm"' + (state.quickCommitGenerating ? ' disabled' : '') + '>' + (state.quickCommitGenerating ? '生成中…' : 'AI 生成') + '</button>' +
|
|
2018
|
-
'</div>' +
|
|
2019
|
-
'<textarea id="quick-commit-message" class="field-input" rows="2" placeholder="输入 commit message 或点击 AI 生成">' + escapeHtml(f.customMessage || "") + '</textarea>' +
|
|
2020
|
-
'</div>' +
|
|
2021
|
-
'<div class="qc-checkbox-row">' +
|
|
2022
|
-
'<label class="qc-checkbox-label" for="quick-commit-make-tag">提交后打 tag</label>' +
|
|
2023
|
-
'<label class="qc-switch">' +
|
|
2024
|
-
'<input type="checkbox" id="quick-commit-make-tag" class="switch-toggle"' + (f.makeTag ? ' checked' : '') + '>' +
|
|
2025
|
-
'<span class="switch-slider"></span>' +
|
|
2026
|
-
'</label>' +
|
|
2027
|
-
'</div>' +
|
|
2028
|
-
'<div class="qc-tag-row' + (f.makeTag ? '' : ' hidden') + '" id="quick-commit-tag-row">' +
|
|
2029
|
-
'<input type="text" id="quick-commit-tag" class="field-input" placeholder="输入 tag 名称;留空将由 AI 在提交时自动生成" value="' + escapeHtml(f.tag || "") + '">' +
|
|
2030
|
-
'</div>' +
|
|
2031
|
-
'<div class="qc-checkbox-row">' +
|
|
2032
|
-
'<label class="qc-checkbox-label" for="quick-commit-push">提交后 push 到远端</label>' +
|
|
2033
|
-
'<label class="qc-switch">' +
|
|
2034
|
-
'<input type="checkbox" id="quick-commit-push" class="switch-toggle"' + (f.push ? ' checked' : '') + '>' +
|
|
2035
|
-
'<span class="switch-slider"></span>' +
|
|
2036
|
-
'</label>' +
|
|
2037
|
-
'</div>' +
|
|
2038
|
-
'<p id="quick-commit-error" class="error-message' + (state.quickCommitError ? '' : ' hidden') + '">' + escapeHtml(state.quickCommitError || "") + '</p>' +
|
|
2039
|
-
'<div class="worktree-merge-actions">' +
|
|
2040
|
-
'<button id="quick-commit-cancel-btn" class="btn btn-secondary" type="button">取消</button>' +
|
|
2041
|
-
'<button id="quick-commit-submit-btn" class="btn btn-primary" type="button"' + (hasChanges && !state.quickCommitSubmitting ? '' : ' disabled') + '>' + (state.quickCommitSubmitting ? '提交中…' : '执行') + '</button>' +
|
|
2042
|
-
'</div>' +
|
|
2493
|
+
section1 +
|
|
2494
|
+
section2 +
|
|
2043
2495
|
'</div>' +
|
|
2044
2496
|
'</div>' +
|
|
2045
2497
|
'</section>';
|