@co0ontty/wand 1.39.1 → 1.40.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/cli.js +0 -0
- package/dist/git-quick-commit.js +27 -0
- package/dist/process-manager.d.ts +1 -0
- package/dist/process-manager.js +134 -11
- package/dist/resume-policy.d.ts +1 -0
- package/dist/resume-policy.js +7 -1
- package/dist/server-session-routes.js +30 -10
- package/dist/types.d.ts +4 -0
- package/dist/web-ui/content/scripts.js +293 -203
- package/dist/web-ui/content/styles.css +335 -341
- package/package.json +1 -1
- package/dist/acp-protocol.d.ts +0 -67
- package/dist/acp-protocol.js +0 -291
- package/dist/claude-stream-adapter.d.ts +0 -35
- package/dist/claude-stream-adapter.js +0 -153
- package/dist/claude-structured-runner.d.ts +0 -27
- package/dist/claude-structured-runner.js +0 -106
- package/dist/message-parser.d.ts +0 -2
- package/dist/message-parser.js +0 -329
- package/dist/message-queue.d.ts +0 -57
- package/dist/message-queue.js +0 -127
- package/dist/session-lifecycle.d.ts +0 -81
- package/dist/session-lifecycle.js +0 -176
|
@@ -277,10 +277,9 @@
|
|
|
277
277
|
quickCommitSubmitting: false,
|
|
278
278
|
quickCommitGenerating: false,
|
|
279
279
|
quickCommitError: "",
|
|
280
|
-
//
|
|
281
|
-
//
|
|
282
|
-
|
|
283
|
-
quickCommitForm: { customMessage: "", makeTag: false, tag: "", primaryAction: "commit" },
|
|
280
|
+
// commitMode: "commit-tag" (commit + version tag) | "commit" (commit only).
|
|
281
|
+
// Pushing is a separate, standalone action — never bundled into the commit button.
|
|
282
|
+
quickCommitForm: { customMessage: "", tag: "", tagEdited: false, commitMode: "commit-tag" },
|
|
284
283
|
// Which inline panel/dropdown is open. Only one can be open at a time, so a
|
|
285
284
|
// single field beats juggling three sibling booleans with mutual-exclusion code.
|
|
286
285
|
// Values: null | "action" | "push" | "tag-head".
|
|
@@ -1531,8 +1530,14 @@
|
|
|
1531
1530
|
if (_macAppVersion) {
|
|
1532
1531
|
checkDmgAutoUpdate();
|
|
1533
1532
|
}
|
|
1534
|
-
|
|
1535
|
-
|
|
1533
|
+
// Warm up history in the background a beat after first paint so
|
|
1534
|
+
// the inline 历史会话 count is real (not "···") and recent CLI
|
|
1535
|
+
// sessions merge into the list without a manual expand. Deferred
|
|
1536
|
+
// to avoid competing with the initial session/output load.
|
|
1537
|
+
if (!state.claudeHistoryLoaded) {
|
|
1538
|
+
setTimeout(function() {
|
|
1539
|
+
if (!state.claudeHistoryLoaded) ensureClaudeHistoryLoaded();
|
|
1540
|
+
}, 600);
|
|
1536
1541
|
}
|
|
1537
1542
|
});
|
|
1538
1543
|
})
|
|
@@ -1785,7 +1790,9 @@
|
|
|
1785
1790
|
// isAnchored = 边栏占据布局空间(推开主内容)。桌面 pin 或 任意端窄条都算 anchored。
|
|
1786
1791
|
var isMobile = isMobileLayout();
|
|
1787
1792
|
var isCollapsed = !!state.sidebarPinned && !!state.sidebarCollapsed;
|
|
1788
|
-
|
|
1793
|
+
// 桌面端任何「可见」的侧栏都停靠(推开内容),绝不悬浮遮挡——避免主区被压到
|
|
1794
|
+
// 侧栏下面。pinned 只表示「锁定常驻」,open 则是临时可见,两者都算停靠。
|
|
1795
|
+
var isAnchored = isCollapsed || (!isMobile && (!!state.sidebarPinned || !!state.sessionsDrawerOpen));
|
|
1789
1796
|
var collapsedCls = isCollapsed ? ' sidebar-collapsed' : '';
|
|
1790
1797
|
var sidebarCollapsedCls = isCollapsed ? ' collapsed' : '';
|
|
1791
1798
|
return '<div class="app-container">' +
|
|
@@ -1814,7 +1821,10 @@
|
|
|
1814
1821
|
'</button>' +
|
|
1815
1822
|
'</div>' +
|
|
1816
1823
|
'</div>' +
|
|
1817
|
-
'<button id="sidebar-
|
|
1824
|
+
'<button id="sidebar-pin-btn" class="btn btn-ghost btn-sm sidebar-pin-toggle' + (state.sidebarPinned ? ' pinned' : '') + '" type="button" title="' + (state.sidebarPinned ? '已固定常驻(点击解除锁定)' : '固定侧栏常驻') + '" aria-label="' + (state.sidebarPinned ? '解除固定常驻' : '固定侧栏常驻') + '" aria-pressed="' + (state.sidebarPinned ? 'true' : 'false') + '">' +
|
|
1825
|
+
'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="12" y1="17" x2="12" y2="22"/><path d="M5 17h14v-1.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V6h1a2 2 0 0 0 0-4H8a2 2 0 0 0 0 4h1v4.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24z"/></svg>' +
|
|
1826
|
+
'</button>' +
|
|
1827
|
+
'<button id="sidebar-collapse-btn" class="btn btn-ghost btn-sm sidebar-collapse-toggle' + (isCollapsed ? ' collapsed' : '') + '" type="button" title="' + (isCollapsed ? '展开为全尺寸' : '收起为窄条') + '" aria-label="' + (isCollapsed ? '展开为全尺寸' : '收起为窄条') + '">' +
|
|
1818
1828
|
(isCollapsed
|
|
1819
1829
|
? '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="10 6 16 12 10 18"/><line x1="20" y1="5" x2="20" y2="19"/></svg>'
|
|
1820
1830
|
: '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="14 6 8 12 14 18"/><line x1="4" y1="5" x2="4" y2="19"/></svg>') +
|
|
@@ -1827,7 +1837,6 @@
|
|
|
1827
1837
|
'<div class="sessions-list" id="sessions-list">' + renderSessionsListContent() + '</div>' +
|
|
1828
1838
|
'</div>' +
|
|
1829
1839
|
'</div>' +
|
|
1830
|
-
'<div id="sidebar-history-region" class="sidebar-history-region">' + renderClaudeHistoryRegion() + '</div>' +
|
|
1831
1840
|
'<div class="sidebar-footer">' +
|
|
1832
1841
|
'<button id="drawer-new-session-button" class="btn btn-primary btn-block"><span>+</span> 新会话</button>' +
|
|
1833
1842
|
'<div class="sidebar-footer-actions">' +
|
|
@@ -2057,13 +2066,14 @@
|
|
|
2057
2066
|
'</div>' +
|
|
2058
2067
|
'</div>' +
|
|
2059
2068
|
renderExpandedShortcutsRow() +
|
|
2060
|
-
// Session info bar at bottom —
|
|
2069
|
+
// Session info bar at bottom — 仅保留信息类徽章(历史会话 id / exit code)。
|
|
2061
2070
|
// 自动批准已从这里移到主 pill 行(renderAutoApproveChip)。
|
|
2062
2071
|
(selectedSession
|
|
2063
2072
|
? (function() {
|
|
2064
2073
|
var bits = "";
|
|
2065
|
-
if (selectedSession.provider === "claude" && selectedSession.claudeSessionId) {
|
|
2066
|
-
|
|
2074
|
+
if ((selectedSession.provider === "claude" || selectedSession.provider === "codex") && selectedSession.claudeSessionId) {
|
|
2075
|
+
var historyIdTitle = selectedSession.provider === "codex" ? "点击复制 Codex thread ID" : "点击复制 Claude 会话 ID";
|
|
2076
|
+
bits += '<span id="claude-session-id-badge" class="claude-session-id-badge" data-claude-id="' + escapeHtml(selectedSession.claudeSessionId) + '" title="' + historyIdTitle + '">' + iconSvg("cloud", { size: 11, strokeWidth: 1.7, cls: "claude-session-id-icon" }) + '<span class="claude-session-id-text">' + escapeHtml(selectedSession.claudeSessionId.slice(0, 8)) + '</span></span>';
|
|
2067
2077
|
}
|
|
2068
2078
|
// 非结构化会话:进程退出后展示退出码(哪怕 0,告诉用户已正常结束)。
|
|
2069
2079
|
// 结构化会话:只在退出码非 0(即真有失败)时展示,避免成功的多轮对话也挂个 "退出码=0" 误导。
|
|
@@ -2162,7 +2172,8 @@
|
|
|
2162
2172
|
|
|
2163
2173
|
var infoItems = "";
|
|
2164
2174
|
if (hasClaudeId) {
|
|
2165
|
-
|
|
2175
|
+
var historyIdLabel = session.provider === "codex" ? "复制 Codex thread ID" : "复制 Claude 会话 ID";
|
|
2176
|
+
infoItems += '<button class="topbar-more-item" data-action="copy-claude-session-id" type="button" role="menuitem">' + cloudIconSvg + '<span>' + historyIdLabel + '</span></button>';
|
|
2166
2177
|
}
|
|
2167
2178
|
if (hasCwd) {
|
|
2168
2179
|
infoItems += '<button class="topbar-more-item" data-action="copy-cwd" type="button" role="menuitem">' + folderIconSvg + '<span>复制工作目录</span></button>';
|
|
@@ -2232,16 +2243,17 @@
|
|
|
2232
2243
|
var quickCommitEscHandler = null;
|
|
2233
2244
|
var quickCommitDocClickHandler = null;
|
|
2234
2245
|
|
|
2235
|
-
// Restore the user's last
|
|
2236
|
-
|
|
2246
|
+
// Restore the user's last commit-mode choice so the split button feels sticky.
|
|
2247
|
+
// Modes: "commit-tag" (commit + version tag) | "commit" (commit only).
|
|
2248
|
+
function readSavedCommitMode() {
|
|
2237
2249
|
try {
|
|
2238
|
-
var v = localStorage.getItem("wand.quickCommit.
|
|
2239
|
-
if (v === "commit" || v === "commit-
|
|
2250
|
+
var v = localStorage.getItem("wand.quickCommit.commitMode");
|
|
2251
|
+
if (v === "commit" || v === "commit-tag") return v;
|
|
2240
2252
|
} catch (e) { /* localStorage may be blocked */ }
|
|
2241
|
-
return "commit";
|
|
2253
|
+
return "commit-tag"; // default: commit + tag
|
|
2242
2254
|
}
|
|
2243
|
-
function
|
|
2244
|
-
try { localStorage.setItem("wand.quickCommit.
|
|
2255
|
+
function saveCommitMode(value) {
|
|
2256
|
+
try { localStorage.setItem("wand.quickCommit.commitMode", value); } catch (e) { /* no-op */ }
|
|
2245
2257
|
}
|
|
2246
2258
|
|
|
2247
2259
|
function openQuickCommitModal() {
|
|
@@ -2251,9 +2263,11 @@
|
|
|
2251
2263
|
state.quickCommitError = "";
|
|
2252
2264
|
state.quickCommitForm = {
|
|
2253
2265
|
customMessage: "",
|
|
2254
|
-
makeTag: false,
|
|
2255
2266
|
tag: "",
|
|
2256
|
-
|
|
2267
|
+
// Whether the user has manually edited the tag (so we stop auto-overwriting it).
|
|
2268
|
+
tagEdited: false,
|
|
2269
|
+
// "commit-tag" → commit + version tag; "commit" → commit only.
|
|
2270
|
+
commitMode: readSavedCommitMode(),
|
|
2257
2271
|
};
|
|
2258
2272
|
state.quickCommitOpenMenu = null;
|
|
2259
2273
|
state.quickCommitTagHeadForm = { tag: "", push: false };
|
|
@@ -2303,6 +2317,12 @@
|
|
|
2303
2317
|
document.addEventListener("click", quickCommitDocClickHandler, true);
|
|
2304
2318
|
loadGitStatus(state.selectedId, { force: true }).then(function() {
|
|
2305
2319
|
if (!state.quickCommitOpen) return;
|
|
2320
|
+
// Seed the tag field with the locally-derived suggestion so a tag is
|
|
2321
|
+
// always shown by default (greyed until the toggle is turned on).
|
|
2322
|
+
var st = state.gitStatus || {};
|
|
2323
|
+
if (!state.quickCommitForm.tagEdited && st.suggestedTag) {
|
|
2324
|
+
state.quickCommitForm.tag = st.suggestedTag;
|
|
2325
|
+
}
|
|
2306
2326
|
rerenderQuickCommitModal();
|
|
2307
2327
|
});
|
|
2308
2328
|
}
|
|
@@ -2354,23 +2374,31 @@
|
|
|
2354
2374
|
var aiBtn = document.getElementById("quick-commit-ai-btn");
|
|
2355
2375
|
if (aiBtn) aiBtn.addEventListener("click", generateCommitMessageAI);
|
|
2356
2376
|
var msgEl = document.getElementById("quick-commit-message");
|
|
2357
|
-
if (msgEl)
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
});
|
|
2377
|
+
if (msgEl) {
|
|
2378
|
+
msgEl.addEventListener("input", function() {
|
|
2379
|
+
state.quickCommitForm.customMessage = msgEl.value;
|
|
2380
|
+
});
|
|
2381
|
+
// Cmd/Ctrl+Enter submits, matching the common editor shortcut.
|
|
2382
|
+
msgEl.addEventListener("keydown", function(e) {
|
|
2383
|
+
if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
|
|
2384
|
+
e.preventDefault();
|
|
2385
|
+
submitQuickCommit();
|
|
2386
|
+
}
|
|
2387
|
+
});
|
|
2388
|
+
}
|
|
2370
2389
|
var tagInput = document.getElementById("quick-commit-tag");
|
|
2371
|
-
if (tagInput)
|
|
2372
|
-
|
|
2373
|
-
|
|
2390
|
+
if (tagInput) {
|
|
2391
|
+
tagInput.addEventListener("input", function() {
|
|
2392
|
+
state.quickCommitForm.tag = tagInput.value;
|
|
2393
|
+
state.quickCommitForm.tagEdited = true;
|
|
2394
|
+
});
|
|
2395
|
+
tagInput.addEventListener("keydown", function(e) {
|
|
2396
|
+
if (e.key === "Enter") {
|
|
2397
|
+
e.preventDefault();
|
|
2398
|
+
submitQuickCommit();
|
|
2399
|
+
}
|
|
2400
|
+
});
|
|
2401
|
+
}
|
|
2374
2402
|
|
|
2375
2403
|
var actionCaret = document.getElementById("quick-commit-action-caret");
|
|
2376
2404
|
if (actionCaret) actionCaret.addEventListener("click", function(e) {
|
|
@@ -2380,17 +2408,24 @@
|
|
|
2380
2408
|
});
|
|
2381
2409
|
var actionMenu = document.getElementById("quick-commit-action-menu");
|
|
2382
2410
|
if (actionMenu) {
|
|
2383
|
-
var actionItems = actionMenu.querySelectorAll("[data-qc-
|
|
2411
|
+
var actionItems = actionMenu.querySelectorAll("[data-qc-commit-mode]");
|
|
2384
2412
|
for (var i = 0; i < actionItems.length; i++) {
|
|
2385
2413
|
(function(btn) {
|
|
2386
2414
|
btn.addEventListener("click", function() {
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2415
|
+
// Keep what the user typed before the re-render.
|
|
2416
|
+
var liveTag = document.getElementById("quick-commit-tag");
|
|
2417
|
+
if (liveTag && !liveTag.disabled) state.quickCommitForm.tag = liveTag.value;
|
|
2418
|
+
var value = btn.getAttribute("data-qc-commit-mode");
|
|
2419
|
+
if (value === "commit" || value === "commit-tag") {
|
|
2420
|
+
state.quickCommitForm.commitMode = value;
|
|
2421
|
+
saveCommitMode(value);
|
|
2391
2422
|
}
|
|
2392
2423
|
state.quickCommitOpenMenu = null;
|
|
2393
2424
|
rerenderQuickCommitModal();
|
|
2425
|
+
if (value === "commit-tag") {
|
|
2426
|
+
var inp = document.getElementById("quick-commit-tag");
|
|
2427
|
+
if (inp) setTimeout(function() { inp.focus(); var v = inp.value; inp.value = ""; inp.value = v; }, 0);
|
|
2428
|
+
}
|
|
2394
2429
|
});
|
|
2395
2430
|
})(actionItems[i]);
|
|
2396
2431
|
}
|
|
@@ -2400,7 +2435,14 @@
|
|
|
2400
2435
|
if (tagHeadToggle) tagHeadToggle.addEventListener("click", function() {
|
|
2401
2436
|
var willOpen = state.quickCommitOpenMenu !== "tag-head";
|
|
2402
2437
|
state.quickCommitOpenMenu = willOpen ? "tag-head" : null;
|
|
2403
|
-
if (willOpen)
|
|
2438
|
+
if (willOpen) {
|
|
2439
|
+
state.quickCommitTagHeadError = "";
|
|
2440
|
+
// Pre-fill with the locally-derived suggestion (unless already set).
|
|
2441
|
+
var sug = (state.gitStatus || {}).suggestedTag;
|
|
2442
|
+
if (sug && !(state.quickCommitTagHeadForm.tag || "").trim()) {
|
|
2443
|
+
state.quickCommitTagHeadForm.tag = sug;
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2404
2446
|
rerenderQuickCommitModal();
|
|
2405
2447
|
if (willOpen) {
|
|
2406
2448
|
var inp = document.getElementById("quick-commit-tag-head-input");
|
|
@@ -2487,14 +2529,19 @@
|
|
|
2487
2529
|
var data = result.data || {};
|
|
2488
2530
|
var aiMessage = typeof data.message === "string" ? data.message : "";
|
|
2489
2531
|
var aiTag = typeof data.suggestedTag === "string" ? data.suggestedTag.trim() : "";
|
|
2490
|
-
//
|
|
2532
|
+
// "AI 生成" recommends BOTH a commit message and a version tag.
|
|
2533
|
+
// Fill the message only when empty (never clobber what the user typed).
|
|
2491
2534
|
var currentMessage = (state.quickCommitForm.customMessage || "").trim();
|
|
2492
2535
|
if (!currentMessage && aiMessage) {
|
|
2493
2536
|
state.quickCommitForm.customMessage = aiMessage;
|
|
2494
2537
|
}
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2538
|
+
// Adopt the AI tag (smarter than the local patch-bump default) unless the
|
|
2539
|
+
// user has manually edited it, and switch to "commit + tag" so the
|
|
2540
|
+
// recommendation is actually applied on commit.
|
|
2541
|
+
if (aiTag) {
|
|
2542
|
+
if (!state.quickCommitForm.tagEdited) state.quickCommitForm.tag = aiTag;
|
|
2543
|
+
state.quickCommitForm.commitMode = "commit-tag";
|
|
2544
|
+
saveCommitMode("commit-tag");
|
|
2498
2545
|
}
|
|
2499
2546
|
})
|
|
2500
2547
|
.catch(function(error) {
|
|
@@ -2513,21 +2560,22 @@
|
|
|
2513
2560
|
var tagEl = document.getElementById("quick-commit-tag");
|
|
2514
2561
|
if (tagEl) state.quickCommitForm.tag = tagEl.value;
|
|
2515
2562
|
var form = state.quickCommitForm || {};
|
|
2516
|
-
var
|
|
2563
|
+
var withTag = form.commitMode === "commit-tag";
|
|
2564
|
+
var userTag = withTag ? (form.tag || "").trim() : "";
|
|
2517
2565
|
var message = (form.customMessage || "").trim();
|
|
2518
2566
|
if (!message) {
|
|
2519
|
-
state.quickCommitError = "
|
|
2567
|
+
state.quickCommitError = "请填写提交信息,或点击「AI 生成」。";
|
|
2520
2568
|
rerenderQuickCommitModal();
|
|
2521
2569
|
return;
|
|
2522
2570
|
}
|
|
2523
|
-
|
|
2524
|
-
//
|
|
2571
|
+
// Commit no longer pushes — pushing is a separate, standalone action.
|
|
2572
|
+
// 选了「提交并打 Tag」但 tag 留空 → 由后端在提交时调 AI 生成。
|
|
2525
2573
|
var payload = {
|
|
2526
2574
|
autoMessage: false,
|
|
2527
2575
|
customMessage: message,
|
|
2528
2576
|
tag: userTag,
|
|
2529
|
-
autoTag: !!(
|
|
2530
|
-
push:
|
|
2577
|
+
autoTag: !!(withTag && !userTag),
|
|
2578
|
+
push: false
|
|
2531
2579
|
};
|
|
2532
2580
|
state.quickCommitSubmitting = true;
|
|
2533
2581
|
state.quickCommitError = "";
|
|
@@ -2550,14 +2598,8 @@
|
|
|
2550
2598
|
var subPrefix = subCommits.length > 0
|
|
2551
2599
|
? "已先提交 " + subCommits.length + " 个 submodule(" + subCommits.map(function(c) { return c.path; }).join("、") + "),"
|
|
2552
2600
|
: "";
|
|
2553
|
-
var base = subPrefix + "已提交" + (hash ? " " + hash : "") + (tagName ? ",已打
|
|
2554
|
-
if (
|
|
2555
|
-
var msg = base + ";push 失败:" + data.pushError;
|
|
2556
|
-
if (typeof showToast === "function") showToast(msg, "error");
|
|
2557
|
-
} else {
|
|
2558
|
-
var okMsg = base + (data.pushed ? ",已 push" : "");
|
|
2559
|
-
if (typeof showToast === "function") showToast(okMsg, "success");
|
|
2560
|
-
}
|
|
2601
|
+
var base = subPrefix + "已提交" + (hash ? " " + hash : "") + (tagName ? ",已打 Tag " + tagName : "");
|
|
2602
|
+
if (typeof showToast === "function") showToast(base + "。可在「同步」区推送到远端。", "success");
|
|
2561
2603
|
closeQuickCommitModal();
|
|
2562
2604
|
if (state.selectedId) loadGitStatus(state.selectedId, { force: true });
|
|
2563
2605
|
})
|
|
@@ -2697,16 +2739,37 @@
|
|
|
2697
2739
|
});
|
|
2698
2740
|
}
|
|
2699
2741
|
|
|
2742
|
+
// Map a porcelain status (XY two-char, e.g. " M", "A.", "??") to a single
|
|
2743
|
+
// VS-Code-style letter badge: pick the first meaningful char, color by kind.
|
|
2744
|
+
function qcStatusBadge(status) {
|
|
2745
|
+
var raw = (status || "").trim();
|
|
2746
|
+
if (raw === "??") return { letter: "U", cls: "untracked", title: "未跟踪" };
|
|
2747
|
+
if (raw === "!!") return { letter: "I", cls: "ignored", title: "已忽略" };
|
|
2748
|
+
var c = "";
|
|
2749
|
+
for (var i = 0; i < status.length; i++) {
|
|
2750
|
+
if (status[i] && status[i] !== "." && status[i] !== " ") { c = status[i]; break; }
|
|
2751
|
+
}
|
|
2752
|
+
c = (c || raw[0] || "?").toUpperCase();
|
|
2753
|
+
var map = {
|
|
2754
|
+
A: { cls: "add", title: "新增" },
|
|
2755
|
+
M: { cls: "mod", title: "修改" },
|
|
2756
|
+
D: { cls: "del", title: "删除" },
|
|
2757
|
+
R: { cls: "ren", title: "重命名" },
|
|
2758
|
+
C: { cls: "ren", title: "复制" },
|
|
2759
|
+
T: { cls: "mod", title: "类型变更" },
|
|
2760
|
+
U: { cls: "del", title: "冲突" }
|
|
2761
|
+
};
|
|
2762
|
+
var hit = map[c] || { cls: "other", title: "已更改" };
|
|
2763
|
+
return { letter: c, cls: hit.cls, title: hit.title };
|
|
2764
|
+
}
|
|
2765
|
+
|
|
2700
2766
|
function renderQuickCommitFileRows(files) {
|
|
2701
2767
|
var rows = files.map(function(item) {
|
|
2702
|
-
var
|
|
2703
|
-
var
|
|
2704
|
-
var
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
else if (flag === "M" || status[0] === "M") cls += " qc-flag-mod";
|
|
2708
|
-
else if (flag === "??" || status === "??") cls += " qc-flag-untracked";
|
|
2709
|
-
else if (flag === "R") cls += " qc-flag-ren";
|
|
2768
|
+
var badge = qcStatusBadge(item.status || "");
|
|
2769
|
+
var fullPath = item.path || "";
|
|
2770
|
+
var slash = fullPath.lastIndexOf("/");
|
|
2771
|
+
var dir = slash >= 0 ? fullPath.slice(0, slash + 1) : "";
|
|
2772
|
+
var base = slash >= 0 ? fullPath.slice(slash + 1) : fullPath;
|
|
2710
2773
|
var subBadge = "";
|
|
2711
2774
|
if (item.isSubmodule) {
|
|
2712
2775
|
var st = item.submoduleState || {};
|
|
@@ -2717,7 +2780,13 @@
|
|
|
2717
2780
|
var label = parts.length ? "submodule · " + parts.join(" / ") : "submodule";
|
|
2718
2781
|
subBadge = '<span class="qc-submodule-badge">' + escapeHtml(label) + '</span>';
|
|
2719
2782
|
}
|
|
2720
|
-
return '<div class="qc-file-row"
|
|
2783
|
+
return '<div class="qc-file-row" title="' + escapeHtml(fullPath) + '">' +
|
|
2784
|
+
'<span class="qc-file-badge qc-badge-' + badge.cls + '" title="' + escapeHtml(badge.title) + '">' + escapeHtml(badge.letter) + '</span>' +
|
|
2785
|
+
'<span class="qc-file-path">' +
|
|
2786
|
+
(dir ? '<span class="qc-file-dir">' + escapeHtml(dir) + '</span>' : '') +
|
|
2787
|
+
'<span class="qc-file-name">' + escapeHtml(base) + '</span>' +
|
|
2788
|
+
'</span>' + subBadge +
|
|
2789
|
+
'</div>';
|
|
2721
2790
|
}).join("");
|
|
2722
2791
|
return rows || '<div class="qc-empty">没有可提交的改动。</div>';
|
|
2723
2792
|
}
|
|
@@ -2726,23 +2795,25 @@
|
|
|
2726
2795
|
return state.quickCommitSubmitting || state.quickCommitTagHeadSubmitting || state.quickCommitPushing;
|
|
2727
2796
|
}
|
|
2728
2797
|
|
|
2729
|
-
function
|
|
2798
|
+
function renderQuickCommitCommitButton(hasChanges) {
|
|
2730
2799
|
var f = state.quickCommitForm;
|
|
2800
|
+
var withTag = f.commitMode === "commit-tag";
|
|
2731
2801
|
var label;
|
|
2732
2802
|
if (state.quickCommitSubmitting) label = "提交中…";
|
|
2733
|
-
else
|
|
2734
|
-
else label = "仅提交";
|
|
2803
|
+
else label = withTag ? "提交并打 Tag" : "提交";
|
|
2735
2804
|
var disabled = !hasChanges || isQuickCommitOpInFlight();
|
|
2736
2805
|
var menuOpen = state.quickCommitOpenMenu === "action";
|
|
2737
2806
|
var caretActive = menuOpen ? " is-active" : "";
|
|
2738
2807
|
var menuItems = [
|
|
2739
|
-
{ value: "commit", label: "
|
|
2740
|
-
{ value: "commit
|
|
2808
|
+
{ value: "commit-tag", label: "提交并打 Tag", desc: "创建 commit,并为它打一个版本 Tag" },
|
|
2809
|
+
{ value: "commit", label: "仅提交", desc: "只创建 commit,不打 Tag" }
|
|
2741
2810
|
];
|
|
2742
2811
|
var menuHtml = menuItems.map(function(item) {
|
|
2743
|
-
var sel = f.
|
|
2744
|
-
return '<button type="button" class="qc-dropdown-item' + sel + '" data-qc-
|
|
2745
|
-
'<span class="qc-dropdown-item-
|
|
2812
|
+
var sel = f.commitMode === item.value ? " is-selected" : "";
|
|
2813
|
+
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') + '">' +
|
|
2814
|
+
'<span class="qc-dropdown-item-main"><span class="qc-dropdown-check" aria-hidden="true">' +
|
|
2815
|
+
(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>' : '') +
|
|
2816
|
+
'</span><span class="qc-dropdown-item-title">' + escapeHtml(item.label) + '</span></span>' +
|
|
2746
2817
|
'<span class="qc-dropdown-item-desc">' + escapeHtml(item.desc) + '</span>' +
|
|
2747
2818
|
'</button>';
|
|
2748
2819
|
}).join("");
|
|
@@ -2750,7 +2821,7 @@
|
|
|
2750
2821
|
'<button id="quick-commit-submit-btn" class="btn btn-primary qc-split-main" type="button"' + (disabled ? ' disabled' : '') + '>' +
|
|
2751
2822
|
escapeHtml(label) +
|
|
2752
2823
|
'</button>' +
|
|
2753
|
-
'<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="
|
|
2824
|
+
'<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="切换提交方式">' +
|
|
2754
2825
|
'<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>' +
|
|
2755
2826
|
'</button>' +
|
|
2756
2827
|
(menuOpen ?
|
|
@@ -2781,18 +2852,19 @@
|
|
|
2781
2852
|
var submitting = state.quickCommitTagHeadSubmitting;
|
|
2782
2853
|
var generating = state.quickCommitTagHeadGenerating;
|
|
2783
2854
|
return '<div class="qc-tag-head-panel">' +
|
|
2855
|
+
'<div class="qc-tag-head-hint">为当前最新提交(HEAD)打 Tag,不创建新提交。</div>' +
|
|
2784
2856
|
'<div class="qc-tag-head-row">' +
|
|
2785
|
-
'<input type="text" id="quick-commit-tag-head-input" class="field-input" placeholder="
|
|
2857
|
+
'<input type="text" id="quick-commit-tag-head-input" class="field-input" placeholder="版本号,如 v1.2.0" value="' + escapeHtml(thf.tag || "") + '"' + (submitting ? ' disabled' : '') + '>' +
|
|
2786
2858
|
'<button type="button" id="quick-commit-tag-head-ai" class="btn btn-ghost btn-sm"' + (generating || submitting ? ' disabled' : '') + '>' + (generating ? '建议中…' : 'AI 建议') + '</button>' +
|
|
2787
2859
|
'</div>' +
|
|
2788
2860
|
'<label class="qc-tag-head-push">' +
|
|
2789
2861
|
'<input type="checkbox" id="quick-commit-tag-head-push"' + (thf.push ? ' checked' : '') + (submitting ? ' disabled' : '') + '>' +
|
|
2790
|
-
'<span
|
|
2862
|
+
'<span>打完后立即推送这个 Tag 到远端</span>' +
|
|
2791
2863
|
'</label>' +
|
|
2792
2864
|
(state.quickCommitTagHeadError ? '<p class="error-message">' + escapeHtml(state.quickCommitTagHeadError) + '</p>' : '') +
|
|
2793
2865
|
'<div class="qc-tag-head-actions">' +
|
|
2794
2866
|
'<button type="button" id="quick-commit-tag-head-cancel" class="btn btn-ghost btn-sm"' + (submitting ? ' disabled' : '') + '>收起</button>' +
|
|
2795
|
-
'<button type="button" id="quick-commit-tag-head-submit" class="btn btn-secondary btn-sm"' + (submitting ? ' disabled' : '') + '>' + (submitting ? '打
|
|
2867
|
+
'<button type="button" id="quick-commit-tag-head-submit" class="btn btn-secondary btn-sm"' + (submitting ? ' disabled' : '') + '>' + (submitting ? '打 Tag 中…' : '打 Tag') + '</button>' +
|
|
2796
2868
|
'</div>' +
|
|
2797
2869
|
'</div>';
|
|
2798
2870
|
}
|
|
@@ -2835,56 +2907,53 @@
|
|
|
2835
2907
|
|
|
2836
2908
|
function renderQuickCommitModal() {
|
|
2837
2909
|
var s = state.gitStatus || {};
|
|
2838
|
-
var f = state.quickCommitForm || { customMessage: "",
|
|
2910
|
+
var f = state.quickCommitForm || { customMessage: "", tag: "", tagEdited: false, commitMode: "commit-tag" };
|
|
2839
2911
|
var hasChanges = (s.modifiedCount || 0) > 0;
|
|
2840
2912
|
var files = Array.isArray(s.files) ? s.files : [];
|
|
2841
2913
|
var fileRows = renderQuickCommitFileRows(files);
|
|
2842
2914
|
|
|
2843
|
-
// Subtitle: branch · N 改动 · ↑X ↓Y
|
|
2915
|
+
// Subtitle: branch · N 改动 · ↑X ↓Y (clean repos show a small ✓ badge instead of "0 个改动")
|
|
2844
2916
|
var subParts = [];
|
|
2845
2917
|
subParts.push(s.branch || "(no branch)");
|
|
2846
|
-
subParts.push((s.modifiedCount || 0) + " 个改动");
|
|
2918
|
+
if (hasChanges) subParts.push((s.modifiedCount || 0) + " 个改动");
|
|
2847
2919
|
if (typeof s.ahead === "number" && s.ahead > 0) subParts.push("↑" + s.ahead);
|
|
2848
2920
|
if (typeof s.behind === "number" && s.behind > 0) subParts.push("↓" + s.behind);
|
|
2849
2921
|
|
|
2850
2922
|
// Section 1: changes + commit form (only when there are changes)
|
|
2851
2923
|
var section1 = "";
|
|
2852
2924
|
if (hasChanges) {
|
|
2925
|
+
var genBusy = state.quickCommitGenerating;
|
|
2926
|
+
var withTag = f.commitMode === "commit-tag";
|
|
2853
2927
|
section1 = '<section class="qc-section qc-section--changes">' +
|
|
2854
|
-
'<div class="qc-section-head"><span class="qc-section-title"
|
|
2928
|
+
'<div class="qc-section-head"><span class="qc-section-title">更改</span><span class="qc-section-meta">' + escapeHtml(s.modifiedCount + " 个文件") + '</span></div>' +
|
|
2855
2929
|
'<div class="qc-files-wrap">' + fileRows + '</div>' +
|
|
2856
2930
|
'<div class="qc-message-row" id="quick-commit-message-row">' +
|
|
2857
|
-
'<div class="qc-message-header"
|
|
2858
|
-
'<
|
|
2859
|
-
|
|
2860
|
-
'<
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
'<input type="checkbox" id="quick-commit-make-tag" class="switch-toggle" aria-label="同时为本次 commit 打 tag"' + (f.makeTag ? ' checked' : '') + ((state.quickCommitSubmitting || state.quickCommitGenerating) ? ' disabled' : '') + '>' +
|
|
2864
|
-
'<span class="switch-slider"></span>' +
|
|
2865
|
-
'</span>' +
|
|
2866
|
-
'</label>' +
|
|
2867
|
-
'</div>' +
|
|
2931
|
+
'<div class="qc-message-header">' +
|
|
2932
|
+
'<label class="field-label qc-message-label" for="quick-commit-message">提交信息</label>' +
|
|
2933
|
+
'<button type="button" id="quick-commit-ai-btn" class="btn btn-ghost btn-sm qc-ai-btn"' + (genBusy ? ' disabled' : '') + ' title="读取改动,用 AI 生成提交信息与版本 Tag">' +
|
|
2934
|
+
'<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>' +
|
|
2935
|
+
'<span>' + (genBusy ? '生成中…' : 'AI 生成') + '</span>' +
|
|
2936
|
+
'</button>' +
|
|
2868
2937
|
'</div>' +
|
|
2869
|
-
'<textarea id="quick-commit-message" class="field-input" rows="
|
|
2938
|
+
'<textarea id="quick-commit-message" class="field-input qc-message-input" rows="3" placeholder="描述这次改动;或点「AI 生成」自动填写" ' + (state.quickCommitSubmitting ? 'disabled' : '') + '>' + escapeHtml(f.customMessage || "") + '</textarea>' +
|
|
2870
2939
|
'</div>' +
|
|
2871
|
-
'<div class="qc-tag-
|
|
2872
|
-
'<
|
|
2940
|
+
'<div class="qc-tag-field' + (withTag ? '' : ' is-off') + '">' +
|
|
2941
|
+
'<span class="qc-tag-field-label" title="' + (withTag ? '这次提交会打上这个版本 Tag' : '当前为「仅提交」,不会打 Tag') + '">Tag</span>' +
|
|
2942
|
+
'<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' : '') + '>' +
|
|
2943
|
+
(withTag ? '' : '<span class="qc-tag-field-note">仅提交</span>') +
|
|
2873
2944
|
'</div>' +
|
|
2874
2945
|
(state.quickCommitError ? '<p class="error-message">' + escapeHtml(state.quickCommitError) + '</p>' : '') +
|
|
2875
2946
|
'<div class="qc-section-actions">' +
|
|
2876
2947
|
'<button id="quick-commit-cancel-btn" class="btn btn-ghost btn-sm" type="button">取消</button>' +
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
section1 = '<section class="qc-section qc-section--empty">' +
|
|
2882
|
-
'<div class="qc-empty-state">' +
|
|
2883
|
-
'<span class="qc-empty-icon">✓</span>' +
|
|
2884
|
-
'<div><div class="qc-empty-title">工作区干净</div><div class="qc-empty-sub">没有暂存改动可提交。仍可在下方为 HEAD 打 tag 或推送。</div></div>' +
|
|
2948
|
+
'<div class="qc-action-group">' +
|
|
2949
|
+
renderQuickCommitCommitButton(hasChanges) +
|
|
2950
|
+
renderQuickCommitPushButton(s) +
|
|
2951
|
+
'</div>' +
|
|
2885
2952
|
'</div>' +
|
|
2886
2953
|
'</section>';
|
|
2887
2954
|
}
|
|
2955
|
+
// When clean, we skip the big "changes" card entirely — a small green
|
|
2956
|
+
// indicator in the header subtitle is enough of a signal (see below).
|
|
2888
2957
|
|
|
2889
2958
|
// Section 2: repo status + secondary actions (always show when there's at least one commit)
|
|
2890
2959
|
var section2 = "";
|
|
@@ -2892,32 +2961,38 @@
|
|
|
2892
2961
|
var lc = s.lastCommit || {};
|
|
2893
2962
|
var headLine = lc.shortHash ? lc.shortHash + " · " + (lc.subject || "") : (s.head ? s.head.substring(0, 7) : "(no commit)");
|
|
2894
2963
|
var upstreamLine = s.upstream ? escapeHtml(s.branch || "") + " → " + escapeHtml(s.upstream) : escapeHtml(s.branch || "(no branch)") + " · 无 upstream";
|
|
2964
|
+
var tagHeadOpen = state.quickCommitOpenMenu === "tag-head";
|
|
2895
2965
|
section2 = '<section class="qc-section qc-section--repo">' +
|
|
2896
|
-
'<div class="qc-section-head"><span class="qc-section-title"
|
|
2966
|
+
'<div class="qc-section-head"><span class="qc-section-title">仓库 · 同步</span><span class="qc-section-meta">' + upstreamLine + '</span></div>' +
|
|
2897
2967
|
'<div class="qc-head-card">' +
|
|
2898
2968
|
'<span class="qc-head-label">HEAD</span>' +
|
|
2899
2969
|
'<code class="qc-head-text">' + escapeHtml(headLine) + '</code>' +
|
|
2900
2970
|
'</div>' +
|
|
2901
2971
|
renderQuickCommitStatusChips(s) +
|
|
2902
|
-
(
|
|
2972
|
+
(tagHeadOpen ? renderQuickCommitTagHeadPanel() : '') +
|
|
2903
2973
|
'<div class="qc-section-actions qc-section-actions--secondary">' +
|
|
2904
|
-
'<button id="quick-commit-tag-head-toggle" class="btn btn-
|
|
2905
|
-
'<svg viewBox="0 0 16 16" width="14" height="14" aria-hidden="true"
|
|
2906
|
-
|
|
2974
|
+
'<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,不会创建新提交">' +
|
|
2975
|
+
'<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>' +
|
|
2976
|
+
'<span>' + (tagHeadOpen ? '收起' : '为当前提交打 Tag') + '</span>' +
|
|
2907
2977
|
'</button>' +
|
|
2908
|
-
|
|
2978
|
+
// Push lives in the commit footer when there are changes; show it here otherwise.
|
|
2979
|
+
(hasChanges ? '' : renderQuickCommitPushButton(s)) +
|
|
2909
2980
|
'</div>' +
|
|
2910
2981
|
'</section>';
|
|
2911
2982
|
}
|
|
2912
2983
|
|
|
2913
2984
|
var subtitleHtml = subParts.map(escapeHtml).join(" · ");
|
|
2985
|
+
// Small "clean" badge shown inline in the header subtitle (replaces the old empty-state card).
|
|
2986
|
+
var cleanBadge = (!hasChanges && s.isGit !== false)
|
|
2987
|
+
? '<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>'
|
|
2988
|
+
: '';
|
|
2914
2989
|
|
|
2915
2990
|
return '<section id="quick-commit-modal" class="modal-backdrop' + (state.quickCommitOpen ? '' : ' hidden') + '">' +
|
|
2916
2991
|
'<div class="modal quick-commit-modal" role="dialog" aria-labelledby="quick-commit-title">' +
|
|
2917
2992
|
'<div class="modal-header">' +
|
|
2918
2993
|
'<div>' +
|
|
2919
2994
|
'<h2 id="quick-commit-title" class="modal-title">快捷提交</h2>' +
|
|
2920
|
-
'<p class="modal-subtitle">' + subtitleHtml + '</p>' +
|
|
2995
|
+
'<p class="modal-subtitle">' + subtitleHtml + cleanBadge + '</p>' +
|
|
2921
2996
|
'</div>' +
|
|
2922
2997
|
'<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>' +
|
|
2923
2998
|
'</div>' +
|
|
@@ -3509,14 +3584,16 @@
|
|
|
3509
3584
|
}
|
|
3510
3585
|
|
|
3511
3586
|
function renderSessions() {
|
|
3512
|
-
// Claude history is
|
|
3513
|
-
//
|
|
3514
|
-
//
|
|
3587
|
+
// Claude/Codex history is rendered INLINE as the final collapsible
|
|
3588
|
+
// group of this same scrolling list (see renderClaudeHistoryGroup),
|
|
3589
|
+
// styled like 已归档. There is no separate docked region anymore —
|
|
3590
|
+
// that previously stranded an empty void above the footer.
|
|
3515
3591
|
var archivedSessions = state.sessions.filter(function(session) { return session.archived; });
|
|
3516
3592
|
var groups = [];
|
|
3517
3593
|
groups.push(renderSessionManageBar());
|
|
3518
3594
|
|
|
3519
3595
|
var recentEntries = getRecentEntries();
|
|
3596
|
+
var historyGroup = renderClaudeHistoryGroup();
|
|
3520
3597
|
|
|
3521
3598
|
if (recentEntries.length > 0) {
|
|
3522
3599
|
groups.push(renderRecentGroup(recentEntries));
|
|
@@ -3524,9 +3601,12 @@
|
|
|
3524
3601
|
if (archivedSessions.length > 0) {
|
|
3525
3602
|
groups.push(renderArchivedGroup(archivedSessions));
|
|
3526
3603
|
}
|
|
3527
|
-
if (recentEntries.length === 0 && archivedSessions.length === 0) {
|
|
3604
|
+
if (recentEntries.length === 0 && archivedSessions.length === 0 && !historyGroup) {
|
|
3528
3605
|
return renderSessionManageBar() + '<div class="empty-state"><strong>还没有会话记录</strong><br>点击上方「新对话」开始你的第一次对话。</div>';
|
|
3529
3606
|
}
|
|
3607
|
+
if (historyGroup) {
|
|
3608
|
+
groups.push(historyGroup);
|
|
3609
|
+
}
|
|
3530
3610
|
return groups.join("");
|
|
3531
3611
|
}
|
|
3532
3612
|
|
|
@@ -3583,19 +3663,21 @@
|
|
|
3583
3663
|
var selectAllAction = allSelected ? "clear-selection" : "select-all-visible";
|
|
3584
3664
|
var selectAllDisabled = selectable === 0 ? ' disabled' : '';
|
|
3585
3665
|
|
|
3586
|
-
//
|
|
3587
|
-
//
|
|
3666
|
+
// Flat in-place toolbar (NOT a popped card): the same sub-header row
|
|
3667
|
+
// morphs to [✕] N 已选 ........ 全选/取消全选 删除. Sticky to the top
|
|
3668
|
+
// of the scroll so the count + delete stay reachable while selecting.
|
|
3669
|
+
var exitBtn = '<button class="session-manage-exit" data-action="toggle-manage-mode" type="button" aria-label="退出管理模式" title="退出管理模式">' +
|
|
3670
|
+
'<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>' +
|
|
3671
|
+
'</button>';
|
|
3672
|
+
var summary = hasAny
|
|
3673
|
+
? '<span class="session-manage-count">' + totalCount + '</span><span class="session-manage-summary-label">已选择</span>'
|
|
3674
|
+
: '<span class="session-manage-summary-label muted">选择要管理的项目</span>';
|
|
3588
3675
|
return '<div class="session-manage-bar active">' +
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
'<span class="session-manage-summary-label">已选择</span>' +
|
|
3592
|
-
'</div>' +
|
|
3676
|
+
exitBtn +
|
|
3677
|
+
'<div class="session-manage-summary">' + summary + '</div>' +
|
|
3593
3678
|
'<div class="session-manage-actions">' +
|
|
3594
3679
|
'<button class="btn btn-ghost btn-xs" data-action="' + selectAllAction + '" type="button"' + selectAllDisabled + '>' + selectAllLabel + '</button>' +
|
|
3595
|
-
'<button class="btn btn-
|
|
3596
|
-
'<span class="session-manage-divider"></span>' +
|
|
3597
|
-
'<button class="btn btn-danger btn-xs" data-action="delete-selected" type="button"' + (hasAny ? '' : ' disabled') + '>删除</button>' +
|
|
3598
|
-
'<button class="btn btn-primary btn-xs" data-action="toggle-manage-mode" type="button">完成</button>' +
|
|
3680
|
+
'<button class="btn btn-danger btn-xs" data-action="delete-selected" type="button"' + (hasAny ? '' : ' disabled') + '>删除' + (hasAny ? ' ' + totalCount : '') + '</button>' +
|
|
3599
3681
|
'</div>' +
|
|
3600
3682
|
'</div>';
|
|
3601
3683
|
}
|
|
@@ -3622,8 +3704,9 @@
|
|
|
3622
3704
|
}
|
|
3623
3705
|
|
|
3624
3706
|
function renderRecentGroup(entries) {
|
|
3625
|
-
|
|
3626
|
-
|
|
3707
|
+
// No "最近" group title here — the section intro ("最近的会话记录") above
|
|
3708
|
+
// already labels this group, so a second label would be redundant.
|
|
3709
|
+
var html = '<section class="session-group session-group--recent">';
|
|
3627
3710
|
html += entries.map(function(e) {
|
|
3628
3711
|
return e.kind === "session"
|
|
3629
3712
|
? renderSessionItem(e.ref, "sessions")
|
|
@@ -3642,46 +3725,30 @@
|
|
|
3642
3725
|
});
|
|
3643
3726
|
}
|
|
3644
3727
|
|
|
3645
|
-
// Render the
|
|
3646
|
-
//
|
|
3647
|
-
//
|
|
3648
|
-
//
|
|
3649
|
-
function
|
|
3728
|
+
// Render history as the final INLINE collapsible group of #sessions-list,
|
|
3729
|
+
// styled like the 已归档 group. Returns '' when history is fully loaded
|
|
3730
|
+
// and empty, so a workspace with no older CLI history shows no stray
|
|
3731
|
+
// "历史会话 0" row (and no stranded bar above the footer).
|
|
3732
|
+
function renderClaudeHistoryGroup() {
|
|
3650
3733
|
var visibleHistory = getClaudeHistoryRegionItems();
|
|
3651
|
-
var expanded = !!state.claudeHistoryExpanded;
|
|
3652
3734
|
var loaded = !!state.claudeHistoryLoaded;
|
|
3653
3735
|
var codexVisible = getVisibleCodexHistorySessions();
|
|
3654
3736
|
var codexLoaded = !!state.codexHistoryLoaded;
|
|
3737
|
+
var fullyLoaded = loaded && codexLoaded;
|
|
3655
3738
|
var count = (loaded ? visibleHistory.length : 0) + (codexLoaded ? codexVisible.length : 0);
|
|
3739
|
+
if (fullyLoaded && count === 0) return '';
|
|
3656
3740
|
|
|
3657
|
-
var
|
|
3658
|
-
var
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
badgeContent = "0";
|
|
3665
|
-
} else {
|
|
3666
|
-
badgeContent = count > 999 ? "999+" : String(count);
|
|
3667
|
-
}
|
|
3668
|
-
var badge = '<span class="' + badgeCls + '">' + badgeContent + '</span>';
|
|
3669
|
-
|
|
3670
|
-
// Chevron rotates: collapsed → up (▲, suggests "expand upward"),
|
|
3671
|
-
// expanded → down (▼, suggests "collapse downward").
|
|
3672
|
-
var chevronSvg = '<svg class="sidebar-history-chevron" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="6 15 12 9 18 15"/></svg>';
|
|
3673
|
-
|
|
3674
|
-
var headerCls = "sidebar-history-header" + (expanded ? " expanded" : "");
|
|
3675
|
-
var header = '<button type="button" class="' + headerCls + '" id="claude-history-toggle" aria-expanded="' + expanded + '" aria-controls="sidebar-history-body" title="' + (expanded ? "收起历史消息" : "展开历史消息") + '">' +
|
|
3676
|
-
'<span class="sidebar-history-label">历史消息</span>' +
|
|
3677
|
-
'<span class="sidebar-history-right">' + badge + chevronSvg + '</span>' +
|
|
3678
|
-
'</button>';
|
|
3679
|
-
|
|
3741
|
+
var expanded = !!state.claudeHistoryExpanded;
|
|
3742
|
+
var chevron = expanded ? "▾" : "▸";
|
|
3743
|
+
var countContent = fullyLoaded ? (count > 999 ? "999+" : String(count)) : "···";
|
|
3744
|
+
var header = '<div class="session-group-title claude-history-toggle session-history-toggle" id="claude-history-toggle" role="button" tabindex="0" aria-expanded="' + expanded + '" title="' + (expanded ? "收起历史会话" : "展开历史会话") + '">' +
|
|
3745
|
+
'<span class="chevron">' + chevron + '</span> 历史会话 ' +
|
|
3746
|
+
'<span class="history-count' + (fullyLoaded ? '' : ' loading') + '">' + countContent + '</span>' +
|
|
3747
|
+
'</div>';
|
|
3680
3748
|
var body = expanded
|
|
3681
|
-
? '<div class="
|
|
3749
|
+
? '<div class="session-history-body">' + renderClaudeHistoryBodyContent(visibleHistory) + renderCodexHistoryBodyContent(codexVisible) + '</div>'
|
|
3682
3750
|
: '';
|
|
3683
|
-
|
|
3684
|
-
return header + body;
|
|
3751
|
+
return '<section class="session-group session-group--history">' + header + body + '</section>';
|
|
3685
3752
|
}
|
|
3686
3753
|
|
|
3687
3754
|
function renderClaudeHistoryBodyContent(visibleHistory) {
|
|
@@ -3689,7 +3756,9 @@
|
|
|
3689
3756
|
return '<div class="claude-history-loading">扫描历史会话中…</div>';
|
|
3690
3757
|
}
|
|
3691
3758
|
if (visibleHistory.length === 0) {
|
|
3692
|
-
|
|
3759
|
+
// Group is only rendered when there is content somewhere (Claude or
|
|
3760
|
+
// Codex); a Claude-empty/Codex-present case shows just the Codex list.
|
|
3761
|
+
return '';
|
|
3693
3762
|
}
|
|
3694
3763
|
var groups = {};
|
|
3695
3764
|
var groupOrder = [];
|
|
@@ -3715,14 +3784,6 @@
|
|
|
3715
3784
|
return toolbar + '<div class="sidebar-history-scroll">' + listHtml + '</div>';
|
|
3716
3785
|
}
|
|
3717
3786
|
|
|
3718
|
-
// Re-render only the docked history region in place. Called by
|
|
3719
|
-
// updateSessionsList() so existing callers (load complete, delete, etc.)
|
|
3720
|
-
// keep working without changes.
|
|
3721
|
-
function updateClaudeHistoryRegion() {
|
|
3722
|
-
var region = document.getElementById("sidebar-history-region");
|
|
3723
|
-
if (region) region.innerHTML = renderClaudeHistoryRegion();
|
|
3724
|
-
}
|
|
3725
|
-
|
|
3726
3787
|
function getVisibleClaudeHistorySessions() {
|
|
3727
3788
|
var managedIds = new Set();
|
|
3728
3789
|
state.sessions.forEach(function(s) {
|
|
@@ -5536,9 +5597,10 @@
|
|
|
5536
5597
|
var resumeButton = "";
|
|
5537
5598
|
var checkbox = renderManageCheckbox("sessions", session.id, "选择会话 " + session.command);
|
|
5538
5599
|
|
|
5539
|
-
if (session.provider === "claude" && session.claudeSessionId) {
|
|
5600
|
+
if ((session.provider === "claude" || session.provider === "codex") && session.claudeSessionId) {
|
|
5540
5601
|
if (session.status !== "running" && !state.sessionsManageMode && !isStructuredSession(session)) {
|
|
5541
|
-
|
|
5602
|
+
var resumeTitle = session.provider === "codex" ? "恢复 Codex 会话" : "恢复 Claude 会话";
|
|
5603
|
+
resumeButton = '<button class="session-action-btn" data-action="resume" data-session-id="' + session.id + '" type="button" aria-label="' + resumeTitle + '" title="' + resumeTitle + '"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 4v6h6"/><path d="M3.51 15a9 9 0 105.64-11.36L3 10"/></svg></button>';
|
|
5542
5604
|
}
|
|
5543
5605
|
}
|
|
5544
5606
|
|
|
@@ -5557,7 +5619,7 @@
|
|
|
5557
5619
|
// Title: summary or command
|
|
5558
5620
|
var titleHtml = session.summary
|
|
5559
5621
|
? '<div class="session-title">' + escapeHtml(session.summary) + '</div>'
|
|
5560
|
-
: '<div class="session-command">' + escapeHtml(session.resumedFromSessionId ? session.command.replace(/\s+--resume\s+\S+/, '') : session.command) + '</div>';
|
|
5622
|
+
: '<div class="session-command">' + escapeHtml(session.resumedFromSessionId ? session.command.replace(/\s+--resume\s+\S+/, '').replace(/\s+resume\s+[0-9a-f-]+/, '') : session.command) + '</div>';
|
|
5561
5623
|
|
|
5562
5624
|
// Activity description for running sessions
|
|
5563
5625
|
var activityDesc = getSessionActivityDesc(session);
|
|
@@ -6121,15 +6183,9 @@
|
|
|
6121
6183
|
sessionsList.addEventListener("mouseout", handleCollapsedTileLeave);
|
|
6122
6184
|
initSwipeToDelete(sessionsList);
|
|
6123
6185
|
}
|
|
6124
|
-
//
|
|
6125
|
-
//
|
|
6126
|
-
// expand
|
|
6127
|
-
// same callbacks so behavior stays identical.
|
|
6128
|
-
var historyRegion = document.getElementById("sidebar-history-region");
|
|
6129
|
-
if (historyRegion) {
|
|
6130
|
-
historyRegion.addEventListener("click", handleSessionItemClick);
|
|
6131
|
-
historyRegion.addEventListener("keydown", handleSessionItemKeydown);
|
|
6132
|
-
}
|
|
6186
|
+
// History now renders inline as the final group inside #sessions-list,
|
|
6187
|
+
// so the delegated handlers above already cover its toggle / directory
|
|
6188
|
+
// expand-collapse / item clicks / clear-all — no separate region wiring.
|
|
6133
6189
|
window.addEventListener("scroll", hideCollapsedTileBubble, true);
|
|
6134
6190
|
window.addEventListener("resize", hideCollapsedTileBubble);
|
|
6135
6191
|
|
|
@@ -6192,6 +6248,8 @@
|
|
|
6192
6248
|
if (closeDrawerBtn) closeDrawerBtn.addEventListener("click", closeSessionsDrawer);
|
|
6193
6249
|
var collapseBtn = document.getElementById("sidebar-collapse-btn");
|
|
6194
6250
|
if (collapseBtn) collapseBtn.addEventListener("click", toggleSidebarCollapsed);
|
|
6251
|
+
var pinBtn = document.getElementById("sidebar-pin-btn");
|
|
6252
|
+
if (pinBtn) pinBtn.addEventListener("click", toggleSidebarPin);
|
|
6195
6253
|
var sidebarMoreBtn = document.getElementById("sidebar-more-btn");
|
|
6196
6254
|
var sidebarOverflow = document.getElementById("sidebar-overflow-menu");
|
|
6197
6255
|
if (sidebarMoreBtn && sidebarOverflow) {
|
|
@@ -6673,7 +6731,7 @@
|
|
|
6673
6731
|
topbarMoreBtn.setAttribute("aria-expanded", "false");
|
|
6674
6732
|
switch (action) {
|
|
6675
6733
|
case "copy-claude-session-id":
|
|
6676
|
-
copySelectedSessionField("claudeSessionId", "Claude 会话 ID 已复制");
|
|
6734
|
+
copySelectedSessionField("claudeSessionId", getSelectedSession() && getSelectedSession().provider === "codex" ? "Codex thread ID 已复制" : "Claude 会话 ID 已复制");
|
|
6677
6735
|
break;
|
|
6678
6736
|
case "copy-cwd":
|
|
6679
6737
|
copySelectedSessionField("cwd", "工作目录已复制");
|
|
@@ -8583,7 +8641,7 @@
|
|
|
8583
8641
|
state.sessions = [];
|
|
8584
8642
|
state.claudeHistory = [];
|
|
8585
8643
|
state.claudeHistoryLoaded = false;
|
|
8586
|
-
state.claudeHistoryExpanded =
|
|
8644
|
+
state.claudeHistoryExpanded = false;
|
|
8587
8645
|
state.claudeHistoryExpandedDirs = {};
|
|
8588
8646
|
state.sessionsDrawerOpen = false;
|
|
8589
8647
|
render();
|
|
@@ -9520,10 +9578,8 @@
|
|
|
9520
9578
|
var countEl = document.getElementById("session-count");
|
|
9521
9579
|
if (listEl) listEl.innerHTML = renderSessionsListContent();
|
|
9522
9580
|
if (countEl) countEl.textContent = String(state.sessions.length);
|
|
9523
|
-
//
|
|
9524
|
-
//
|
|
9525
|
-
// delete, clear) don't need to know about it.
|
|
9526
|
-
updateClaudeHistoryRegion();
|
|
9581
|
+
// History renders inline inside #sessions-list now, so the line above
|
|
9582
|
+
// already refreshed it — no separate docked region to update.
|
|
9527
9583
|
if (typeof hideCollapsedTileBubble === "function") hideCollapsedTileBubble();
|
|
9528
9584
|
updateShellChrome();
|
|
9529
9585
|
// Re-render cross-session queue (container may have been destroyed by DOM rebuild)
|
|
@@ -9737,7 +9793,7 @@
|
|
|
9737
9793
|
// 与 renderAppShell 保持一致:手机端只允许窄条形态 anchored。
|
|
9738
9794
|
var isMobile = isMobileLayout();
|
|
9739
9795
|
var isCollapsed = !!state.sidebarPinned && !!state.sidebarCollapsed;
|
|
9740
|
-
var isAnchored = isCollapsed || (!!state.sidebarPinned
|
|
9796
|
+
var isAnchored = isCollapsed || (!isMobile && (!!state.sidebarPinned || !!state.sessionsDrawerOpen));
|
|
9741
9797
|
if (drawer) {
|
|
9742
9798
|
drawer.classList.toggle("pinned", isAnchored);
|
|
9743
9799
|
drawer.classList.toggle("collapsed", isCollapsed);
|
|
@@ -9746,6 +9802,13 @@
|
|
|
9746
9802
|
mainLayout.classList.toggle("sidebar-pinned", isAnchored);
|
|
9747
9803
|
mainLayout.classList.toggle("sidebar-collapsed", isCollapsed);
|
|
9748
9804
|
}
|
|
9805
|
+
var pinBtn = document.getElementById("sidebar-pin-btn");
|
|
9806
|
+
if (pinBtn) {
|
|
9807
|
+
pinBtn.classList.toggle("pinned", !!state.sidebarPinned);
|
|
9808
|
+
pinBtn.title = state.sidebarPinned ? "已固定常驻(点击解除锁定)" : "固定侧栏常驻";
|
|
9809
|
+
pinBtn.setAttribute("aria-label", state.sidebarPinned ? "解除固定常驻" : "固定侧栏常驻");
|
|
9810
|
+
pinBtn.setAttribute("aria-pressed", state.sidebarPinned ? "true" : "false");
|
|
9811
|
+
}
|
|
9749
9812
|
}
|
|
9750
9813
|
|
|
9751
9814
|
function updateDrawerState() {
|
|
@@ -9950,6 +10013,26 @@
|
|
|
9950
10013
|
scheduleTerminalRefitAfterPaddingTransition();
|
|
9951
10014
|
}
|
|
9952
10015
|
|
|
10016
|
+
// 常驻(图钉)开关:把侧栏「留在原地」并排停靠 ⟷ 悬浮抽屉。
|
|
10017
|
+
// - 常驻 ON:停靠并推开内容;配合「收起为窄条」可在全尺寸/窄条间切换。
|
|
10018
|
+
// - 常驻 OFF:变为悬浮抽屉,交互时自动收起(非常驻)。
|
|
10019
|
+
// 手机端不支持常驻(屏幕太窄),保持抽屉行为。
|
|
10020
|
+
// 「图钉」只切换「是否锁定常驻」,绝不收起 / 隐藏侧栏——这是用户两次反馈的核心:
|
|
10021
|
+
// 点图钉不该让侧栏消失,也不该把主区压到侧栏下面。
|
|
10022
|
+
// - 锁定(pinned):常驻停靠,不会被「关闭(X)」一键收走的语义区分开。
|
|
10023
|
+
// - 解除锁定(unpinned):仍然停靠可见(因为 isAnchored 把 open 也算停靠),
|
|
10024
|
+
// 只是变成「可被 X 关闭」的临时态。
|
|
10025
|
+
// 全尺寸 / 窄条由「收起为窄条」按钮控制,与图钉正交。
|
|
10026
|
+
function toggleSidebarPin() {
|
|
10027
|
+
if (isMobileLayout()) return;
|
|
10028
|
+
state.sidebarPinned = !state.sidebarPinned;
|
|
10029
|
+
// 关键:保持侧栏可见停靠,无论锁定与否,点图钉都不让它消失。
|
|
10030
|
+
state.sessionsDrawerOpen = true;
|
|
10031
|
+
try { localStorage.setItem("wand-sidebar-pinned", String(state.sidebarPinned)); } catch (e) {}
|
|
10032
|
+
updateLayoutState();
|
|
10033
|
+
scheduleTerminalRefitAfterPaddingTransition();
|
|
10034
|
+
}
|
|
10035
|
+
|
|
9953
10036
|
// 收窄按钮的图标/title/状态随 collapsed 切换。抽出来给轻量更新路径用,
|
|
9954
10037
|
// 避免为了换一个箭头方向就走全量 render()。
|
|
9955
10038
|
function updateSidebarCollapseButton() {
|
|
@@ -12812,7 +12895,7 @@
|
|
|
12812
12895
|
|
|
12813
12896
|
return ensureSessionReadyForInput(selectedSession).then(function(readySession) {
|
|
12814
12897
|
if (!readySession) {
|
|
12815
|
-
// ensureSessionReadyForInput /
|
|
12898
|
+
// ensureSessionReadyForInput / resumeSession 已经在失败路径里
|
|
12816
12899
|
// 自行 toast,这里不再重复提示,避免叠两条消息。
|
|
12817
12900
|
return null;
|
|
12818
12901
|
}
|
|
@@ -13606,14 +13689,14 @@
|
|
|
13606
13689
|
var isCodex = selectedSession && selectedSession.provider === "codex";
|
|
13607
13690
|
if (error && (error.errorCode === "SESSION_NOT_RUNNING" || error.errorCode === "SESSION_NO_PTY")) {
|
|
13608
13691
|
return isCodex
|
|
13609
|
-
? "Codex
|
|
13692
|
+
? "Codex 会话已结束;若存在 Codex 历史会话,将在你下次发送消息时自动恢复。"
|
|
13610
13693
|
: "会话已结束;若存在 Claude 历史会话,将在你下次发送消息时自动恢复。";
|
|
13611
13694
|
}
|
|
13612
13695
|
if (error && error.errorCode === "SESSION_NOT_FOUND") {
|
|
13613
13696
|
return "会话不存在,请重新选择或新建会话。";
|
|
13614
13697
|
}
|
|
13615
13698
|
return (error && error.message) || (isCodex
|
|
13616
|
-
? "Codex
|
|
13699
|
+
? "Codex 会话暂不可用;若存在 Codex 历史会话,将自动尝试恢复。"
|
|
13617
13700
|
: "会话暂不可用;若存在 Claude 历史会话,将自动尝试恢复。");
|
|
13618
13701
|
}
|
|
13619
13702
|
|
|
@@ -13654,10 +13737,10 @@
|
|
|
13654
13737
|
}
|
|
13655
13738
|
|
|
13656
13739
|
function canAutoResumeSession(session) {
|
|
13657
|
-
// 只要是 Claude provider + 非运行中 +
|
|
13740
|
+
// 只要是 Claude/Codex PTY provider + 非运行中 + 有可恢复历史 id,
|
|
13658
13741
|
// 就允许在用户发送时静默触发恢复。不再要求 messages 里同时
|
|
13659
13742
|
// 有 user + assistant 文本(slim 列表/截断历史会让该判断失真)。
|
|
13660
|
-
return !!(session && session.provider === "claude" && session.status !== "running" && session.claudeSessionId);
|
|
13743
|
+
return !!(session && !isStructuredSession(session) && (session.provider === "claude" || session.provider === "codex") && session.status !== "running" && session.claudeSessionId);
|
|
13661
13744
|
}
|
|
13662
13745
|
|
|
13663
13746
|
function ensureSessionReadyForInput(session, errorEl) {
|
|
@@ -13670,12 +13753,13 @@
|
|
|
13670
13753
|
return Promise.resolve(session);
|
|
13671
13754
|
}
|
|
13672
13755
|
if (!canAutoResumeSession(session)) {
|
|
13673
|
-
|
|
13756
|
+
var providerLabel = session && session.provider === "codex" ? "Codex" : "Claude";
|
|
13757
|
+
showToast("该会话没有可恢复的 " + providerLabel + " 历史上下文,请新建会话。", "error");
|
|
13674
13758
|
return Promise.resolve(null);
|
|
13675
13759
|
}
|
|
13676
13760
|
|
|
13677
13761
|
// 静默恢复:不再弹 "正在恢复历史会话…" 提示,让用户发送动作看起来无缝。
|
|
13678
|
-
return
|
|
13762
|
+
return resumeSession(session.id, errorEl).then(function(data) {
|
|
13679
13763
|
if (!data) return null;
|
|
13680
13764
|
updateSessionSnapshot(data);
|
|
13681
13765
|
updateSessionsList();
|
|
@@ -14114,7 +14198,7 @@
|
|
|
14114
14198
|
: "Enter 发送 · Shift+Enter 换行";
|
|
14115
14199
|
}
|
|
14116
14200
|
}
|
|
14117
|
-
// 历史会话只要可自动恢复(Claude
|
|
14201
|
+
// 历史会话只要可自动恢复(Claude/Codex PTY + 有历史 id),输入框/发送按钮
|
|
14118
14202
|
// 就保持可用——发送时由 ensureSessionReadyForInput 透明完成恢复。
|
|
14119
14203
|
var canResumeOnSend = !structured && !isRunning && canAutoResumeSession(selectedSession);
|
|
14120
14204
|
if (composer) {
|
|
@@ -14649,9 +14733,15 @@
|
|
|
14649
14733
|
return resumeSession(sessionId).then(function(data) {
|
|
14650
14734
|
if (!data) return null;
|
|
14651
14735
|
if (data.claudeSessionId) {
|
|
14652
|
-
|
|
14653
|
-
|
|
14654
|
-
|
|
14736
|
+
if (data.provider === "codex") {
|
|
14737
|
+
state.codexHistory = state.codexHistory.filter(function(s) {
|
|
14738
|
+
return s.claudeSessionId !== data.claudeSessionId;
|
|
14739
|
+
});
|
|
14740
|
+
} else {
|
|
14741
|
+
state.claudeHistory = state.claudeHistory.filter(function(s) {
|
|
14742
|
+
return s.claudeSessionId !== data.claudeSessionId;
|
|
14743
|
+
});
|
|
14744
|
+
}
|
|
14655
14745
|
}
|
|
14656
14746
|
return activateSession(data).then(function() {
|
|
14657
14747
|
return data;
|