feat: add MCP session visibility toggle and rebuild release

This commit is contained in:
shiyue
2026-08-26 17:12:45 +08:00
parent f15bc92d1e
commit e62a545569
13 changed files with 564 additions and 7 deletions

View File

@@ -12,6 +12,7 @@
const CCWEB_PROMPT_VIEW_MODE_STORAGE_KEY = 'cc-web-ccweb-prompt-view-mode';
const PROJECT_COLLAPSE_STORAGE_KEY = 'cc-web-collapsed-projects';
const SIDEBAR_COLLAPSE_STORAGE_KEY = 'cc-web-sidebar-collapsed';
const MCP_SESSION_VISIBILITY_STORAGE_KEY = 'cc-web-mcp-session-visibility';
const SIDEBAR_DRAWER_MEDIA_QUERY = '(max-width: 768px)';
const SIDEBAR_DESKTOP_COLLAPSE_MEDIA_QUERY = '(width > 768px)';
const SIDEBAR_HOVER_PREVIEW_MEDIA_QUERY = '(width > 768px) and (hover: hover) and (pointer: fine)';
@@ -272,6 +273,7 @@
let isReloadingMcp = false;
const mcpStartupToastKeys = new Map();
let sessionSearchQuery = '';
let showMcpSessions = readMcpSessionVisibilityPreference();
const advancedSessionSearchState = {
open: false,
query: '',
@@ -365,6 +367,7 @@
const importSessionBtn = $('#import-session-btn');
const sessionSearchInput = $('#session-search-input');
const sessionSearchClear = $('#session-search-clear');
const mcpSessionVisibilityToggle = $('#mcp-session-visibility-toggle');
const advancedSearchOpen = $('#advanced-search-open');
const advancedSearchPanel = $('#advanced-search-panel');
const advancedSearchClose = $('#advanced-search-close');
@@ -4521,6 +4524,39 @@
}
}
function readMcpSessionVisibilityPreference() {
try {
return localStorage.getItem(MCP_SESSION_VISIBILITY_STORAGE_KEY) !== 'false';
} catch {
return true;
}
}
function persistMcpSessionVisibilityPreference(visible) {
try {
localStorage.setItem(MCP_SESSION_VISIBILITY_STORAGE_KEY, visible ? 'true' : 'false');
} catch {
// 存储不可用时仍保留当前页面内的筛选状态。
}
}
function sessionPassesMcpVisibilityFilter(session, normalizedSearchQuery = '') {
if (normalizedSearchQuery) return true;
if (showMcpSessions) return true;
const isMcpCreated = String(session?.createdFromKind || '').toLowerCase() === 'mcp';
if (!isMcpCreated) return true;
return session.id === currentSessionId || !!session.isRunning;
}
function syncMcpSessionVisibilityToggle() {
if (!mcpSessionVisibilityToggle) return;
const isHidingMcpSessions = !showMcpSessions;
const actionLabel = isHidingMcpSessions ? '显示 MCP 会话' : '隐藏 MCP 会话';
mcpSessionVisibilityToggle.setAttribute('aria-pressed', String(isHidingMcpSessions));
mcpSessionVisibilityToggle.title = actionLabel;
mcpSessionVisibilityToggle.setAttribute('aria-label', actionLabel);
}
function getSessionSearchText(session) {
const cwd = getSessionEffectiveCwd(session);
return [
@@ -10856,6 +10892,7 @@
return JSON.stringify({
agent: currentAgent,
currentSessionId,
showMcpSessions,
search: model.normalizedSearchQuery,
allEmpty: model.allVisibleSessions.length === 0,
visibleEmpty: model.visibleSessions.length === 0,
@@ -10890,12 +10927,16 @@
function renderSessionList() {
syncSessionSearchUi();
syncMcpSessionVisibilityToggle();
const allVisibleSessions = getVisibleSessions();
const normalizedSearchQuery = normalizeSessionSearchQuery(sessionSearchQuery);
const isSearchingSessions = !!normalizedSearchQuery;
const mcpVisibilityFilteredSessions = allVisibleSessions.filter((session) => (
sessionPassesMcpVisibilityFilter(session, normalizedSearchQuery)
));
const visibleSessions = isSearchingSessions
? allVisibleSessions.filter((session) => sessionMatchesSearch(session, normalizedSearchQuery))
: allVisibleSessions;
? mcpVisibilityFilteredSessions.filter((session) => sessionMatchesSearch(session, normalizedSearchQuery))
: mcpVisibilityFilteredSessions;
const { pinnedSessions, regularSessions } = splitPinnedSessions(visibleSessions);
const { groups: projectGroups, ungroupedSessions } = groupSessionsByProject(regularSessions);
const renderGroups = projectGroups.map((group, groupIndex) => {
@@ -10951,7 +10992,7 @@
if (visibleSessions.length === 0) {
const empty = document.createElement('div');
empty.className = 'session-list-empty';
empty.textContent = '没有匹配的会话或项目。';
empty.textContent = isSearchingSessions ? '没有匹配的会话或项目。' : 'MCP 会话已隐藏。';
sessionList.appendChild(empty);
lastSessionListStructureSignature = structureSignature;
return;
@@ -11821,6 +11862,14 @@
});
}
if (mcpSessionVisibilityToggle) {
mcpSessionVisibilityToggle.addEventListener('click', () => {
showMcpSessions = !showMcpSessions;
persistMcpSessionVisibilityPreference(showMcpSessions);
renderSessionList();
});
}
if (advancedSearchOpen && advancedSearchPanel) {
advancedSearchOpen.addEventListener('click', openAdvancedSessionSearch);
advancedSearchClose?.addEventListener('click', () => closeAdvancedSessionSearch());

View File

@@ -63,6 +63,13 @@
<input id="session-search-input" class="session-search-input" type="search" placeholder="检索会话 / 项目" autocomplete="off" aria-label="检索会话或项目">
<button id="session-search-clear" class="session-search-clear" type="button" title="清空检索" aria-label="清空检索" hidden>×</button>
</div>
<button id="mcp-session-visibility-toggle" class="mcp-session-visibility-toggle icon-only" type="button" title="隐藏 MCP 会话" aria-label="隐藏 MCP 会话" aria-pressed="false">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M2.5 12s3.5-6 9.5-6 9.5 6 9.5 6-3.5 6-9.5 6-9.5-6-9.5-6Z"></path>
<circle cx="12" cy="12" r="2.5"></circle>
<path class="mcp-session-visibility-slash" d="m4 4 16 16"></path>
</svg>
</button>
<button id="advanced-search-open" class="advanced-search-open" type="button" title="高级会话检索" aria-label="打开高级会话检索" aria-controls="advanced-search-panel" aria-expanded="false"></button>
</div>
</div>

View File

@@ -1252,11 +1252,13 @@ body.session-loading-active {
color: var(--text-primary);
outline: none;
}
.advanced-search-open {
.advanced-search-open,
.mcp-session-visibility-toggle {
appearance: none;
width: 34px;
height: 34px;
flex: 0 0 34px;
flex-basis: 34px;
display: inline-flex;
align-items: center;
justify-content: center;
@@ -1277,6 +1279,29 @@ body.session-loading-active {
transform 0.16s ease,
box-shadow 0.16s ease;
}
.mcp-session-visibility-toggle svg {
width: 17px;
height: 17px;
fill: none;
stroke: currentColor;
stroke-width: 1.8;
stroke-linecap: round;
stroke-linejoin: round;
}
.mcp-session-visibility-slash {
opacity: 0;
transition: opacity 0.16s ease;
}
.mcp-session-visibility-toggle[aria-pressed='true'] {
background: var(--accent-light);
border-color: rgba(192, 85, 58, 0.34);
color: var(--accent);
}
.mcp-session-visibility-toggle[aria-pressed='true'] .mcp-session-visibility-slash {
opacity: 1;
}
.mcp-session-visibility-toggle:hover,
.mcp-session-visibility-toggle:focus-visible,
.advanced-search-open:hover,
.advanced-search-open:focus-visible {
background: var(--accent-light);
@@ -1285,6 +1310,7 @@ body.session-loading-active {
outline: none;
box-shadow: 0 0 0 3px rgba(192, 85, 58, 0.1);
}
.mcp-session-visibility-toggle:active,
.advanced-search-open:active {
transform: translateY(1px);
}
@@ -2137,7 +2163,7 @@ body.session-loading-active {
html[data-theme='cinder'],
html[data-theme='gilded'],
html[data-theme='wasteland']
) .advanced-search-open {
) :is(.advanced-search-open, .mcp-session-visibility-toggle) {
background: var(--dark-panel-soft);
border-color: var(--theme-card-border);
color: var(--text-secondary);
@@ -2149,14 +2175,14 @@ body.session-loading-active {
html[data-theme='cinder'],
html[data-theme='gilded'],
html[data-theme='wasteland']
) .advanced-search-open:hover,
) :is(.advanced-search-open, .mcp-session-visibility-toggle):hover,
:is(
html[data-theme='carbon'],
html[data-theme='nocturne'],
html[data-theme='cinder'],
html[data-theme='gilded'],
html[data-theme='wasteland']
) .advanced-search-open:focus-visible {
) :is(.advanced-search-open, .mcp-session-visibility-toggle):focus-visible {
background: var(--accent-light);
border-color: var(--theme-card-hover-border);
color: var(--accent);
@@ -2192,6 +2218,15 @@ html[data-theme='wasteland'] .advanced-search-open {
clip-path: var(--wasteland-cut);
}
html[data-theme='wasteland'] .mcp-session-visibility-toggle {
width: 36px;
height: 36px;
flex-basis: 36px;
border-color: var(--wasteland-line);
border-radius: 0;
clip-path: var(--wasteland-cut);
}
html[data-theme='wasteland'] .advanced-search-panel {
--advanced-search-bg: #151715;
--advanced-search-line: var(--wasteland-line);
@@ -2309,6 +2344,7 @@ html[data-theme='wasteland'] .advanced-search-panel {
@media (prefers-reduced-motion: reduce) {
.advanced-search-open,
.mcp-session-visibility-toggle,
.advanced-search-close,
.advanced-search-submit,
.advanced-search-pill {
@@ -9502,6 +9538,12 @@ html[data-theme='wasteland'] .session-search-row .advanced-search-open {
flex-basis: 36px;
}
html[data-theme='wasteland'] .session-search-row .mcp-session-visibility-toggle {
width: 36px;
height: 36px;
flex-basis: 36px;
}
html[data-theme='wasteland'] .session-search::after {
inset: 0;
background-image: url('assets/themes/wasteland/frames/search.png');