fix session switch race on message send
This commit is contained in:
185
public/app.js
185
public/app.js
@@ -67,7 +67,7 @@
|
||||
const SESSION_CACHE_MAX_WEIGHT = 1_500_000;
|
||||
const SIDEBAR_SWIPE_TRIGGER = 72;
|
||||
const SIDEBAR_SWIPE_MAX_VERTICAL_DRIFT = 42;
|
||||
const OLD_SESSION_COLLAPSE_VISIBLE_LIMIT = 5;
|
||||
const OLD_SESSION_GROUP_INITIAL_VISIBLE = 3;
|
||||
const OLD_SESSION_COLLAPSE_DAYS = 7;
|
||||
const OLD_SESSION_COLLAPSE_MS = OLD_SESSION_COLLAPSE_DAYS * 24 * 60 * 60 * 1000;
|
||||
|
||||
@@ -194,6 +194,7 @@
|
||||
let codexAppApprovalModal = null;
|
||||
let pendingNewSessionRequest = null;
|
||||
let pendingSessionSwitchRequest = null;
|
||||
let sessionSwitchRequestSeq = 0;
|
||||
let skipDeleteConfirm = localStorage.getItem('cc-web-skip-delete-confirm') === '1';
|
||||
let pendingInitialSessionLoad = false;
|
||||
let initialSessionListHandled = false;
|
||||
@@ -1630,6 +1631,44 @@
|
||||
}
|
||||
}
|
||||
|
||||
function mergeSessionListSnapshot(snapshot) {
|
||||
if (!snapshot?.sessionId) return;
|
||||
const nextMeta = {
|
||||
id: snapshot.sessionId,
|
||||
sessionId: snapshot.sessionId,
|
||||
cwd: snapshot.cwd || '',
|
||||
projectName: snapshot.cwd ? getPathLeaf(snapshot.cwd) : snapshot.projectName || '',
|
||||
title: snapshot.title || '新会话',
|
||||
agent: normalizeAgent(snapshot.agent),
|
||||
updated: snapshot.updated || new Date().toISOString(),
|
||||
pinnedAt: snapshot.pinnedAt || null,
|
||||
hasUnread: !!snapshot.hasUnread,
|
||||
isRunning: !!snapshot.isRunning,
|
||||
waitingOnChildren: !!snapshot.waitingOnChildren,
|
||||
pendingReplyCount: Number(snapshot.pendingReplyCount || 0),
|
||||
readyReplyCount: Number(snapshot.readyReplyCount || 0),
|
||||
waitingReplyCount: Number(snapshot.waitingReplyCount || 0),
|
||||
failedReplyCount: Number(snapshot.failedReplyCount || 0),
|
||||
oversized: !!snapshot.oversized,
|
||||
fileBytes: Number(snapshot.fileBytes || 0),
|
||||
};
|
||||
let found = false;
|
||||
sessions = sessions.map((session) => {
|
||||
if (session.id !== snapshot.sessionId) return session;
|
||||
found = true;
|
||||
return {
|
||||
...session,
|
||||
...nextMeta,
|
||||
cwd: nextMeta.cwd || session.cwd || '',
|
||||
projectName: nextMeta.cwd ? getPathLeaf(nextMeta.cwd) : session.projectName || nextMeta.projectName || '',
|
||||
title: nextMeta.title || session.title,
|
||||
};
|
||||
});
|
||||
if (!found) {
|
||||
sessions = [nextMeta, ...sessions].sort(compareSessionUpdatedDesc);
|
||||
}
|
||||
}
|
||||
|
||||
function getSessionCacheDisposition(sessionId) {
|
||||
const entry = sessionCache.get(sessionId);
|
||||
const meta = getSessionMeta(sessionId);
|
||||
@@ -3009,30 +3048,36 @@
|
||||
return `${normalizeAgent(currentAgent)}:ungrouped`;
|
||||
}
|
||||
|
||||
function splitCollapsedOldSessions(regularSessions, pinnedCount, getCollapseKey = () => '') {
|
||||
const totalCount = pinnedCount + regularSessions.length;
|
||||
if (totalCount <= OLD_SESSION_COLLAPSE_VISIBLE_LIMIT) {
|
||||
return { visibleRegularSessions: regularSessions, hiddenOldSessions: [] };
|
||||
}
|
||||
function expandOldSessionGroup(collapseKey) {
|
||||
if (!collapseKey) return;
|
||||
expandedOldSessionGroups.add(collapseKey);
|
||||
}
|
||||
|
||||
function shouldAlwaysShowOldSession(session) {
|
||||
return session.id === currentSessionId || session.isRunning || session.hasUnread || session.waitingOnChildren;
|
||||
}
|
||||
|
||||
function splitCollapsedOldSessions(sessionItems, collapseKey) {
|
||||
const isExpanded = collapseKey && expandedOldSessionGroups.has(collapseKey);
|
||||
const nowMs = Date.now();
|
||||
const visibleRegularLimit = Math.max(0, OLD_SESSION_COLLAPSE_VISIBLE_LIMIT - pinnedCount);
|
||||
const visibleRegularSessions = [];
|
||||
const visibleSessions = [];
|
||||
const hiddenOldSessions = [];
|
||||
|
||||
regularSessions.forEach((session, index) => {
|
||||
const collapseKey = getCollapseKey(session);
|
||||
const isExpanded = collapseKey && expandedOldSessionGroups.has(collapseKey);
|
||||
const shouldKeepVisible = session.id === currentSessionId || session.isRunning || session.hasUnread || session.waitingOnChildren;
|
||||
const canCollapse = !isExpanded && index >= visibleRegularLimit && isOlderThanOldSessionWindow(session, nowMs);
|
||||
if (canCollapse && !shouldKeepVisible) {
|
||||
sessionItems.forEach((session) => {
|
||||
const shouldHideOldSession = (
|
||||
!isExpanded
|
||||
&& !shouldAlwaysShowOldSession(session)
|
||||
&& isOlderThanOldSessionWindow(session, nowMs)
|
||||
&& visibleSessions.length >= OLD_SESSION_GROUP_INITIAL_VISIBLE
|
||||
);
|
||||
if (shouldHideOldSession) {
|
||||
hiddenOldSessions.push(session);
|
||||
} else {
|
||||
visibleRegularSessions.push(session);
|
||||
visibleSessions.push(session);
|
||||
}
|
||||
});
|
||||
|
||||
return { visibleRegularSessions, hiddenOldSessions };
|
||||
return { visibleSessions, hiddenOldSessions };
|
||||
}
|
||||
|
||||
function createOldSessionLoadMoreButton(hiddenCount, collapseKey, projectName = '') {
|
||||
@@ -3042,10 +3087,10 @@
|
||||
button.setAttribute('aria-label', `加载更多${projectName ? ` ${projectName}` : ''} ${hiddenCount} 条 7 天前会话`);
|
||||
button.innerHTML = `
|
||||
<span class="session-list-load-more-title">加载更多</span>
|
||||
<span class="session-list-load-more-meta">${hiddenCount} 条 7 天前会话</span>
|
||||
<span class="session-list-load-more-meta">还有 ${hiddenCount} 条</span>
|
||||
`;
|
||||
button.addEventListener('click', () => {
|
||||
if (collapseKey) expandedOldSessionGroups.add(collapseKey);
|
||||
expandOldSessionGroup(collapseKey);
|
||||
renderSessionList();
|
||||
});
|
||||
return button;
|
||||
@@ -3393,7 +3438,8 @@
|
||||
function setSessionLoading(sessionId, options = {}) {
|
||||
const loading = !!sessionId;
|
||||
const blocking = options.blocking !== false;
|
||||
activeSessionLoad = loading ? { sessionId, blocking, snapshot: null } : null;
|
||||
const requestId = loading ? (options.requestId || createSessionSwitchRequestId(sessionId)) : '';
|
||||
activeSessionLoad = loading ? { sessionId, blocking, snapshot: null, requestId } : null;
|
||||
const showOverlay = !!(loading && blocking);
|
||||
document.body.classList.toggle('session-loading-active', showOverlay);
|
||||
sessionLoadingOverlay.hidden = !showOverlay;
|
||||
@@ -3413,6 +3459,11 @@
|
||||
setSessionLoading(null, { blocking: false });
|
||||
}
|
||||
|
||||
function createSessionSwitchRequestId(sessionId) {
|
||||
sessionSwitchRequestSeq += 1;
|
||||
return `session-load-${Date.now()}-${sessionSwitchRequestSeq}-${String(sessionId || '').slice(0, 8)}`;
|
||||
}
|
||||
|
||||
function isBlockingSessionLoad(sessionId) {
|
||||
return !!(activeSessionLoad &&
|
||||
activeSessionLoad.blocking &&
|
||||
@@ -3454,6 +3505,7 @@
|
||||
sessionId,
|
||||
blocking: options.blocking !== false,
|
||||
label: options.label || '',
|
||||
requestId: options.requestId || activeSessionLoad?.requestId || createSessionSwitchRequestId(sessionId),
|
||||
};
|
||||
if (ws && ws.readyState === 1 && wsAuthenticated) {
|
||||
flushPendingSessionSwitch();
|
||||
@@ -3471,9 +3523,10 @@
|
||||
setSessionLoading(request.sessionId, {
|
||||
blocking: request.blocking,
|
||||
label: request.label || undefined,
|
||||
requestId: request.requestId,
|
||||
});
|
||||
}
|
||||
ws.send(JSON.stringify({ type: 'load_session', sessionId: request.sessionId }));
|
||||
ws.send(JSON.stringify({ type: 'load_session', sessionId: request.sessionId, requestId: request.requestId }));
|
||||
}
|
||||
|
||||
function showCachedSession(sessionId) {
|
||||
@@ -3971,6 +4024,7 @@
|
||||
sessionId: activeSessionLoad.sessionId,
|
||||
blocking: activeSessionLoad.blocking,
|
||||
label: sessionLoadingLabel?.textContent || '',
|
||||
requestId: activeSessionLoad.requestId || createSessionSwitchRequestId(activeSessionLoad.sessionId),
|
||||
};
|
||||
}
|
||||
clearSessionLoading();
|
||||
@@ -4068,28 +4122,29 @@
|
||||
break;
|
||||
|
||||
case 'session_info':
|
||||
if (pendingNewSessionRequest) pendingNewSessionRequest = null;
|
||||
const snapshot = normalizeSessionSnapshot(msg);
|
||||
sessions = sessions.map((session) => (
|
||||
session.id === snapshot.sessionId
|
||||
? {
|
||||
...session,
|
||||
cwd: snapshot.cwd || session.cwd || '',
|
||||
projectName: snapshot.cwd ? getPathLeaf(snapshot.cwd) : session.projectName || '',
|
||||
title: snapshot.title || session.title,
|
||||
pinnedAt: snapshot.pinnedAt || session.pinnedAt || null,
|
||||
isRunning: snapshot.isRunning,
|
||||
waitingOnChildren: snapshot.waitingOnChildren,
|
||||
pendingReplyCount: snapshot.pendingReplyCount,
|
||||
readyReplyCount: snapshot.readyReplyCount,
|
||||
waitingReplyCount: snapshot.waitingReplyCount,
|
||||
failedReplyCount: snapshot.failedReplyCount,
|
||||
}
|
||||
: session
|
||||
));
|
||||
if (activeSessionLoad?.sessionId === msg.sessionId) {
|
||||
const activeLoad = activeSessionLoad;
|
||||
const pendingNewSession = pendingNewSessionRequest;
|
||||
const messageRequestId = String(msg.requestId || '');
|
||||
const matchesActiveLoad = !!(activeLoad?.sessionId === msg.sessionId
|
||||
&& (!activeLoad.requestId || activeLoad.requestId === messageRequestId));
|
||||
const matchesPendingNewSession = !!(pendingNewSession
|
||||
&& (!pendingNewSession.requestId || pendingNewSession.requestId === messageRequestId));
|
||||
const canSwitchToSessionInfo = matchesActiveLoad
|
||||
|| matchesPendingNewSession
|
||||
|| msg.sessionId === currentSessionId
|
||||
|| (!currentSessionId && !activeLoad && !pendingNewSession)
|
||||
|| (!messageRequestId && !activeLoad && !pendingNewSession);
|
||||
mergeSessionListSnapshot(snapshot);
|
||||
if (matchesActiveLoad) {
|
||||
activeSessionLoad.snapshot = snapshot;
|
||||
}
|
||||
if (!canSwitchToSessionInfo) {
|
||||
if (!msg.historyPending) cacheSessionSnapshot(snapshot);
|
||||
renderSessionList();
|
||||
break;
|
||||
}
|
||||
if (matchesPendingNewSession) pendingNewSessionRequest = null;
|
||||
applySessionSnapshot(snapshot, {
|
||||
immediate: isBlockingSessionLoad(msg.sessionId),
|
||||
suppressUnreadToast: false,
|
||||
@@ -4099,7 +4154,7 @@
|
||||
setCurrentSessionRunningState(!!msg.isRunning);
|
||||
}
|
||||
if (!msg.historyPending) {
|
||||
if (activeSessionLoad?.sessionId === msg.sessionId) {
|
||||
if (matchesActiveLoad) {
|
||||
finalizeLoadedSession(msg.sessionId);
|
||||
} else {
|
||||
cacheSessionSnapshot(snapshot);
|
||||
@@ -4380,6 +4435,7 @@
|
||||
agent: request.agent,
|
||||
mode: request.mode,
|
||||
createCwd: true,
|
||||
requestId: request.requestId,
|
||||
});
|
||||
},
|
||||
onCancel: () => {
|
||||
@@ -6515,33 +6571,6 @@
|
||||
|
||||
const { pinnedSessions, regularSessions } = splitPinnedSessions(visibleSessions);
|
||||
const { groups: projectGroups, ungroupedSessions } = groupSessionsByProject(regularSessions);
|
||||
const oldSessionCollapseKeysBySessionId = new Map();
|
||||
projectGroups.forEach((group) => {
|
||||
const oldSessionCollapseKey = getProjectOldSessionCollapseKey(group);
|
||||
group.sessions.forEach((session) => {
|
||||
oldSessionCollapseKeysBySessionId.set(session.id, oldSessionCollapseKey);
|
||||
});
|
||||
});
|
||||
ungroupedSessions.forEach((session) => {
|
||||
oldSessionCollapseKeysBySessionId.set(session.id, getUngroupedOldSessionCollapseKey());
|
||||
});
|
||||
const { visibleRegularSessions, hiddenOldSessions } = isSearchingSessions
|
||||
? { visibleRegularSessions: regularSessions, hiddenOldSessions: [] }
|
||||
: splitCollapsedOldSessions(
|
||||
regularSessions,
|
||||
pinnedSessions.length,
|
||||
(session) => oldSessionCollapseKeysBySessionId.get(session.id) || ''
|
||||
);
|
||||
const hiddenOldSessionCountsByKey = new Map();
|
||||
hiddenOldSessions.forEach((session) => {
|
||||
const oldSessionCollapseKey = oldSessionCollapseKeysBySessionId.get(session.id);
|
||||
if (!oldSessionCollapseKey) return;
|
||||
hiddenOldSessionCountsByKey.set(
|
||||
oldSessionCollapseKey,
|
||||
(hiddenOldSessionCountsByKey.get(oldSessionCollapseKey) || 0) + 1
|
||||
);
|
||||
});
|
||||
const visibleRegularSessionIds = new Set(visibleRegularSessions.map((session) => session.id));
|
||||
if (pinnedSessions.length > 0) {
|
||||
const pinnedGroupEl = document.createElement('section');
|
||||
pinnedGroupEl.className = 'session-project-group session-pinned-group';
|
||||
@@ -6563,8 +6592,9 @@
|
||||
projectGroups.forEach((group, groupIndex) => {
|
||||
const groupKey = getProjectCollapseKey(group);
|
||||
const oldSessionCollapseKey = getProjectOldSessionCollapseKey(group);
|
||||
const visibleGroupSessions = group.sessions.filter((session) => visibleRegularSessionIds.has(session.id));
|
||||
const hiddenGroupOldSessionCount = hiddenOldSessionCountsByKey.get(oldSessionCollapseKey) || 0;
|
||||
const { visibleSessions: visibleGroupSessions, hiddenOldSessions: hiddenGroupOldSessions } = isSearchingSessions
|
||||
? { visibleSessions: group.sessions, hiddenOldSessions: [] }
|
||||
: splitCollapsedOldSessions(group.sessions, oldSessionCollapseKey);
|
||||
const isCollapsed = !isSearchingSessions && collapsedProjectKeys.has(groupKey);
|
||||
const hasActiveSession = group.sessions.some((session) => session.id === currentSessionId);
|
||||
const hasUnreadSession = group.sessions.some((session) => session.hasUnread);
|
||||
@@ -6596,8 +6626,8 @@
|
||||
for (const s of visibleGroupSessions) {
|
||||
groupBody.appendChild(createSessionListItem(s));
|
||||
}
|
||||
if (hiddenGroupOldSessionCount > 0) {
|
||||
groupBody.appendChild(createOldSessionLoadMoreButton(hiddenGroupOldSessionCount, oldSessionCollapseKey, group.name));
|
||||
if (hiddenGroupOldSessions.length > 0) {
|
||||
groupBody.appendChild(createOldSessionLoadMoreButton(hiddenGroupOldSessions.length, oldSessionCollapseKey, group.name));
|
||||
}
|
||||
groupEl.appendChild(groupBody);
|
||||
|
||||
@@ -6614,15 +6644,16 @@
|
||||
});
|
||||
|
||||
const ungroupedCollapseKey = getUngroupedOldSessionCollapseKey();
|
||||
const visibleUngroupedSessions = ungroupedSessions.filter((session) => visibleRegularSessionIds.has(session.id));
|
||||
const hiddenUngroupedOldSessionCount = hiddenOldSessionCountsByKey.get(ungroupedCollapseKey) || 0;
|
||||
const { visibleSessions: visibleUngroupedSessions, hiddenOldSessions: hiddenUngroupedOldSessions } = isSearchingSessions
|
||||
? { visibleSessions: ungroupedSessions, hiddenOldSessions: [] }
|
||||
: splitCollapsedOldSessions(ungroupedSessions, ungroupedCollapseKey);
|
||||
|
||||
for (const s of visibleUngroupedSessions) {
|
||||
sessionList.appendChild(createSessionListItem(s));
|
||||
}
|
||||
|
||||
if (hiddenUngroupedOldSessionCount > 0) {
|
||||
sessionList.appendChild(createOldSessionLoadMoreButton(hiddenUngroupedOldSessionCount, ungroupedCollapseKey));
|
||||
if (hiddenUngroupedOldSessions.length > 0) {
|
||||
sessionList.appendChild(createOldSessionLoadMoreButton(hiddenUngroupedOldSessions.length, ungroupedCollapseKey));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8583,14 +8614,16 @@
|
||||
const rawCwd = options.rawCwd !== undefined ? options.rawCwd : (cwd || '');
|
||||
const agent = normalizeAgent(options.agent || currentAgent);
|
||||
const mode = ['default', 'plan', 'yolo'].includes(options.mode) ? options.mode : currentMode;
|
||||
const requestId = createSessionSwitchRequestId('new');
|
||||
pendingNewSessionRequest = {
|
||||
cwd,
|
||||
rawCwd,
|
||||
agent,
|
||||
mode,
|
||||
requestId,
|
||||
};
|
||||
if (cwd) saveRecentCwd(cwd);
|
||||
send({ type: 'new_session', cwd, agent, mode });
|
||||
send({ type: 'new_session', cwd, agent, mode, requestId });
|
||||
}
|
||||
|
||||
// --- New Session Modal ---
|
||||
|
||||
@@ -200,8 +200,8 @@ html[data-theme='coolvibe'] .session-search-clear:focus-visible {
|
||||
|
||||
html[data-theme='coolvibe'] .session-project-header {
|
||||
background: linear-gradient(180deg, rgba(247, 251, 252, 0.94), rgba(239, 248, 250, 0.88));
|
||||
color: #5f7f87;
|
||||
border-bottom: 1px solid rgba(191, 220, 228, 0.56);
|
||||
border-color: rgba(156, 199, 211, 0.9);
|
||||
color: #335e69;
|
||||
}
|
||||
|
||||
html[data-theme='coolvibe'] .session-project-count {
|
||||
@@ -337,8 +337,8 @@ html[data-theme='coolvibe'] .theme-card.active {
|
||||
|
||||
html[data-theme='editorial'] .session-project-header {
|
||||
background: linear-gradient(180deg, rgba(239, 232, 220, 0.94), rgba(246, 241, 232, 0.84));
|
||||
color: #7f6f61;
|
||||
border-bottom: 1px solid rgba(139, 94, 60, 0.12);
|
||||
border-color: rgba(139, 94, 60, 0.22);
|
||||
color: #4f4035;
|
||||
}
|
||||
|
||||
html[data-theme='editorial'] .session-project-count {
|
||||
@@ -1146,15 +1146,18 @@ body.session-loading-active {
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
margin: 4px 2px 5px;
|
||||
padding: 6px 8px 5px;
|
||||
background: rgba(242, 235, 226, 0.92);
|
||||
margin: 6px 3px 6px;
|
||||
padding: 6px 8px;
|
||||
border: 1px solid rgba(134, 106, 80, 0.22);
|
||||
border-radius: 8px;
|
||||
background: rgba(255, 249, 242, 0.88);
|
||||
backdrop-filter: blur(8px);
|
||||
color: var(--text-muted);
|
||||
font-size: 11px;
|
||||
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.72), 0 1px 6px rgba(45, 31, 20, 0.04);
|
||||
color: var(--text-secondary);
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0;
|
||||
text-transform: none;
|
||||
}
|
||||
.session-project-name {
|
||||
min-width: 0;
|
||||
@@ -1162,14 +1165,15 @@ body.session-loading-active {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
flex: 1;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
.session-project-toggle {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
height: 24px;
|
||||
gap: 6px;
|
||||
height: 26px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
@@ -1182,9 +1186,13 @@ body.session-loading-active {
|
||||
}
|
||||
.session-project-toggle:hover,
|
||||
.session-project-toggle:focus-visible {
|
||||
color: var(--text-secondary);
|
||||
color: var(--accent);
|
||||
outline: none;
|
||||
}
|
||||
.session-project-toggle:hover .session-project-name,
|
||||
.session-project-toggle:focus-visible .session-project-name {
|
||||
color: var(--accent);
|
||||
}
|
||||
.session-project-toggle:focus-visible .session-project-name {
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 3px;
|
||||
@@ -1192,8 +1200,8 @@ body.session-loading-active {
|
||||
.session-project-chevron {
|
||||
width: 12px;
|
||||
flex-shrink: 0;
|
||||
color: var(--text-muted);
|
||||
font-size: 10px;
|
||||
color: currentColor;
|
||||
font-size: 11px;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
transform: translateY(-0.5px);
|
||||
@@ -1211,9 +1219,10 @@ body.session-loading-active {
|
||||
min-width: 22px;
|
||||
height: 18px;
|
||||
padding: 0 7px;
|
||||
border: 1px solid rgba(134, 106, 80, 0.14);
|
||||
border-radius: 999px;
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text-secondary);
|
||||
background: rgba(45, 31, 20, 0.06);
|
||||
color: var(--text-primary);
|
||||
font-size: 10px;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
@@ -1244,6 +1253,9 @@ body.session-loading-active {
|
||||
.session-pinned-header {
|
||||
color: var(--accent);
|
||||
}
|
||||
.session-pinned-header .session-project-name {
|
||||
color: var(--accent);
|
||||
}
|
||||
.session-project-sessions[hidden] {
|
||||
display: none;
|
||||
}
|
||||
@@ -1256,6 +1268,9 @@ body.session-loading-active {
|
||||
.session-project-group.has-active-session.collapsed .session-project-header {
|
||||
color: var(--accent);
|
||||
}
|
||||
.session-project-group.has-active-session.collapsed .session-project-name {
|
||||
color: var(--accent);
|
||||
}
|
||||
.session-project-group.has-active-session.collapsed .session-project-count {
|
||||
background: var(--accent-light);
|
||||
color: var(--accent);
|
||||
@@ -1460,23 +1475,24 @@ body.session-loading-active {
|
||||
color: var(--danger);
|
||||
}
|
||||
.session-list-load-more {
|
||||
width: calc(100% - 4px);
|
||||
margin: 6px 2px 10px;
|
||||
padding: 9px 10px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
background: rgba(255, 249, 242, 0.72);
|
||||
width: fit-content;
|
||||
max-width: calc(100% - 18px);
|
||||
margin: 2px 8px 6px;
|
||||
padding: 3px 7px;
|
||||
border: 1px solid rgba(134, 106, 80, 0.18);
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
justify-content: flex-start;
|
||||
gap: 6px;
|
||||
text-align: left;
|
||||
transition: background 0.16s, border-color 0.16s, color 0.16s, transform 0.16s;
|
||||
}
|
||||
.session-list-load-more:hover {
|
||||
background: var(--bg-tertiary);
|
||||
background: rgba(255, 249, 242, 0.58);
|
||||
border-color: rgba(192, 85, 58, 0.24);
|
||||
color: var(--accent);
|
||||
transform: translateY(-1px);
|
||||
@@ -1487,13 +1503,13 @@ body.session-loading-active {
|
||||
}
|
||||
.session-list-load-more-title {
|
||||
min-width: 0;
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.session-list-load-more-meta {
|
||||
flex-shrink: 0;
|
||||
color: var(--text-muted);
|
||||
font-size: 11px;
|
||||
font-size: 10px;
|
||||
}
|
||||
/* Inline edit in sidebar */
|
||||
.session-item-edit-input {
|
||||
|
||||
Reference in New Issue
Block a user