chore: rebuild CentOS7 release package

This commit is contained in:
shiyue
2026-07-03 08:53:37 +08:00
parent d816ae28b9
commit faf6adceb7
7 changed files with 499 additions and 82 deletions

View File

@@ -2,7 +2,7 @@
(function () {
'use strict';
const ASSET_VERSION = '20260629-ccweb-prompt-dark-theme';
const ASSET_VERSION = '20260702-visible-no-rerender';
const WS_URL = `${location.protocol === 'https:' ? 'wss' : 'ws'}://${location.host}/ws`;
const RENDER_DEBOUNCE = 100;
const COMPOSER_SUGGESTION_DEBOUNCE = 120;
@@ -76,6 +76,8 @@
const OLD_SESSION_COLLAPSE_DAYS = 7;
const OLD_SESSION_COLLAPSE_MS = OLD_SESSION_COLLAPSE_DAYS * 24 * 60 * 60 * 1000;
const SESSION_LOAD_OVERLAY_TIMEOUT_MS = 12_000;
const SESSION_LOAD_REQUEST_TIMEOUT_MS = 45_000;
const SESSION_RESUME_FALLBACK_MS = 1_500;
const MODEL_OPTIONS = [
{ value: 'opus', label: 'Opus', desc: '最强大1M 上下文' },
@@ -183,6 +185,8 @@
let loadedHistorySessionId = null;
let activeSessionLoad = null;
let sessionLoadOverlayTimer = null;
let sessionLoadRequestTimer = null;
let sessionResumeFallbackTimer = null;
let sidebarSwipe = null;
let activeComposerToken = null;
let composerSuggestionTimer = null;
@@ -202,6 +206,7 @@
let codexAppApprovalModal = null;
let pendingNewSessionRequest = null;
let pendingSessionSwitchRequest = null;
let pendingSessionResumeRequest = null;
let sessionSwitchRequestSeq = 0;
let skipDeleteConfirm = localStorage.getItem('cc-web-skip-delete-confirm') === '1';
let pendingInitialSessionLoad = false;
@@ -4126,7 +4131,11 @@
if (fileBrowserState && (fileBrowserState.sessionId !== snapshot.sessionId || (snapshot.cwd && fileBrowserState.rootPath !== snapshot.cwd))) {
closeFileBrowser();
}
const preserveStreaming = !!(options.preserveStreaming && isGenerating && snapshot.sessionId === currentSessionId && snapshot.isRunning);
const hasStreamingElement = !!document.getElementById('streaming-msg');
const preserveStreaming = !!(options.preserveStreaming &&
snapshot.sessionId === currentSessionId &&
snapshot.isRunning &&
(isGenerating || currentSessionRunning || hasStreamingElement));
if (isGenerating && !preserveStreaming) {
isGenerating = false;
generatingSessionId = null;
@@ -4220,6 +4229,59 @@
sessionLoadOverlayTimer = null;
}
function clearSessionLoadRequestTimer() {
if (!sessionLoadRequestTimer) return;
clearTimeout(sessionLoadRequestTimer);
sessionLoadRequestTimer = null;
}
function clearSessionResumeFallbackTimer() {
if (!sessionResumeFallbackTimer) return;
clearTimeout(sessionResumeFallbackTimer);
sessionResumeFallbackTimer = null;
}
function clearPendingSessionSwitchRequest(sessionId, requestId) {
if (!pendingSessionSwitchRequest) return;
if (sessionId && pendingSessionSwitchRequest.sessionId !== sessionId) return;
if (requestId && pendingSessionSwitchRequest.requestId !== requestId) return;
pendingSessionSwitchRequest = null;
}
function clearPendingSessionResumeRequest(sessionId, requestId) {
if (!pendingSessionResumeRequest) return;
if (sessionId && pendingSessionResumeRequest.sessionId !== sessionId) return;
if (requestId && pendingSessionResumeRequest.requestId !== requestId) return;
pendingSessionResumeRequest = null;
clearSessionResumeFallbackTimer();
}
function notifySessionLoadTimeout(sessionId) {
const meta = sessionId ? getSessionMeta(sessionId) : null;
const title = meta?.title ? `${meta.title}` : '所选会话';
appendError(`${title} 加载超时,已取消本次切换。若服务刚重启或网络恢复后,可重新点击该会话。`, {
transient: true,
autoDismissMs: 9000,
preserveScroll: false,
});
}
function scheduleSessionLoadRequestTimeout(sessionId, requestId) {
clearSessionLoadRequestTimer();
if (!sessionId || !requestId) return;
sessionLoadRequestTimer = setTimeout(() => {
sessionLoadRequestTimer = null;
if (!activeSessionLoad ||
activeSessionLoad.sessionId !== sessionId ||
activeSessionLoad.requestId !== requestId) {
return;
}
clearPendingSessionSwitchRequest(sessionId, requestId);
clearSessionLoading(sessionId);
notifySessionLoadTimeout(sessionId);
}, SESSION_LOAD_REQUEST_TIMEOUT_MS);
}
function releaseSessionLoadingOverlay({ keepActiveLoad = true, allowRetry = false } = {}) {
clearSessionLoadOverlayTimer();
document.body.classList.remove('session-loading-active');
@@ -4236,10 +4298,19 @@
function setSessionLoading(sessionId, options = {}) {
clearSessionLoadOverlayTimer();
clearSessionLoadRequestTimer();
const loading = !!sessionId;
const blocking = options.blocking !== false;
const requestId = loading ? (options.requestId || createSessionSwitchRequestId(sessionId)) : '';
activeSessionLoad = loading ? { sessionId, blocking, snapshot: null, requestId, overlayReleased: false } : null;
activeSessionLoad = loading ? {
sessionId,
blocking,
snapshot: null,
requestId,
overlayReleased: false,
recoverCurrent: options.recoverCurrent === true,
} : null;
if (loading) scheduleSessionLoadRequestTimeout(sessionId, requestId);
const showOverlay = !!(loading && blocking);
if (showOverlay) {
document.body.classList.add('session-loading-active');
@@ -4315,6 +4386,7 @@
blocking: options.blocking !== false,
label: options.label || '',
requestId: options.requestId || activeSessionLoad?.requestId || createSessionSwitchRequestId(sessionId),
recoverCurrent: options.recoverCurrent === true,
};
if (ws && ws.readyState === 1 && wsAuthenticated) {
flushPendingSessionSwitch();
@@ -4323,9 +4395,42 @@
if (!ws || ws.readyState > 1) connect();
}
function requestSessionResume(sessionId, options = {}) {
if (!sessionId) return;
pendingSessionResumeRequest = {
sessionId,
requestId: options.requestId || createSessionSwitchRequestId(sessionId),
};
if (ws && ws.readyState === 1 && wsAuthenticated) {
flushPendingSessionResume();
return;
}
if (!ws || ws.readyState > 1) connect();
}
function scheduleSessionResumeFallback(sessionId, requestId) {
clearSessionResumeFallbackTimer();
if (!sessionId || !requestId) return;
sessionResumeFallbackTimer = setTimeout(() => {
sessionResumeFallbackTimer = null;
if (!pendingSessionResumeRequest ||
pendingSessionResumeRequest.sessionId !== sessionId ||
pendingSessionResumeRequest.requestId !== requestId) {
return;
}
pendingSessionResumeRequest = null;
requestSessionLoad(sessionId, {
blocking: false,
label: '正在恢复运行输出…',
requestId: createSessionSwitchRequestId(sessionId),
recoverCurrent: true,
});
}, SESSION_RESUME_FALLBACK_MS);
}
function flushPendingSessionSwitch() {
if (!pendingSessionSwitchRequest) return;
if (!ws || ws.readyState !== 1 || !wsAuthenticated) return;
if (!pendingSessionSwitchRequest) return false;
if (!ws || ws.readyState !== 1 || !wsAuthenticated) return false;
const request = pendingSessionSwitchRequest;
pendingSessionSwitchRequest = null;
if (!activeSessionLoad) {
@@ -4333,9 +4438,20 @@
blocking: request.blocking,
label: request.label || undefined,
requestId: request.requestId,
recoverCurrent: request.recoverCurrent === true,
});
}
ws.send(JSON.stringify({ type: 'load_session', sessionId: request.sessionId, requestId: request.requestId }));
return true;
}
function flushPendingSessionResume() {
if (!pendingSessionResumeRequest) return false;
if (!ws || ws.readyState !== 1 || !wsAuthenticated) return false;
const request = pendingSessionResumeRequest;
ws.send(JSON.stringify({ type: 'resume_session', sessionId: request.sessionId, requestId: request.requestId }));
scheduleSessionResumeFallback(request.sessionId, request.requestId);
return true;
}
function showCachedSession(sessionId) {
@@ -4883,9 +4999,15 @@
if (activeSessionLoad?.sessionId && !isPageUnloading) {
pendingSessionSwitchRequest = {
sessionId: activeSessionLoad.sessionId,
blocking: activeSessionLoad.blocking,
blocking: false,
label: sessionLoadingLabel?.textContent || '',
requestId: activeSessionLoad.requestId || createSessionSwitchRequestId(activeSessionLoad.sessionId),
recoverCurrent: activeSessionLoad.recoverCurrent === true,
};
} else if (currentSessionId && (isGenerating || currentSessionRunning) && !isPageUnloading) {
pendingSessionResumeRequest = {
sessionId: currentSessionId,
requestId: createSessionSwitchRequestId(currentSessionId),
};
}
clearSessionLoading();
@@ -4934,7 +5056,18 @@
document.dispatchEvent(new CustomEvent('cc-web-auth-restored'));
loginOverlay.hidden = true;
app.hidden = false;
flushPendingSessionSwitch();
const flushedSessionSwitch = flushPendingSessionSwitch();
const flushedSessionResume = flushedSessionSwitch ? false : flushPendingSessionResume();
if (!flushedSessionSwitch &&
!flushedSessionResume &&
!pendingSessionSwitchRequest &&
!pendingSessionResumeRequest &&
currentSessionId &&
(isGenerating || currentSessionRunning)) {
requestSessionResume(currentSessionId, {
requestId: createSessionSwitchRequestId(currentSessionId),
});
}
send({ type: 'get_codex_config' });
// Check if must change password
if (msg.mustChangePassword) {
@@ -4944,6 +5077,8 @@
}
} else {
pendingSessionSwitchRequest = null;
pendingSessionResumeRequest = null;
clearSessionResumeFallbackTimer();
clearSessionLoading();
authToken = null;
wsAuthenticated = false;
@@ -5007,7 +5142,7 @@
const canSwitchToSessionInfo = matchesActiveLoad
|| matchesPendingNewSession
|| msg.sessionId === currentSessionId
|| (!currentSessionId && !activeLoad && !pendingNewSession)
|| (!messageRequestId && !currentSessionId && !activeLoad && !pendingNewSession)
|| (!messageRequestId && !activeLoad && !pendingNewSession);
mergeSessionListSnapshot(snapshot);
if (matchesActiveLoad) {
@@ -5041,6 +5176,10 @@
break;
case 'session_history_chunk':
if (activeSessionLoad?.recoverCurrent && activeSessionLoad.sessionId === msg.sessionId) {
if (!msg.remaining) finalizeLoadedSession(msg.sessionId);
break;
}
if (msg.sessionId === currentSessionId && loadedHistorySessionId === msg.sessionId) {
const blocking = isBlockingSessionLoad(msg.sessionId);
if (activeSessionLoad?.sessionId === msg.sessionId && activeSessionLoad.snapshot) {
@@ -5274,10 +5413,11 @@
case 'resume_generating':
if (!isCurrentSessionEvent(msg)) break;
clearPendingSessionResumeRequest(msg.sessionId || currentSessionId, msg.requestId);
// Server has an active process for this session — resume streaming
setCurrentSessionRunningState(true);
if (!isGenerating || !document.getElementById('streaming-msg')) {
startGenerating(msg.sessionId || currentSessionId);
startGenerating(msg.sessionId || currentSessionId, { follow: isNearBottom() });
} else {
updateGenerationControls();
toolGroupCount = 0;
@@ -5312,8 +5452,24 @@
}
break;
case 'error':
case 'resume_session_result':
if (!isCurrentSessionEvent(msg)) break;
clearPendingSessionResumeRequest(msg.sessionId || currentSessionId, msg.requestId);
setCurrentSessionRunningState(!!msg.isRunning);
if (!msg.isRunning && currentSessionId && msg.sessionId === currentSessionId) {
updateGenerationControls();
}
break;
case 'error':
const errorRequestId = String(msg.requestId || '');
const matchesActiveLoadError = !!(activeSessionLoad &&
(!msg.sessionId || msg.sessionId === activeSessionLoad.sessionId) &&
(!errorRequestId || errorRequestId === activeSessionLoad.requestId));
if (!matchesActiveLoadError && !isCurrentSessionEvent(msg)) break;
if (matchesActiveLoadError) {
clearPendingSessionSwitchRequest(activeSessionLoad.sessionId, activeSessionLoad.requestId);
}
if (msg.code === 'new_session_cwd_missing' && pendingNewSessionRequest?.cwd) {
const request = pendingNewSessionRequest;
pendingNewSessionRequest = null;
@@ -5401,8 +5557,12 @@
showToast(`${msg.title}」任务完成`, msg.sessionId);
showBrowserNotification(msg.title);
if (msg.sessionId === currentSessionId) {
// Reload current session to show completed response
openSession(msg.sessionId, { forceSync: true, blocking: false });
// 当前用户如果正在向上翻历史,不要自动整屏重绘并把滚动条拽回底部。
if (isNearBottom()) {
openSession(msg.sessionId, { forceSync: true, blocking: false });
} else {
send({ type: 'list_sessions' });
}
} else {
send({ type: 'list_sessions' });
}
@@ -5435,9 +5595,10 @@
}
// --- Generating State ---
function startGenerating(sessionId = currentSessionId) {
function startGenerating(sessionId = currentSessionId, options = {}) {
const targetSessionId = sessionId || currentSessionId || null;
if (targetSessionId && currentSessionId && targetSessionId !== currentSessionId) return false;
const shouldFollow = options.follow !== false;
isGenerating = true;
generatingSessionId = targetSessionId;
setCurrentSessionRunningState(true);
@@ -5469,7 +5630,11 @@
bubble.appendChild(toolsDiv);
syncAssistantLastSectionButton(msgEl);
messagesDiv.appendChild(msgEl);
scrollToBottom();
if (shouldFollow) {
scrollToBottom();
} else {
updateScrollbar();
}
return true;
}
@@ -10210,20 +10375,21 @@
rememberPw.checked = true;
}
// Visibility change: re-sync state when user returns to tab (critical for mobile)
// 页签切回只做轻量状态同步:不要因为 visible 事件整屏重载当前会话,
// 否则 renderMessages() 会重建气泡并把滚动条拉回底部。
document.addEventListener('visibilitychange', () => {
if (document.visibilityState !== 'visible') return;
if (!ws || ws.readyState > 1) {
// WS is dead, force reconnect
reconnectAttempts = 0;
connect();
} else if (ws.readyState === 1 && currentSessionId) {
// Preserve active streaming UI when returning to foreground.
if (isGenerating || currentSessionRunning) {
send({ type: 'load_session', sessionId: currentSessionId });
} else {
beginSessionSwitch(currentSessionId, { blocking: false, force: true });
} else if (ws.readyState === 1 && wsAuthenticated) {
if (currentSessionId && !activeSessionLoad && (isGenerating || currentSessionRunning)) {
requestSessionResume(currentSessionId, {
requestId: createSessionSwitchRequestId(currentSessionId),
});
}
send({ type: 'list_sessions' });
}
});

View File

@@ -173,6 +173,6 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/12.0.1/marked.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.9.1/mermaid.min.js"></script>
<script src="app.js?v=20260629-ccweb-prompt-dark-theme"></script>
<script src="app.js?v=20260702-visible-no-rerender"></script>
</body>
</html>