@co0ontty/wand 1.42.0 → 1.43.1
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.
|
@@ -284,19 +284,11 @@
|
|
|
284
284
|
quickCommitSubmitting: false,
|
|
285
285
|
quickCommitGenerating: false,
|
|
286
286
|
quickCommitError: "",
|
|
287
|
-
|
|
288
|
-
// Pushing is a separate, standalone action — never bundled into the commit button.
|
|
289
|
-
quickCommitForm: { customMessage: "", tag: "", tagEdited: false, commitMode: "commit-tag" },
|
|
290
|
-
// Which inline panel/dropdown is open. Only one can be open at a time, so a
|
|
291
|
-
// single field beats juggling three sibling booleans with mutual-exclusion code.
|
|
292
|
-
// Values: null | "action" | "push" | "tag-head".
|
|
293
|
-
quickCommitOpenMenu: null,
|
|
294
|
-
quickCommitTagHeadForm: { tag: "", push: false },
|
|
295
|
-
quickCommitTagHeadSubmitting: false,
|
|
296
|
-
quickCommitTagHeadGenerating: false,
|
|
297
|
-
quickCommitTagHeadError: "",
|
|
287
|
+
quickCommitForm: { customMessage: "", tag: "", tagEdited: false },
|
|
298
288
|
quickCommitPushing: false,
|
|
299
289
|
quickCommitPushError: "",
|
|
290
|
+
quickCommitResult: null,
|
|
291
|
+
quickCommitDragAction: "commit",
|
|
300
292
|
// Telegram 风格的"贴底"状态:true = 用户当前贴在底部,新消息会自然出现;
|
|
301
293
|
// false = 用户向上滚了,未读会累积到气泡里,不会自动滚他们的视图。
|
|
302
294
|
chatStickToBottom: true,
|
|
@@ -2249,41 +2241,99 @@
|
|
|
2249
2241
|
}
|
|
2250
2242
|
|
|
2251
2243
|
var quickCommitEscHandler = null;
|
|
2252
|
-
var
|
|
2244
|
+
var quickCommitDragCleanup = null;
|
|
2245
|
+
var quickCommitDragState = null;
|
|
2246
|
+
|
|
2247
|
+
function normalizeQuickCommitAction(value) {
|
|
2248
|
+
if (
|
|
2249
|
+
value === "commit-tag" ||
|
|
2250
|
+
value === "commit-tag-push" ||
|
|
2251
|
+
value === "commit-push"
|
|
2252
|
+
) return value;
|
|
2253
|
+
return "commit";
|
|
2254
|
+
}
|
|
2255
|
+
|
|
2256
|
+
function getQuickCommitActionMeta(action) {
|
|
2257
|
+
action = normalizeQuickCommitAction(action);
|
|
2258
|
+
if (action === "commit-tag-push") {
|
|
2259
|
+
return {
|
|
2260
|
+
action: action,
|
|
2261
|
+
label: "Commit + Tag + Push",
|
|
2262
|
+
verb: "提交、打 Tag 并推送",
|
|
2263
|
+
withTag: true,
|
|
2264
|
+
push: true,
|
|
2265
|
+
tone: "all",
|
|
2266
|
+
};
|
|
2267
|
+
}
|
|
2268
|
+
if (action === "commit-tag") {
|
|
2269
|
+
return {
|
|
2270
|
+
action: action,
|
|
2271
|
+
label: "Commit + Tag",
|
|
2272
|
+
verb: "提交并打 Tag",
|
|
2273
|
+
withTag: true,
|
|
2274
|
+
push: false,
|
|
2275
|
+
tone: "tag",
|
|
2276
|
+
};
|
|
2277
|
+
}
|
|
2278
|
+
if (action === "commit-push") {
|
|
2279
|
+
return {
|
|
2280
|
+
action: action,
|
|
2281
|
+
label: "Commit + Push",
|
|
2282
|
+
verb: "提交并推送",
|
|
2283
|
+
withTag: false,
|
|
2284
|
+
push: true,
|
|
2285
|
+
tone: "push",
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
2288
|
+
return {
|
|
2289
|
+
action: "commit",
|
|
2290
|
+
label: "Commit",
|
|
2291
|
+
verb: "仅提交",
|
|
2292
|
+
withTag: false,
|
|
2293
|
+
push: false,
|
|
2294
|
+
tone: "commit",
|
|
2295
|
+
};
|
|
2296
|
+
}
|
|
2253
2297
|
|
|
2254
|
-
//
|
|
2255
|
-
//
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
return "
|
|
2298
|
+
// Knob starts at LEFT (Commit). Three zones, left → right:
|
|
2299
|
+
// commit (0.00..0.32) — starting position
|
|
2300
|
+
// tag (0.32..0.68) — dwell here ~600ms to "arm" the Tag latch
|
|
2301
|
+
// push (0.68..1.00) — release here to push
|
|
2302
|
+
function getQuickCommitZoneFromRatio(ratio) {
|
|
2303
|
+
if (ratio <= 0.32) return "commit";
|
|
2304
|
+
if (ratio >= 0.68) return "push";
|
|
2305
|
+
return "tag";
|
|
2262
2306
|
}
|
|
2263
|
-
|
|
2264
|
-
|
|
2307
|
+
|
|
2308
|
+
// tagArmed is only true if the user dwelled long enough in the Tag zone.
|
|
2309
|
+
// Sliding straight through Tag → Push without dwelling = commit-push (skip Tag).
|
|
2310
|
+
function composeQuickCommitAction(zone, tagArmed) {
|
|
2311
|
+
if (zone === "push") return tagArmed ? "commit-tag-push" : "commit-push";
|
|
2312
|
+
if (zone === "tag") return "commit-tag";
|
|
2313
|
+
return "commit";
|
|
2265
2314
|
}
|
|
2266
2315
|
|
|
2316
|
+
// How long the user must hold the knob inside the Tag zone before the Tag latch arms.
|
|
2317
|
+
// Keep in sync with `--qc-dwell-ms` in styles.css.
|
|
2318
|
+
var QUICK_COMMIT_TAG_DWELL_MS = 1100;
|
|
2319
|
+
var QUICK_COMMIT_ZONE_RANK = { commit: 0, tag: 1, push: 2 };
|
|
2320
|
+
|
|
2267
2321
|
function openQuickCommitModal() {
|
|
2268
2322
|
if (!state.selectedId) return;
|
|
2269
2323
|
state.quickCommitOpen = true;
|
|
2270
2324
|
state.quickCommitSubmitting = false;
|
|
2325
|
+
state.quickCommitAutoGenerating = false;
|
|
2271
2326
|
state.quickCommitError = "";
|
|
2272
2327
|
state.quickCommitForm = {
|
|
2273
2328
|
customMessage: "",
|
|
2274
2329
|
tag: "",
|
|
2275
2330
|
// Whether the user has manually edited the tag (so we stop auto-overwriting it).
|
|
2276
2331
|
tagEdited: false,
|
|
2277
|
-
// "commit-tag" → commit + version tag; "commit" → commit only.
|
|
2278
|
-
commitMode: readSavedCommitMode(),
|
|
2279
2332
|
};
|
|
2280
|
-
state.quickCommitOpenMenu = null;
|
|
2281
|
-
state.quickCommitTagHeadForm = { tag: "", push: false };
|
|
2282
|
-
state.quickCommitTagHeadSubmitting = false;
|
|
2283
|
-
state.quickCommitTagHeadGenerating = false;
|
|
2284
|
-
state.quickCommitTagHeadError = "";
|
|
2285
2333
|
state.quickCommitPushing = false;
|
|
2286
2334
|
state.quickCommitPushError = "";
|
|
2335
|
+
state.quickCommitResult = null;
|
|
2336
|
+
state.quickCommitDragAction = "commit";
|
|
2287
2337
|
closeWorktreeMergeModal();
|
|
2288
2338
|
closeSessionModal();
|
|
2289
2339
|
closeSettingsModal();
|
|
@@ -2296,33 +2346,11 @@
|
|
|
2296
2346
|
}
|
|
2297
2347
|
if (quickCommitEscHandler) document.removeEventListener("keydown", quickCommitEscHandler);
|
|
2298
2348
|
quickCommitEscHandler = function(e) {
|
|
2299
|
-
if (e.key === "Escape" && state.quickCommitOpen && !state.quickCommitSubmitting && !state.
|
|
2300
|
-
// First Esc closes any open dropdown; second closes the modal.
|
|
2301
|
-
if (state.quickCommitOpenMenu) {
|
|
2302
|
-
state.quickCommitOpenMenu = null;
|
|
2303
|
-
rerenderQuickCommitModal();
|
|
2304
|
-
return;
|
|
2305
|
-
}
|
|
2349
|
+
if (e.key === "Escape" && state.quickCommitOpen && !state.quickCommitSubmitting && !state.quickCommitPushing) {
|
|
2306
2350
|
closeQuickCommitModal();
|
|
2307
2351
|
}
|
|
2308
2352
|
};
|
|
2309
2353
|
document.addEventListener("keydown", quickCommitEscHandler);
|
|
2310
|
-
if (quickCommitDocClickHandler) document.removeEventListener("click", quickCommitDocClickHandler, true);
|
|
2311
|
-
quickCommitDocClickHandler = function(e) {
|
|
2312
|
-
if (!state.quickCommitOpen) return;
|
|
2313
|
-
// tag-head is an inline drawer, not a dropdown — clicking outside shouldn't close it.
|
|
2314
|
-
if (state.quickCommitOpenMenu !== "action" && state.quickCommitOpenMenu !== "push") return;
|
|
2315
|
-
var modalEl = document.getElementById("quick-commit-modal");
|
|
2316
|
-
if (!modalEl) return;
|
|
2317
|
-
var t = e.target;
|
|
2318
|
-
while (t && t !== modalEl) {
|
|
2319
|
-
if (t.dataset && (t.dataset.qcDropdownToggle || t.dataset.qcDropdownMenu)) return;
|
|
2320
|
-
t = t.parentNode;
|
|
2321
|
-
}
|
|
2322
|
-
state.quickCommitOpenMenu = null;
|
|
2323
|
-
rerenderQuickCommitModal();
|
|
2324
|
-
};
|
|
2325
|
-
document.addEventListener("click", quickCommitDocClickHandler, true);
|
|
2326
2354
|
loadGitStatus(state.selectedId, { force: true }).then(function() {
|
|
2327
2355
|
if (!state.quickCommitOpen) return;
|
|
2328
2356
|
// Seed the tag field with the locally-derived suggestion so a tag is
|
|
@@ -2339,7 +2367,8 @@
|
|
|
2339
2367
|
state.quickCommitOpen = false;
|
|
2340
2368
|
state.quickCommitSubmitting = false;
|
|
2341
2369
|
state.quickCommitError = "";
|
|
2342
|
-
state.
|
|
2370
|
+
state.quickCommitResult = null;
|
|
2371
|
+
state.quickCommitDragAction = "commit";
|
|
2343
2372
|
var modal = document.getElementById("quick-commit-modal");
|
|
2344
2373
|
if (modal) modal.classList.add("hidden");
|
|
2345
2374
|
if (focusTrapHandler) {
|
|
@@ -2350,9 +2379,9 @@
|
|
|
2350
2379
|
document.removeEventListener("keydown", quickCommitEscHandler);
|
|
2351
2380
|
quickCommitEscHandler = null;
|
|
2352
2381
|
}
|
|
2353
|
-
if (
|
|
2354
|
-
|
|
2355
|
-
|
|
2382
|
+
if (quickCommitDragCleanup) {
|
|
2383
|
+
quickCommitDragCleanup();
|
|
2384
|
+
quickCommitDragCleanup = null;
|
|
2356
2385
|
}
|
|
2357
2386
|
if (lastFocusedElement && typeof lastFocusedElement.focus === "function") {
|
|
2358
2387
|
lastFocusedElement.focus();
|
|
@@ -2362,6 +2391,10 @@
|
|
|
2362
2391
|
function rerenderQuickCommitModal() {
|
|
2363
2392
|
var modal = document.getElementById("quick-commit-modal");
|
|
2364
2393
|
if (!modal) return;
|
|
2394
|
+
if (quickCommitDragCleanup) {
|
|
2395
|
+
quickCommitDragCleanup();
|
|
2396
|
+
quickCommitDragCleanup = null;
|
|
2397
|
+
}
|
|
2365
2398
|
var html = renderQuickCommitModal();
|
|
2366
2399
|
var temp = document.createElement("div");
|
|
2367
2400
|
temp.innerHTML = html;
|
|
@@ -2377,8 +2410,6 @@
|
|
|
2377
2410
|
var cancelBtn = document.getElementById("quick-commit-cancel-btn");
|
|
2378
2411
|
if (cancelBtn) cancelBtn.addEventListener("click", closeQuickCommitModal);
|
|
2379
2412
|
|
|
2380
|
-
var submitBtn = document.getElementById("quick-commit-submit-btn");
|
|
2381
|
-
if (submitBtn) submitBtn.addEventListener("click", submitQuickCommit);
|
|
2382
2413
|
var aiBtn = document.getElementById("quick-commit-ai-btn");
|
|
2383
2414
|
if (aiBtn) aiBtn.addEventListener("click", generateCommitMessageAI);
|
|
2384
2415
|
var msgEl = document.getElementById("quick-commit-message");
|
|
@@ -2390,7 +2421,7 @@
|
|
|
2390
2421
|
msgEl.addEventListener("keydown", function(e) {
|
|
2391
2422
|
if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
|
|
2392
2423
|
e.preventDefault();
|
|
2393
|
-
submitQuickCommit();
|
|
2424
|
+
submitQuickCommit("commit");
|
|
2394
2425
|
}
|
|
2395
2426
|
});
|
|
2396
2427
|
}
|
|
@@ -2403,114 +2434,251 @@
|
|
|
2403
2434
|
tagInput.addEventListener("keydown", function(e) {
|
|
2404
2435
|
if (e.key === "Enter") {
|
|
2405
2436
|
e.preventDefault();
|
|
2406
|
-
submitQuickCommit();
|
|
2437
|
+
submitQuickCommit("commit-tag");
|
|
2407
2438
|
}
|
|
2408
2439
|
});
|
|
2409
2440
|
}
|
|
2410
2441
|
|
|
2411
|
-
var
|
|
2412
|
-
if (
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
rerenderQuickCommitModal();
|
|
2442
|
+
var pushAfterBtn = document.getElementById("quick-commit-push-after-btn");
|
|
2443
|
+
if (pushAfterBtn) pushAfterBtn.addEventListener("click", function() {
|
|
2444
|
+
var result = state.quickCommitResult || {};
|
|
2445
|
+
submitPushOnly({ pushCommits: true, pushTags: !!result.tagName, closeOnSuccess: true });
|
|
2416
2446
|
});
|
|
2417
|
-
var actionMenu = document.getElementById("quick-commit-action-menu");
|
|
2418
|
-
if (actionMenu) {
|
|
2419
|
-
var actionItems = actionMenu.querySelectorAll("[data-qc-commit-mode]");
|
|
2420
|
-
for (var i = 0; i < actionItems.length; i++) {
|
|
2421
|
-
(function(btn) {
|
|
2422
|
-
btn.addEventListener("click", function() {
|
|
2423
|
-
// Keep what the user typed before the re-render.
|
|
2424
|
-
var liveTag = document.getElementById("quick-commit-tag");
|
|
2425
|
-
if (liveTag && !liveTag.disabled) state.quickCommitForm.tag = liveTag.value;
|
|
2426
|
-
var value = btn.getAttribute("data-qc-commit-mode");
|
|
2427
|
-
if (value === "commit" || value === "commit-tag") {
|
|
2428
|
-
state.quickCommitForm.commitMode = value;
|
|
2429
|
-
saveCommitMode(value);
|
|
2430
|
-
}
|
|
2431
|
-
state.quickCommitOpenMenu = null;
|
|
2432
|
-
rerenderQuickCommitModal();
|
|
2433
|
-
if (value === "commit-tag") {
|
|
2434
|
-
var inp = document.getElementById("quick-commit-tag");
|
|
2435
|
-
if (inp) setTimeout(function() { inp.focus(); var v = inp.value; inp.value = ""; inp.value = v; }, 0);
|
|
2436
|
-
}
|
|
2437
|
-
});
|
|
2438
|
-
})(actionItems[i]);
|
|
2439
|
-
}
|
|
2440
|
-
}
|
|
2441
2447
|
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2448
|
+
attachQuickCommitDrag();
|
|
2449
|
+
}
|
|
2450
|
+
|
|
2451
|
+
// ratio: knob CENTER position as fraction of track [0..1]. 0 = left (Commit start), 1 = right (Push).
|
|
2452
|
+
function updateQuickCommitDragVisual(action, ratio, opts) {
|
|
2453
|
+
action = normalizeQuickCommitAction(action);
|
|
2454
|
+
state.quickCommitDragAction = action;
|
|
2455
|
+
var track = document.getElementById("quick-commit-drag-track");
|
|
2456
|
+
if (!track) return;
|
|
2457
|
+
var meta = getQuickCommitActionMeta(action);
|
|
2458
|
+
// Resting position per action when user isn't dragging.
|
|
2459
|
+
var defaultRatio = 0;
|
|
2460
|
+
if (action === "commit-tag") defaultRatio = 0.5;
|
|
2461
|
+
else if (action === "commit-push" || action === "commit-tag-push") defaultRatio = 1;
|
|
2462
|
+
var progress = typeof ratio === "number"
|
|
2463
|
+
? Math.max(0, Math.min(1, ratio))
|
|
2464
|
+
: defaultRatio;
|
|
2465
|
+
var zone = getQuickCommitZoneFromRatio(progress);
|
|
2466
|
+
track.setAttribute("data-action", action);
|
|
2467
|
+
track.setAttribute("data-zone", zone);
|
|
2468
|
+
track.setAttribute("data-tag-armed", (action === "commit-tag" || action === "commit-tag-push") ? "1" : "0");
|
|
2469
|
+
track.style.setProperty("--qc-progress", (progress * 100).toFixed(1) + "%");
|
|
2470
|
+
var knob = document.getElementById("quick-commit-drag-action");
|
|
2471
|
+
if (knob) {
|
|
2472
|
+
var trackWidth = track.clientWidth;
|
|
2473
|
+
var knobWidth = knob.offsetWidth;
|
|
2474
|
+
var pad = 6;
|
|
2475
|
+
// Knob CENTER tracks ratio * trackWidth, clamped so the knob stays fully inside.
|
|
2476
|
+
var center = progress * trackWidth;
|
|
2477
|
+
var minLeft = pad;
|
|
2478
|
+
var maxLeft = Math.max(pad, trackWidth - knobWidth - pad);
|
|
2479
|
+
var left = Math.max(minLeft, Math.min(maxLeft, center - knobWidth / 2));
|
|
2480
|
+
track.style.setProperty("--qc-knob-x", left.toFixed(1) + "px");
|
|
2481
|
+
}
|
|
2482
|
+
var label = document.getElementById("quick-commit-drag-label");
|
|
2483
|
+
if (label) {
|
|
2484
|
+
var dwell = track.getAttribute("data-tag-dwell") || "idle";
|
|
2485
|
+
var cancelMode = track.getAttribute("data-cancel-mode") === "1";
|
|
2486
|
+
var txt;
|
|
2487
|
+
if (state.quickCommitSubmitting) {
|
|
2488
|
+
txt = state.quickCommitAutoGenerating ? "AI 生成 + 提交中…" : "执行中…";
|
|
2489
|
+
} else if (cancelMode) {
|
|
2490
|
+
txt = "松手取消 ✕";
|
|
2491
|
+
} else if (dwell === "active") {
|
|
2492
|
+
txt = "锁定 Tag 中…";
|
|
2493
|
+
} else if (dwell === "armed" && zone === "tag") {
|
|
2494
|
+
txt = "Tag 已就绪 · 继续推送 →";
|
|
2495
|
+
} else {
|
|
2496
|
+
txt = meta.label;
|
|
2453
2497
|
}
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2498
|
+
label.textContent = txt;
|
|
2499
|
+
}
|
|
2500
|
+
// Stage class refresh.
|
|
2501
|
+
var stages = track.querySelectorAll("[data-qc-stage]");
|
|
2502
|
+
for (var i = 0; i < stages.length; i++) {
|
|
2503
|
+
var st = stages[i].getAttribute("data-qc-stage");
|
|
2504
|
+
var active = false, passed = false;
|
|
2505
|
+
if (st === "commit") {
|
|
2506
|
+
active = action === "commit";
|
|
2507
|
+
passed = true;
|
|
2508
|
+
} else if (st === "tag") {
|
|
2509
|
+
active = action === "commit-tag";
|
|
2510
|
+
passed = action === "commit-tag" || action === "commit-tag-push";
|
|
2511
|
+
} else if (st === "push") {
|
|
2512
|
+
active = action === "commit-push" || action === "commit-tag-push";
|
|
2513
|
+
passed = active;
|
|
2458
2514
|
}
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
if (tagHeadCancel) tagHeadCancel.addEventListener("click", function() {
|
|
2462
|
-
if (state.quickCommitOpenMenu === "tag-head") state.quickCommitOpenMenu = null;
|
|
2463
|
-
state.quickCommitTagHeadError = "";
|
|
2464
|
-
rerenderQuickCommitModal();
|
|
2465
|
-
});
|
|
2466
|
-
var tagHeadInput = document.getElementById("quick-commit-tag-head-input");
|
|
2467
|
-
if (tagHeadInput) {
|
|
2468
|
-
tagHeadInput.addEventListener("input", function() {
|
|
2469
|
-
state.quickCommitTagHeadForm.tag = tagHeadInput.value;
|
|
2470
|
-
});
|
|
2471
|
-
tagHeadInput.addEventListener("keydown", function(e) {
|
|
2472
|
-
if (e.key === "Enter") {
|
|
2473
|
-
e.preventDefault();
|
|
2474
|
-
submitTagHead(false);
|
|
2475
|
-
}
|
|
2476
|
-
});
|
|
2515
|
+
stages[i].classList.toggle("is-active", active);
|
|
2516
|
+
stages[i].classList.toggle("is-passed", passed);
|
|
2477
2517
|
}
|
|
2478
|
-
|
|
2479
|
-
if (tagHeadAi) tagHeadAi.addEventListener("click", generateTagHeadAI);
|
|
2480
|
-
var tagHeadPushCb = document.getElementById("quick-commit-tag-head-push");
|
|
2481
|
-
if (tagHeadPushCb) tagHeadPushCb.addEventListener("change", function() {
|
|
2482
|
-
state.quickCommitTagHeadForm.push = tagHeadPushCb.checked;
|
|
2483
|
-
});
|
|
2484
|
-
var tagHeadSubmit = document.getElementById("quick-commit-tag-head-submit");
|
|
2485
|
-
if (tagHeadSubmit) tagHeadSubmit.addEventListener("click", function() {
|
|
2486
|
-
submitTagHead(false);
|
|
2487
|
-
});
|
|
2518
|
+
}
|
|
2488
2519
|
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
if (pushMenu) {
|
|
2501
|
-
var pushItems = pushMenu.querySelectorAll("[data-qc-push]");
|
|
2502
|
-
for (var j = 0; j < pushItems.length; j++) {
|
|
2503
|
-
(function(btn) {
|
|
2504
|
-
btn.addEventListener("click", function() {
|
|
2505
|
-
var value = btn.getAttribute("data-qc-push");
|
|
2506
|
-
state.quickCommitOpenMenu = null;
|
|
2507
|
-
if (value === "commits") submitPushOnly({ pushCommits: true, pushTags: false });
|
|
2508
|
-
else if (value === "tags") submitPushOnly({ pushCommits: false, pushTags: true });
|
|
2509
|
-
else if (value === "both") submitPushOnly({ pushCommits: true, pushTags: true });
|
|
2510
|
-
});
|
|
2511
|
-
})(pushItems[j]);
|
|
2520
|
+
function attachQuickCommitDrag() {
|
|
2521
|
+
var track = document.getElementById("quick-commit-drag-track");
|
|
2522
|
+
var knob = document.getElementById("quick-commit-drag-action");
|
|
2523
|
+
if (!track || !knob) return;
|
|
2524
|
+
updateQuickCommitDragVisual(state.quickCommitDragAction || "commit");
|
|
2525
|
+
|
|
2526
|
+
function setDwell(state) { track.setAttribute("data-tag-dwell", state); }
|
|
2527
|
+
function clearArmTimer() {
|
|
2528
|
+
if (quickCommitDragState && quickCommitDragState.tagArmTimer) {
|
|
2529
|
+
clearTimeout(quickCommitDragState.tagArmTimer);
|
|
2530
|
+
quickCommitDragState.tagArmTimer = null;
|
|
2512
2531
|
}
|
|
2513
2532
|
}
|
|
2533
|
+
|
|
2534
|
+
function setCancelMode(on) { track.setAttribute("data-cancel-mode", on ? "1" : "0"); }
|
|
2535
|
+
|
|
2536
|
+
var onPointerDown = function(e) {
|
|
2537
|
+
if (knob.disabled || isQuickCommitOpInFlight()) return;
|
|
2538
|
+
var rect = track.getBoundingClientRect();
|
|
2539
|
+
quickCommitDragState = {
|
|
2540
|
+
pointerId: e.pointerId,
|
|
2541
|
+
rect: rect,
|
|
2542
|
+
startX: e.clientX,
|
|
2543
|
+
moved: false,
|
|
2544
|
+
zone: "commit",
|
|
2545
|
+
maxZone: "commit", // furthest zone the knob ever reached this drag
|
|
2546
|
+
tagArmed: false,
|
|
2547
|
+
tagArmTimer: null,
|
|
2548
|
+
action: "commit",
|
|
2549
|
+
};
|
|
2550
|
+
try { knob.setPointerCapture(e.pointerId); } catch (err) { /* ignored */ }
|
|
2551
|
+
track.classList.add("is-dragging");
|
|
2552
|
+
setDwell("idle");
|
|
2553
|
+
setCancelMode(false);
|
|
2554
|
+
updateQuickCommitDragVisual("commit", 0);
|
|
2555
|
+
e.preventDefault();
|
|
2556
|
+
};
|
|
2557
|
+
var onPointerMove = function(e) {
|
|
2558
|
+
if (!quickCommitDragState || quickCommitDragState.pointerId !== e.pointerId) return;
|
|
2559
|
+
var rect = quickCommitDragState.rect;
|
|
2560
|
+
var ratio = rect.width > 0 ? (e.clientX - rect.left) / rect.width : 0;
|
|
2561
|
+
ratio = Math.max(0, Math.min(1, ratio));
|
|
2562
|
+
if (Math.abs(e.clientX - quickCommitDragState.startX) > 5) quickCommitDragState.moved = true;
|
|
2563
|
+
var zone = getQuickCommitZoneFromRatio(ratio);
|
|
2564
|
+
// Track furthest zone reached so we can detect a backtrack-to-commit.
|
|
2565
|
+
if (QUICK_COMMIT_ZONE_RANK[zone] > QUICK_COMMIT_ZONE_RANK[quickCommitDragState.maxZone]) {
|
|
2566
|
+
quickCommitDragState.maxZone = zone;
|
|
2567
|
+
}
|
|
2568
|
+
// Zone transitions drive the dwell state machine.
|
|
2569
|
+
if (zone !== quickCommitDragState.zone) {
|
|
2570
|
+
if (zone === "tag" && !quickCommitDragState.tagArmed) {
|
|
2571
|
+
// Entered Tag zone unarmed → kick off the dwell timer + CSS fill animation.
|
|
2572
|
+
clearArmTimer();
|
|
2573
|
+
setDwell("active");
|
|
2574
|
+
quickCommitDragState.tagArmTimer = setTimeout(function() {
|
|
2575
|
+
if (!quickCommitDragState) return;
|
|
2576
|
+
quickCommitDragState.tagArmed = true;
|
|
2577
|
+
quickCommitDragState.tagArmTimer = null;
|
|
2578
|
+
setDwell("armed");
|
|
2579
|
+
var newAction = composeQuickCommitAction(quickCommitDragState.zone, true);
|
|
2580
|
+
quickCommitDragState.action = newAction;
|
|
2581
|
+
updateQuickCommitDragVisual(newAction);
|
|
2582
|
+
}, QUICK_COMMIT_TAG_DWELL_MS);
|
|
2583
|
+
} else if (zone !== "tag" && !quickCommitDragState.tagArmed) {
|
|
2584
|
+
// Left Tag zone before arming → cancel the dwell.
|
|
2585
|
+
clearArmTimer();
|
|
2586
|
+
setDwell("idle");
|
|
2587
|
+
} else if (zone === "tag" && quickCommitDragState.tagArmed) {
|
|
2588
|
+
setDwell("armed");
|
|
2589
|
+
}
|
|
2590
|
+
quickCommitDragState.zone = zone;
|
|
2591
|
+
}
|
|
2592
|
+
// Cancel-mode: backed back to commit after venturing further.
|
|
2593
|
+
// Visual cue is set here; the actual "no-submit" decision is made in finish().
|
|
2594
|
+
var inCancelMode = quickCommitDragState.maxZone !== "commit" && zone === "commit";
|
|
2595
|
+
setCancelMode(inCancelMode);
|
|
2596
|
+
var action = composeQuickCommitAction(zone, quickCommitDragState.tagArmed);
|
|
2597
|
+
quickCommitDragState.action = action;
|
|
2598
|
+
updateQuickCommitDragVisual(action, ratio);
|
|
2599
|
+
};
|
|
2600
|
+
var finish = function(e, cancelled) {
|
|
2601
|
+
if (!quickCommitDragState) return;
|
|
2602
|
+
var current = quickCommitDragState;
|
|
2603
|
+
clearArmTimer();
|
|
2604
|
+
quickCommitDragState = null;
|
|
2605
|
+
track.classList.remove("is-dragging");
|
|
2606
|
+
setDwell("idle");
|
|
2607
|
+
setCancelMode(false);
|
|
2608
|
+
try {
|
|
2609
|
+
if (typeof knob.releasePointerCapture === "function") knob.releasePointerCapture(current.pointerId);
|
|
2610
|
+
} catch (err) { /* ignored */ }
|
|
2611
|
+
// Backtrack: user ventured out past Commit, then dragged back and released at Commit → cancel.
|
|
2612
|
+
var isBacktrack = current.moved
|
|
2613
|
+
&& current.zone === "commit"
|
|
2614
|
+
&& current.maxZone !== "commit";
|
|
2615
|
+
if (cancelled || isBacktrack) {
|
|
2616
|
+
updateQuickCommitDragVisual("commit");
|
|
2617
|
+
return;
|
|
2618
|
+
}
|
|
2619
|
+
// Tap (no drag) → just commit. Auto-message will fire if the message is empty.
|
|
2620
|
+
var action = current.moved
|
|
2621
|
+
? composeQuickCommitAction(current.zone, current.tagArmed)
|
|
2622
|
+
: "commit";
|
|
2623
|
+
updateQuickCommitDragVisual(action);
|
|
2624
|
+
submitQuickCommit(action);
|
|
2625
|
+
if (e && typeof e.preventDefault === "function") e.preventDefault();
|
|
2626
|
+
};
|
|
2627
|
+
var onPointerUp = function(e) {
|
|
2628
|
+
if (!quickCommitDragState || quickCommitDragState.pointerId !== e.pointerId) return;
|
|
2629
|
+
finish(e, false);
|
|
2630
|
+
};
|
|
2631
|
+
var onPointerCancel = function(e) {
|
|
2632
|
+
if (!quickCommitDragState || quickCommitDragState.pointerId !== e.pointerId) return;
|
|
2633
|
+
finish(e, true);
|
|
2634
|
+
};
|
|
2635
|
+
// Keyboard parity:
|
|
2636
|
+
// → step zone right (commit → commit-tag → commit-tag-push). The first → into Tag arms it instantly.
|
|
2637
|
+
// ← step zone left
|
|
2638
|
+
// Home → reset to commit
|
|
2639
|
+
// Enter / Space → submit current action
|
|
2640
|
+
var onKeyDown = function(e) {
|
|
2641
|
+
if (knob.disabled || isQuickCommitOpInFlight()) return;
|
|
2642
|
+
var cur = state.quickCommitDragAction || "commit";
|
|
2643
|
+
if (e.key === "ArrowRight") {
|
|
2644
|
+
e.preventDefault();
|
|
2645
|
+
var next = cur;
|
|
2646
|
+
if (cur === "commit") next = "commit-tag"; // arm Tag
|
|
2647
|
+
else if (cur === "commit-tag") next = "commit-tag-push";
|
|
2648
|
+
else if (cur === "commit-push") next = "commit-push";
|
|
2649
|
+
updateQuickCommitDragVisual(next);
|
|
2650
|
+
} else if (e.key === "ArrowLeft") {
|
|
2651
|
+
e.preventDefault();
|
|
2652
|
+
var prev = cur;
|
|
2653
|
+
if (cur === "commit-tag-push") prev = "commit-tag";
|
|
2654
|
+
else if (cur === "commit-tag") prev = "commit";
|
|
2655
|
+
else if (cur === "commit-push") prev = "commit";
|
|
2656
|
+
updateQuickCommitDragVisual(prev);
|
|
2657
|
+
} else if (e.key === "Home") {
|
|
2658
|
+
e.preventDefault();
|
|
2659
|
+
updateQuickCommitDragVisual("commit");
|
|
2660
|
+
} else if (e.key === "Enter" || e.key === " ") {
|
|
2661
|
+
e.preventDefault();
|
|
2662
|
+
submitQuickCommit(state.quickCommitDragAction || "commit");
|
|
2663
|
+
}
|
|
2664
|
+
};
|
|
2665
|
+
|
|
2666
|
+
knob.addEventListener("pointerdown", onPointerDown);
|
|
2667
|
+
knob.addEventListener("pointermove", onPointerMove);
|
|
2668
|
+
knob.addEventListener("pointerup", onPointerUp);
|
|
2669
|
+
knob.addEventListener("pointercancel", onPointerCancel);
|
|
2670
|
+
knob.addEventListener("keydown", onKeyDown);
|
|
2671
|
+
quickCommitDragCleanup = function() {
|
|
2672
|
+
knob.removeEventListener("pointerdown", onPointerDown);
|
|
2673
|
+
knob.removeEventListener("pointermove", onPointerMove);
|
|
2674
|
+
knob.removeEventListener("pointerup", onPointerUp);
|
|
2675
|
+
knob.removeEventListener("pointercancel", onPointerCancel);
|
|
2676
|
+
knob.removeEventListener("keydown", onKeyDown);
|
|
2677
|
+
if (quickCommitDragState && quickCommitDragState.tagArmTimer) {
|
|
2678
|
+
clearTimeout(quickCommitDragState.tagArmTimer);
|
|
2679
|
+
}
|
|
2680
|
+
quickCommitDragState = null;
|
|
2681
|
+
};
|
|
2514
2682
|
}
|
|
2515
2683
|
|
|
2516
2684
|
function generateCommitMessageAI() {
|
|
@@ -2548,8 +2716,7 @@
|
|
|
2548
2716
|
// recommendation is actually applied on commit.
|
|
2549
2717
|
if (aiTag) {
|
|
2550
2718
|
if (!state.quickCommitForm.tagEdited) state.quickCommitForm.tag = aiTag;
|
|
2551
|
-
state.
|
|
2552
|
-
saveCommitMode("commit-tag");
|
|
2719
|
+
state.quickCommitDragAction = "commit-tag";
|
|
2553
2720
|
}
|
|
2554
2721
|
})
|
|
2555
2722
|
.catch(function(error) {
|
|
@@ -2561,32 +2728,43 @@
|
|
|
2561
2728
|
});
|
|
2562
2729
|
}
|
|
2563
2730
|
|
|
2564
|
-
function submitQuickCommit() {
|
|
2731
|
+
function submitQuickCommit(action) {
|
|
2565
2732
|
if (!state.selectedId || state.quickCommitSubmitting) return;
|
|
2566
2733
|
var msgEl = document.getElementById("quick-commit-message");
|
|
2567
2734
|
if (msgEl) state.quickCommitForm.customMessage = msgEl.value;
|
|
2568
2735
|
var tagEl = document.getElementById("quick-commit-tag");
|
|
2569
2736
|
if (tagEl) state.quickCommitForm.tag = tagEl.value;
|
|
2570
2737
|
var form = state.quickCommitForm || {};
|
|
2571
|
-
var
|
|
2738
|
+
var meta = getQuickCommitActionMeta(action || state.quickCommitDragAction || "commit");
|
|
2739
|
+
var withTag = meta.withTag;
|
|
2572
2740
|
var userTag = withTag ? (form.tag || "").trim() : "";
|
|
2573
2741
|
var message = (form.customMessage || "").trim();
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2742
|
+
// Auto-generate flow: empty commit message → ask backend to write one (autoMessage:true).
|
|
2743
|
+
// Empty tag (when withTag) → ask backend to derive one (autoTag:true). Both go in one round-trip.
|
|
2744
|
+
var autoMessage = !message;
|
|
2745
|
+
var before = {
|
|
2746
|
+
branch: (state.gitStatus || {}).branch || "",
|
|
2747
|
+
commitHash: (state.gitStatus || {}).lastCommit && (state.gitStatus || {}).lastCommit.shortHash
|
|
2748
|
+
? (state.gitStatus || {}).lastCommit.shortHash
|
|
2749
|
+
: ((state.gitStatus || {}).head ? (state.gitStatus || {}).head.substring(0, 7) : ""),
|
|
2750
|
+
commitSubject: (state.gitStatus || {}).lastCommit && (state.gitStatus || {}).lastCommit.subject
|
|
2751
|
+
? (state.gitStatus || {}).lastCommit.subject
|
|
2752
|
+
: "",
|
|
2753
|
+
tag: (state.gitStatus || {}).latestTag || "",
|
|
2754
|
+
};
|
|
2581
2755
|
var payload = {
|
|
2582
|
-
autoMessage:
|
|
2583
|
-
customMessage: message,
|
|
2756
|
+
autoMessage: autoMessage,
|
|
2757
|
+
customMessage: autoMessage ? "" : message,
|
|
2584
2758
|
tag: userTag,
|
|
2585
2759
|
autoTag: !!(withTag && !userTag),
|
|
2586
|
-
push:
|
|
2760
|
+
push: !!meta.push
|
|
2587
2761
|
};
|
|
2588
2762
|
state.quickCommitSubmitting = true;
|
|
2763
|
+
state.quickCommitAutoGenerating = autoMessage || payload.autoTag;
|
|
2589
2764
|
state.quickCommitError = "";
|
|
2765
|
+
state.quickCommitPushError = "";
|
|
2766
|
+
state.quickCommitResult = null;
|
|
2767
|
+
state.quickCommitDragAction = meta.action;
|
|
2590
2768
|
rerenderQuickCommitModal();
|
|
2591
2769
|
fetch("/api/sessions/" + encodeURIComponent(state.selectedId) + "/quick-commit", {
|
|
2592
2770
|
method: "POST",
|
|
@@ -2607,99 +2785,36 @@
|
|
|
2607
2785
|
? "已先提交 " + subCommits.length + " 个 submodule(" + subCommits.map(function(c) { return c.path; }).join("、") + "),"
|
|
2608
2786
|
: "";
|
|
2609
2787
|
var base = subPrefix + "已提交" + (hash ? " " + hash : "") + (tagName ? ",已打 Tag " + tagName : "");
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
if (!state.selectedId || state.quickCommitTagHeadGenerating) return;
|
|
2626
|
-
var inp = document.getElementById("quick-commit-tag-head-input");
|
|
2627
|
-
if (inp) state.quickCommitTagHeadForm.tag = inp.value;
|
|
2628
|
-
state.quickCommitTagHeadGenerating = true;
|
|
2629
|
-
state.quickCommitTagHeadError = "";
|
|
2630
|
-
rerenderQuickCommitModal();
|
|
2631
|
-
// Reuse the existing generator — it stages and asks for {message, tag}.
|
|
2632
|
-
// We only consume `suggestedTag` here.
|
|
2633
|
-
fetch("/api/sessions/" + encodeURIComponent(state.selectedId) + "/generate-commit-message", {
|
|
2634
|
-
method: "POST",
|
|
2635
|
-
credentials: "same-origin",
|
|
2636
|
-
headers: { "Content-Type": "application/json" },
|
|
2637
|
-
body: JSON.stringify({})
|
|
2638
|
-
})
|
|
2639
|
-
.then(function(res) { return res.json().then(function(data) { return { ok: res.ok, data: data }; }); })
|
|
2640
|
-
.then(function(result) {
|
|
2641
|
-
if (!result.ok) throw new Error((result.data && result.data.error) || "AI 生成失败。");
|
|
2642
|
-
var aiTag = (result.data && typeof result.data.suggestedTag === "string") ? result.data.suggestedTag.trim() : "";
|
|
2643
|
-
var currentTag = (state.quickCommitTagHeadForm.tag || "").trim();
|
|
2644
|
-
if (!currentTag && aiTag) {
|
|
2645
|
-
state.quickCommitTagHeadForm.tag = aiTag;
|
|
2646
|
-
} else if (!aiTag) {
|
|
2647
|
-
throw new Error("AI 没有给出 tag 建议。");
|
|
2648
|
-
}
|
|
2649
|
-
})
|
|
2650
|
-
.catch(function(error) {
|
|
2651
|
-
state.quickCommitTagHeadError = (error && error.message) || "AI 生成失败。";
|
|
2652
|
-
})
|
|
2653
|
-
.finally(function() {
|
|
2654
|
-
state.quickCommitTagHeadGenerating = false;
|
|
2655
|
-
if (state.quickCommitOpen) rerenderQuickCommitModal();
|
|
2656
|
-
});
|
|
2657
|
-
}
|
|
2658
|
-
|
|
2659
|
-
// Tag the existing HEAD without making a new commit.
|
|
2660
|
-
function submitTagHead(silent) {
|
|
2661
|
-
if (!state.selectedId || state.quickCommitTagHeadSubmitting) return;
|
|
2662
|
-
var inp = document.getElementById("quick-commit-tag-head-input");
|
|
2663
|
-
if (inp) state.quickCommitTagHeadForm.tag = inp.value;
|
|
2664
|
-
var tag = (state.quickCommitTagHeadForm.tag || "").trim();
|
|
2665
|
-
if (!tag) {
|
|
2666
|
-
state.quickCommitTagHeadError = "请填写 tag 名称,或点击 AI 建议。";
|
|
2667
|
-
rerenderQuickCommitModal();
|
|
2668
|
-
return;
|
|
2669
|
-
}
|
|
2670
|
-
state.quickCommitTagHeadSubmitting = true;
|
|
2671
|
-
state.quickCommitTagHeadError = "";
|
|
2672
|
-
rerenderQuickCommitModal();
|
|
2673
|
-
fetch("/api/sessions/" + encodeURIComponent(state.selectedId) + "/git/tag-head", {
|
|
2674
|
-
method: "POST",
|
|
2675
|
-
credentials: "same-origin",
|
|
2676
|
-
headers: { "Content-Type": "application/json" },
|
|
2677
|
-
body: JSON.stringify({ tag: tag, push: !!state.quickCommitTagHeadForm.push })
|
|
2678
|
-
})
|
|
2679
|
-
.then(function(res) { return res.json().then(function(data) { return { ok: res.ok, data: data }; }); })
|
|
2680
|
-
.then(function(result) {
|
|
2681
|
-
if (!result.ok) throw new Error((result.data && result.data.error) || "打 tag 失败。");
|
|
2682
|
-
var data = result.data || {};
|
|
2683
|
-
var name = data.tag && data.tag.name ? data.tag.name : tag;
|
|
2684
|
-
var pushed = !!data.pushed;
|
|
2685
|
-
var pushErr = data.pushError;
|
|
2686
|
-
var base = "已为 HEAD 打 tag " + name;
|
|
2687
|
-
if (state.quickCommitTagHeadForm.push && pushErr) {
|
|
2688
|
-
if (typeof showToast === "function") showToast(base + ";push tag 失败:" + pushErr, "error");
|
|
2788
|
+
state.quickCommitResult = {
|
|
2789
|
+
action: meta.action,
|
|
2790
|
+
pushed: !!data.pushed,
|
|
2791
|
+
pushError: data.pushError || "",
|
|
2792
|
+
commitHash: hash,
|
|
2793
|
+
commitMessage: data.commit && data.commit.message ? data.commit.message : message,
|
|
2794
|
+
tagName: tagName,
|
|
2795
|
+
oldTag: before.tag,
|
|
2796
|
+
oldCommitHash: before.commitHash,
|
|
2797
|
+
oldCommitSubject: before.commitSubject,
|
|
2798
|
+
submoduleCount: subCommits.length,
|
|
2799
|
+
};
|
|
2800
|
+
if (meta.push && !data.pushError) {
|
|
2801
|
+
if (typeof showToast === "function") showToast(base + ",已推送。", "success");
|
|
2802
|
+
closeQuickCommitModal();
|
|
2689
2803
|
} else {
|
|
2690
|
-
if (typeof showToast === "function")
|
|
2804
|
+
if (typeof showToast === "function") {
|
|
2805
|
+
showToast(base + (data.pushError ? ";push 失败:" + data.pushError : "。"), data.pushError ? "error" : "success");
|
|
2806
|
+
}
|
|
2807
|
+
if (state.selectedId) loadGitStatus(state.selectedId, { force: true }).then(function() {
|
|
2808
|
+
if (state.quickCommitOpen) rerenderQuickCommitModal();
|
|
2809
|
+
});
|
|
2691
2810
|
}
|
|
2692
|
-
if (state.quickCommitOpenMenu === "tag-head") state.quickCommitOpenMenu = null;
|
|
2693
|
-
state.quickCommitTagHeadForm = { tag: "", push: false };
|
|
2694
|
-
if (state.selectedId) loadGitStatus(state.selectedId, { force: true }).then(function() {
|
|
2695
|
-
if (state.quickCommitOpen) rerenderQuickCommitModal();
|
|
2696
|
-
});
|
|
2697
2811
|
})
|
|
2698
2812
|
.catch(function(error) {
|
|
2699
|
-
state.
|
|
2813
|
+
state.quickCommitError = (error && error.message) || "快捷提交失败。";
|
|
2700
2814
|
})
|
|
2701
2815
|
.finally(function() {
|
|
2702
|
-
state.
|
|
2816
|
+
state.quickCommitSubmitting = false;
|
|
2817
|
+
state.quickCommitAutoGenerating = false;
|
|
2703
2818
|
if (state.quickCommitOpen) rerenderQuickCommitModal();
|
|
2704
2819
|
});
|
|
2705
2820
|
}
|
|
@@ -2708,6 +2823,7 @@
|
|
|
2708
2823
|
if (!state.selectedId || state.quickCommitPushing) return;
|
|
2709
2824
|
var pushCommits = !!(opts && opts.pushCommits);
|
|
2710
2825
|
var pushTags = !!(opts && opts.pushTags);
|
|
2826
|
+
var closeOnSuccess = !!(opts && opts.closeOnSuccess);
|
|
2711
2827
|
if (!pushCommits && !pushTags) return;
|
|
2712
2828
|
state.quickCommitPushing = true;
|
|
2713
2829
|
state.quickCommitPushError = "";
|
|
@@ -2733,9 +2849,15 @@
|
|
|
2733
2849
|
if (data.pushedTags) parts.push("tags");
|
|
2734
2850
|
var label = parts.length ? parts.join(" 和 ") : "(无内容)";
|
|
2735
2851
|
if (typeof showToast === "function") showToast("已推送 " + label, "success");
|
|
2736
|
-
if (state.
|
|
2737
|
-
|
|
2738
|
-
|
|
2852
|
+
if (state.quickCommitResult) state.quickCommitResult.pushed = true;
|
|
2853
|
+
if (closeOnSuccess) {
|
|
2854
|
+
closeQuickCommitModal();
|
|
2855
|
+
if (state.selectedId) loadGitStatus(state.selectedId, { force: true });
|
|
2856
|
+
} else if (state.selectedId) {
|
|
2857
|
+
loadGitStatus(state.selectedId, { force: true }).then(function() {
|
|
2858
|
+
if (state.quickCommitOpen) rerenderQuickCommitModal();
|
|
2859
|
+
});
|
|
2860
|
+
}
|
|
2739
2861
|
})
|
|
2740
2862
|
.catch(function(error) {
|
|
2741
2863
|
state.quickCommitPushError = (error && error.message) || "推送失败。";
|
|
@@ -2800,213 +2922,132 @@
|
|
|
2800
2922
|
}
|
|
2801
2923
|
|
|
2802
2924
|
function isQuickCommitOpInFlight() {
|
|
2803
|
-
return state.quickCommitSubmitting || state.
|
|
2925
|
+
return state.quickCommitSubmitting || state.quickCommitPushing;
|
|
2804
2926
|
}
|
|
2805
2927
|
|
|
2806
|
-
function
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
var menuItems = [
|
|
2816
|
-
{ value: "commit-tag", label: "提交并打 Tag", desc: "创建 commit,并为它打一个版本 Tag" },
|
|
2817
|
-
{ value: "commit", label: "仅提交", desc: "只创建 commit,不打 Tag" }
|
|
2818
|
-
];
|
|
2819
|
-
var menuHtml = menuItems.map(function(item) {
|
|
2820
|
-
var sel = f.commitMode === item.value ? " is-selected" : "";
|
|
2821
|
-
return '<button type="button" class="qc-dropdown-item' + sel + '" data-qc-commit-mode="' + item.value + '" role="menuitemradio" aria-checked="' + (f.commitMode === item.value ? 'true' : 'false') + '">' +
|
|
2822
|
-
'<span class="qc-dropdown-item-main"><span class="qc-dropdown-check" aria-hidden="true">' +
|
|
2823
|
-
(f.commitMode === item.value ? '<svg viewBox="0 0 16 16" width="13" height="13"><path d="M13 4.5l-6 6L3 7" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>' : '') +
|
|
2824
|
-
'</span><span class="qc-dropdown-item-title">' + escapeHtml(item.label) + '</span></span>' +
|
|
2825
|
-
'<span class="qc-dropdown-item-desc">' + escapeHtml(item.desc) + '</span>' +
|
|
2826
|
-
'</button>';
|
|
2827
|
-
}).join("");
|
|
2828
|
-
return '<div class="qc-split-button">' +
|
|
2829
|
-
'<button id="quick-commit-submit-btn" class="btn btn-primary qc-split-main" type="button"' + (disabled ? ' disabled' : '') + '>' +
|
|
2830
|
-
escapeHtml(label) +
|
|
2831
|
-
'</button>' +
|
|
2832
|
-
'<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="' + (menuOpen ? 'true' : 'false') + '" aria-label="切换提交方式">' +
|
|
2833
|
-
'<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>' +
|
|
2834
|
-
'</button>' +
|
|
2835
|
-
(menuOpen ?
|
|
2836
|
-
'<div id="quick-commit-action-menu" class="qc-dropdown-menu" data-qc-dropdown-menu="action" role="menu">' + menuHtml + '</div>' : '') +
|
|
2837
|
-
'</div>';
|
|
2838
|
-
}
|
|
2839
|
-
|
|
2840
|
-
function renderQuickCommitStatusChips(s) {
|
|
2841
|
-
var chips = [];
|
|
2842
|
-
var hasUpstream = !!s.upstream;
|
|
2843
|
-
if (typeof s.ahead === "number" && s.ahead > 0) {
|
|
2844
|
-
chips.push('<span class="qc-chip qc-chip--ahead" title="本地领先 ' + s.ahead + ' 个 commit">↑ ' + s.ahead + ' 待推送</span>');
|
|
2845
|
-
}
|
|
2846
|
-
if (typeof s.behind === "number" && s.behind > 0) {
|
|
2847
|
-
chips.push('<span class="qc-chip qc-chip--behind" title="远端领先 ' + s.behind + ' 个 commit">↓ ' + s.behind + ' 待拉取</span>');
|
|
2848
|
-
}
|
|
2849
|
-
if (!hasUpstream) {
|
|
2850
|
-
chips.push('<span class="qc-chip qc-chip--warn" title="当前分支没有 upstream,将首次推送时自动设置">无 upstream</span>');
|
|
2851
|
-
} else if (!chips.length) {
|
|
2852
|
-
chips.push('<span class="qc-chip qc-chip--clean">与远端同步</span>');
|
|
2853
|
-
}
|
|
2854
|
-
return '<div class="qc-status-chips">' + chips.join("") + '</div>';
|
|
2928
|
+
function renderQuickCommitPair(label, fromHtml, toHtml, extraClass) {
|
|
2929
|
+
return '<div class="qc-pair' + (extraClass ? ' ' + extraClass : '') + '">' +
|
|
2930
|
+
'<div class="qc-pair-label">' + escapeHtml(label) + '</div>' +
|
|
2931
|
+
'<div class="qc-pair-flow">' +
|
|
2932
|
+
'<div class="qc-pair-value qc-pair-value--from">' + fromHtml + '</div>' +
|
|
2933
|
+
'<div class="qc-pair-arrow" aria-hidden="true">→</div>' +
|
|
2934
|
+
'<div class="qc-pair-value qc-pair-value--to">' + toHtml + '</div>' +
|
|
2935
|
+
'</div>' +
|
|
2936
|
+
'</div>';
|
|
2855
2937
|
}
|
|
2856
2938
|
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
var
|
|
2860
|
-
var
|
|
2861
|
-
var
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
'
|
|
2939
|
+
function renderQuickCommitDragControl(hasChanges) {
|
|
2940
|
+
var action = normalizeQuickCommitAction(state.quickCommitDragAction || "commit");
|
|
2941
|
+
var meta = getQuickCommitActionMeta(action);
|
|
2942
|
+
var disabled = !hasChanges || isQuickCommitOpInFlight();
|
|
2943
|
+
var label = state.quickCommitSubmitting ? "执行中…" : meta.label;
|
|
2944
|
+
// Stage classes derived from the current action.
|
|
2945
|
+
var commitActive = action === "commit";
|
|
2946
|
+
var tagActive = action === "commit-tag";
|
|
2947
|
+
var tagPassed = action === "commit-tag" || action === "commit-tag-push";
|
|
2948
|
+
var pushActive = action === "commit-push" || action === "commit-tag-push";
|
|
2949
|
+
var hint = "向右拖 · 经过 Tag 时停一下打 Tag · 直接划到 Push 跳过 Tag · 留空会自动生成";
|
|
2950
|
+
return '<div class="qc-drag-wrap">' +
|
|
2951
|
+
'<div id="quick-commit-drag-track" class="qc-drag-track" data-action="' + escapeHtml(action) + '" data-zone="commit" data-tag-dwell="idle">' +
|
|
2952
|
+
// Baseline track: subtle grey rail under everything, segments brighten as the knob passes.
|
|
2953
|
+
'<svg class="qc-drag-baseline" viewBox="0 0 100 24" preserveAspectRatio="none" aria-hidden="true">' +
|
|
2954
|
+
'<line class="qc-baseline-rail" x1="4" y1="12" x2="96" y2="12" />' +
|
|
2955
|
+
'<line class="qc-baseline-seg qc-baseline-seg--left" x1="4" y1="12" x2="50" y2="12" />' +
|
|
2956
|
+
'<line class="qc-baseline-seg qc-baseline-seg--right" x1="50" y1="12" x2="96" y2="12" />' +
|
|
2957
|
+
'</svg>' +
|
|
2958
|
+
// Three marching chevrons running right — "drag this way".
|
|
2959
|
+
'<div class="qc-chevrons" aria-hidden="true">' +
|
|
2960
|
+
'<svg class="qc-chevron" viewBox="0 0 12 16" width="9" height="12"><path d="M3 2 L9 8 L3 14" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg>' +
|
|
2961
|
+
'<svg class="qc-chevron" viewBox="0 0 12 16" width="9" height="12"><path d="M3 2 L9 8 L3 14" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg>' +
|
|
2962
|
+
'<svg class="qc-chevron" viewBox="0 0 12 16" width="9" height="12"><path d="M3 2 L9 8 L3 14" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg>' +
|
|
2963
|
+
'</div>' +
|
|
2964
|
+
// Stage dots / labels along the rail.
|
|
2965
|
+
'<div class="qc-drag-stages" aria-hidden="true">' +
|
|
2966
|
+
'<span class="qc-drag-stage qc-stage-commit' + (commitActive ? ' is-active' : '') + ' is-passed" data-qc-stage="commit">Commit</span>' +
|
|
2967
|
+
'<span class="qc-drag-stage qc-stage-tag' + (tagActive ? ' is-active' : '') + (tagPassed ? ' is-passed' : '') + '" data-qc-stage="tag">Tag</span>' +
|
|
2968
|
+
'<span class="qc-drag-stage qc-stage-push' + (pushActive ? ' is-active is-passed' : '') + '" data-qc-stage="push">Push</span>' +
|
|
2969
|
+
'</div>' +
|
|
2970
|
+
// Knob — starts at left, slides right.
|
|
2971
|
+
'<button id="quick-commit-drag-action" class="qc-drag-action" type="button"' + (disabled ? ' disabled' : '') + ' aria-label="' + escapeHtml(meta.verb) + '" title="' + escapeHtml(hint) + '">' +
|
|
2972
|
+
'<span class="qc-drag-grip" aria-hidden="true"><i></i><i></i><i></i></span>' +
|
|
2973
|
+
'<span id="quick-commit-drag-label" class="qc-drag-label">' + escapeHtml(label) + '</span>' +
|
|
2974
|
+
// Bottom progress bar — fills 0→100% over DWELL_MS while sitting in the Tag zone.
|
|
2975
|
+
'<span class="qc-drag-dwell-bar" aria-hidden="true"></span>' +
|
|
2976
|
+
'</button>' +
|
|
2876
2977
|
'</div>' +
|
|
2978
|
+
'<div class="qc-drag-help" aria-hidden="true">' + escapeHtml(hint) + '</div>' +
|
|
2877
2979
|
'</div>';
|
|
2878
2980
|
}
|
|
2879
2981
|
|
|
2880
|
-
function
|
|
2881
|
-
var
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
var
|
|
2890
|
-
var
|
|
2891
|
-
var
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
}).join("");
|
|
2904
|
-
return '<div class="qc-split-button qc-split-button--secondary">' +
|
|
2905
|
-
'<button id="quick-commit-push-btn" class="btn btn-secondary qc-split-main" type="button"' + (disabled ? ' disabled' : '') + '>' +
|
|
2906
|
-
escapeHtml(mainLabel) +
|
|
2907
|
-
'</button>' +
|
|
2908
|
-
'<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="' + (menuOpen ? 'true' : 'false') + '" aria-label="更多推送方式">' +
|
|
2909
|
-
'<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>' +
|
|
2910
|
-
'</button>' +
|
|
2911
|
-
(menuOpen ?
|
|
2912
|
-
'<div id="quick-commit-push-menu" class="qc-dropdown-menu qc-dropdown-menu--right" data-qc-dropdown-menu="push" role="menu">' + menuHtml + '</div>' : '') +
|
|
2913
|
-
'</div>';
|
|
2982
|
+
function renderQuickCommitResultPanel() {
|
|
2983
|
+
var r = state.quickCommitResult;
|
|
2984
|
+
if (!r) return "";
|
|
2985
|
+
var oldCommit = r.oldCommitHash
|
|
2986
|
+
? '<code>' + escapeHtml(r.oldCommitHash) + '</code>' + (r.oldCommitSubject ? '<span>' + escapeHtml(r.oldCommitSubject) + '</span>' : '')
|
|
2987
|
+
: '<span class="qc-muted">无</span>';
|
|
2988
|
+
var newCommit = r.commitHash
|
|
2989
|
+
? '<code>' + escapeHtml(r.commitHash) + '</code><span>' + escapeHtml(r.commitMessage || "") + '</span>'
|
|
2990
|
+
: '<span class="qc-muted">无</span>';
|
|
2991
|
+
var oldTag = r.oldTag ? '<code>' + escapeHtml(r.oldTag) + '</code>' : '<span class="qc-muted">无 tag</span>';
|
|
2992
|
+
var newTag = r.tagName ? '<code>' + escapeHtml(r.tagName) + '</code>' : '<span class="qc-muted">未打 tag</span>';
|
|
2993
|
+
var pushButton = r.pushed
|
|
2994
|
+
? '<span class="qc-result-pushed">已推送</span>'
|
|
2995
|
+
: '<button id="quick-commit-push-after-btn" class="btn btn-primary btn-sm" type="button"' + (state.quickCommitPushing ? ' disabled' : '') + '>' + (state.quickCommitPushing ? '推送中...' : 'Push & Close') + '</button>';
|
|
2996
|
+
return '<section class="qc-result-panel">' +
|
|
2997
|
+
renderQuickCommitPair("Commit", oldCommit, newCommit, "") +
|
|
2998
|
+
renderQuickCommitPair("Tag", oldTag, newTag, "qc-pair--tag") +
|
|
2999
|
+
(r.pushError || state.quickCommitPushError ? '<p class="error-message">' + escapeHtml(r.pushError || state.quickCommitPushError) + '</p>' : '') +
|
|
3000
|
+
'<div class="qc-result-actions">' +
|
|
3001
|
+
'<button id="quick-commit-cancel-btn" class="btn btn-ghost btn-sm" type="button">关闭</button>' +
|
|
3002
|
+
pushButton +
|
|
3003
|
+
'</div>' +
|
|
3004
|
+
'</section>';
|
|
2914
3005
|
}
|
|
2915
3006
|
|
|
2916
3007
|
function renderQuickCommitModal() {
|
|
2917
3008
|
var s = state.gitStatus || {};
|
|
2918
|
-
var f = state.quickCommitForm || { customMessage: "", tag: "", tagEdited: false
|
|
3009
|
+
var f = state.quickCommitForm || { customMessage: "", tag: "", tagEdited: false };
|
|
2919
3010
|
var hasChanges = (s.modifiedCount || 0) > 0;
|
|
2920
|
-
var
|
|
2921
|
-
var
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
if (
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
'<
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
'<div class="qc-tag-field' + (withTag ? '' : ' is-off') + '">' +
|
|
2949
|
-
'<span class="qc-tag-field-label" title="' + (withTag ? '这次提交会打上这个版本 Tag' : '当前为「仅提交」,不会打 Tag') + '">Tag</span>' +
|
|
2950
|
-
'<input type="text" id="quick-commit-tag" class="field-input qc-tag-field-input" placeholder="版本号,如 v1.2.0" value="' + escapeHtml(f.tag || "") + '"' + ((!withTag || state.quickCommitSubmitting) ? ' disabled' : '') + '>' +
|
|
2951
|
-
(withTag ? '' : '<span class="qc-tag-field-note">仅提交</span>') +
|
|
2952
|
-
'</div>' +
|
|
2953
|
-
(state.quickCommitError ? '<p class="error-message">' + escapeHtml(state.quickCommitError) + '</p>' : '') +
|
|
2954
|
-
'<div class="qc-section-actions">' +
|
|
2955
|
-
'<button id="quick-commit-cancel-btn" class="btn btn-ghost btn-sm" type="button">取消</button>' +
|
|
2956
|
-
'<div class="qc-action-group">' +
|
|
2957
|
-
renderQuickCommitCommitButton(hasChanges) +
|
|
2958
|
-
renderQuickCommitPushButton(s) +
|
|
2959
|
-
'</div>' +
|
|
2960
|
-
'</div>' +
|
|
2961
|
-
'</section>';
|
|
2962
|
-
}
|
|
2963
|
-
// When clean, we skip the big "changes" card entirely — a small green
|
|
2964
|
-
// indicator in the header subtitle is enough of a signal (see below).
|
|
2965
|
-
|
|
2966
|
-
// Section 2: repo status + secondary actions (always show when there's at least one commit)
|
|
2967
|
-
var section2 = "";
|
|
2968
|
-
if (!s.initialCommit && s.isGit !== false) {
|
|
2969
|
-
var lc = s.lastCommit || {};
|
|
2970
|
-
var headLine = lc.shortHash ? lc.shortHash + " · " + (lc.subject || "") : (s.head ? s.head.substring(0, 7) : "(no commit)");
|
|
2971
|
-
var upstreamLine = s.upstream ? escapeHtml(s.branch || "") + " → " + escapeHtml(s.upstream) : escapeHtml(s.branch || "(no branch)") + " · 无 upstream";
|
|
2972
|
-
var tagHeadOpen = state.quickCommitOpenMenu === "tag-head";
|
|
2973
|
-
section2 = '<section class="qc-section qc-section--repo">' +
|
|
2974
|
-
'<div class="qc-section-head"><span class="qc-section-title">仓库 · 同步</span><span class="qc-section-meta">' + upstreamLine + '</span></div>' +
|
|
2975
|
-
'<div class="qc-head-card">' +
|
|
2976
|
-
'<span class="qc-head-label">HEAD</span>' +
|
|
2977
|
-
'<code class="qc-head-text">' + escapeHtml(headLine) + '</code>' +
|
|
2978
|
-
'</div>' +
|
|
2979
|
-
renderQuickCommitStatusChips(s) +
|
|
2980
|
-
(tagHeadOpen ? renderQuickCommitTagHeadPanel() : '') +
|
|
2981
|
-
'<div class="qc-section-actions qc-section-actions--secondary">' +
|
|
2982
|
-
'<button id="quick-commit-tag-head-toggle" class="btn btn-secondary btn-sm qc-tag-head-btn' + (tagHeadOpen ? ' is-open' : '') + '" type="button"' + (state.quickCommitPushing ? ' disabled' : '') + ' title="给当前最新提交(HEAD)打 Tag,不会创建新提交">' +
|
|
2983
|
-
'<svg viewBox="0 0 16 16" width="14" height="14" aria-hidden="true"><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>' +
|
|
2984
|
-
'<span>' + (tagHeadOpen ? '收起' : '为当前提交打 Tag') + '</span>' +
|
|
2985
|
-
'</button>' +
|
|
2986
|
-
// Push lives in the commit footer when there are changes; show it here otherwise.
|
|
2987
|
-
(hasChanges ? '' : renderQuickCommitPushButton(s)) +
|
|
2988
|
-
'</div>' +
|
|
2989
|
-
'</section>';
|
|
2990
|
-
}
|
|
2991
|
-
|
|
2992
|
-
var subtitleHtml = subParts.map(escapeHtml).join(" · ");
|
|
2993
|
-
// Small "clean" badge shown inline in the header subtitle (replaces the old empty-state card).
|
|
2994
|
-
var cleanBadge = (!hasChanges && s.isGit !== false)
|
|
2995
|
-
? '<span class="qc-clean-badge" title="工作区干净,没有待提交的改动"><svg viewBox="0 0 16 16" width="11" height="11" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M13 4.5l-6 6L3 7"/></svg>干净</span>'
|
|
2996
|
-
: '';
|
|
2997
|
-
|
|
3011
|
+
var genBusy = state.quickCommitGenerating;
|
|
3012
|
+
var lc = s.lastCommit || {};
|
|
3013
|
+
var oldCommitHtml = lc.shortHash
|
|
3014
|
+
? '<code>' + escapeHtml(lc.shortHash) + '</code><span>' + escapeHtml(lc.subject || "") + '</span>'
|
|
3015
|
+
: (s.head ? '<code>' + escapeHtml(s.head.substring(0, 7)) + '</code>' : '<span class="qc-muted">无 commit</span>');
|
|
3016
|
+
var oldTagHtml = s.latestTag ? '<code>' + escapeHtml(s.latestTag) + '</code>' : '<span class="qc-muted">无 tag</span>';
|
|
3017
|
+
var newTagHtml = '<input type="text" id="quick-commit-tag" class="field-input qc-tag-field-input" placeholder="v1.2.0" value="' + escapeHtml(f.tag || "") + '"' + (state.quickCommitSubmitting ? ' disabled' : '') + '>';
|
|
3018
|
+
var nextCommitHtml = '<textarea id="quick-commit-message" class="field-input qc-message-input" rows="3" placeholder="New commit message" ' + (state.quickCommitSubmitting ? 'disabled' : '') + '>' + escapeHtml(f.customMessage || "") + '</textarea>';
|
|
3019
|
+
var subtitleParts = [];
|
|
3020
|
+
subtitleParts.push(s.branch || "(no branch)");
|
|
3021
|
+
subtitleParts.push(hasChanges ? ((s.modifiedCount || 0) + " 个改动") : "工作区干净");
|
|
3022
|
+
if (typeof s.ahead === "number" && s.ahead > 0) subtitleParts.push("↑" + s.ahead);
|
|
3023
|
+
if (typeof s.behind === "number" && s.behind > 0) subtitleParts.push("↓" + s.behind);
|
|
3024
|
+
var formPanel = state.quickCommitResult ? "" : '<section class="qc-release-panel">' +
|
|
3025
|
+
'<div class="qc-message-header">' +
|
|
3026
|
+
'<span class="qc-section-title">New</span>' +
|
|
3027
|
+
'<button type="button" id="quick-commit-ai-btn" class="btn btn-ghost btn-sm qc-ai-btn"' + (genBusy ? ' disabled' : '') + ' title="AI 生成 commit message 与 tag">' +
|
|
3028
|
+
'<svg viewBox="0 0 16 16" width="13" height="13" aria-hidden="true"><path d="M8 1.5l1.4 3.6L13 6.5 9.4 7.9 8 11.5 6.6 7.9 3 6.5l3.6-1.4L8 1.5zM12.5 10.5l.7 1.8 1.8.7-1.8.7-.7 1.8-.7-1.8-1.8-.7 1.8-.7.7-1.8z" fill="currentColor"/></svg>' +
|
|
3029
|
+
'<span>' + (genBusy ? '生成中...' : 'AI') + '</span>' +
|
|
3030
|
+
'</button>' +
|
|
3031
|
+
'</div>' +
|
|
3032
|
+
renderQuickCommitPair("Commit", oldCommitHtml, nextCommitHtml, "qc-pair--commit") +
|
|
3033
|
+
renderQuickCommitPair("Tag", oldTagHtml, newTagHtml, "qc-pair--tag") +
|
|
3034
|
+
(state.quickCommitError ? '<p class="error-message">' + escapeHtml(state.quickCommitError) + '</p>' : '') +
|
|
3035
|
+
renderQuickCommitDragControl(hasChanges) +
|
|
3036
|
+
'<div class="qc-modal-actions"><button id="quick-commit-cancel-btn" class="btn btn-ghost btn-sm" type="button">取消</button></div>' +
|
|
3037
|
+
'</section>';
|
|
3038
|
+
var resultPanel = renderQuickCommitResultPanel();
|
|
2998
3039
|
return '<section id="quick-commit-modal" class="modal-backdrop' + (state.quickCommitOpen ? '' : ' hidden') + '">' +
|
|
2999
3040
|
'<div class="modal quick-commit-modal" role="dialog" aria-labelledby="quick-commit-title">' +
|
|
3000
3041
|
'<div class="modal-header">' +
|
|
3001
3042
|
'<div>' +
|
|
3002
3043
|
'<h2 id="quick-commit-title" class="modal-title">快捷提交</h2>' +
|
|
3003
|
-
'<p class="modal-subtitle">' +
|
|
3044
|
+
'<p class="modal-subtitle">' + escapeHtml(subtitleParts.join(" · ")) + '</p>' +
|
|
3004
3045
|
'</div>' +
|
|
3005
3046
|
'<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>' +
|
|
3006
3047
|
'</div>' +
|
|
3007
3048
|
'<div class="modal-body">' +
|
|
3008
|
-
|
|
3009
|
-
|
|
3049
|
+
formPanel +
|
|
3050
|
+
resultPanel +
|
|
3010
3051
|
'</div>' +
|
|
3011
3052
|
'</div>' +
|
|
3012
3053
|
'</section>';
|
|
@@ -8444,9 +8485,16 @@
|
|
|
8444
8485
|
rows: 36,
|
|
8445
8486
|
autoResize: true,
|
|
8446
8487
|
cursorBlink: false,
|
|
8447
|
-
onData: function(
|
|
8448
|
-
|
|
8449
|
-
|
|
8488
|
+
onData: function() {
|
|
8489
|
+
// 物理键盘进 PTY 只允许在「终端交互(键盘透传)」开启时发生,而开启态那条
|
|
8490
|
+
// 路径由 captureTerminalInput(document keydown capture)独占处理——所以
|
|
8491
|
+
// wterm 自身的 onData 一律不再直接发:
|
|
8492
|
+
// · 关闭态(默认):用户点一下终端会触发 wterm 内部 _onClickFocus,让它的
|
|
8493
|
+
// 隐藏输入元素拿到焦点;之后敲的每个键都从 onData 冒出来。旧代码在这里
|
|
8494
|
+
// 直接 queueDirectInput,于是"没开透传也漏键进 PTY"(反复误触的根因)。
|
|
8495
|
+
// · 开启态:captureTerminalInput 已接管全部按键,onData 再发就是双份重复。
|
|
8496
|
+
// 两种状态都让路。要发命令请先开透传开关,或直接用输入框。
|
|
8497
|
+
return;
|
|
8450
8498
|
},
|
|
8451
8499
|
onResize: function(cols, rows) {
|
|
8452
8500
|
sendTerminalResize(cols, rows);
|