From c1718257133cfce940451a36ce2cc449800686f4 Mon Sep 17 00:00:00 2001 From: cmliu Date: Tue, 28 Jul 2026 01:38:57 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=A7=BB=E9=99=A4=E7=BB=88=E7=AB=AF?= =?UTF-8?q?=E6=A0=87=E7=AD=BE=E5=92=8C=E8=BE=B9=E7=BC=98=E6=8C=87=E6=A0=87?= =?UTF-8?q?=EF=BC=8C=E7=AE=80=E5=8C=96=E7=95=8C=E9=9D=A2=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/index.html | 2 -- frontend/src/main.ts | 30 ++++++++++-------------------- frontend/src/style.css | 16 ++-------------- 3 files changed, 12 insertions(+), 36 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index ea39e41..696f8c5 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -254,7 +254,6 @@
-
边缘--
延迟--
时长00:00
主机密钥--
@@ -264,7 +263,6 @@
-
终端 · 等待
diff --git a/frontend/src/main.ts b/frontend/src/main.ts index 508796d..e912d72 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -273,7 +273,6 @@ const ui = { sessionTitle: element('session-title'), sessionSubtitle: element('session-subtitle'), liveOrb: element('live-orb'), - metricEdge: element('metric-edge'), metricRtt: element('metric-rtt'), metricUptime: element('metric-uptime'), metricHostKey: element('metric-host-key'), @@ -281,7 +280,6 @@ const ui = { terminalStage: element('terminal-stage'), terminalElement: element('terminal'), terminalEmpty: element('terminal-empty'), - terminalLabel: element('terminal-label'), emptyConnect: element('empty-connect'), clearTerminal: element('clear-terminal'), fullscreenTerminal: element('fullscreen-terminal'), @@ -359,7 +357,6 @@ let lastPingAt = 0; let pendingHostKey: { fingerprint: string; keyType: string } | null = null; let currentTargetKey = ''; let currentTargetLabel = ''; -let currentTerminalTarget = ''; let currentInitialCommand = ''; let initialCommandSent = false; let decoder = new TextDecoder('utf-8'); @@ -715,10 +712,12 @@ function clearCredentials(): void { clearPrivateKeyFields(); } -function invalidateAndClearCredentials(): void { +// A stale async history-password decrypt must never overwrite the form. The +// connection lifecycle keeps the entered credentials in place (history can +// restore them anyway), so only the in-flight load is invalidated here. +function invalidateHistoryPasswordLoad(): void { historyPasswordLoadGeneration++; historyPasswordLoading = false; - clearCredentials(); } function normalizeHost(host: string): string { @@ -1043,7 +1042,7 @@ function setPanelOpen(open: boolean): void { panelOpen = open; const view = resolveConnectionPanel(open, panelMedia.matches); if (!open && (connectionState === 'connecting' || connectionState === 'connected' || connectionState === 'disconnecting')) { - invalidateAndClearCredentials(); + invalidateHistoryPasswordLoad(); } if (!view.expanded && ui.panel.contains(document.activeElement)) ui.panelToggle.focus(); ui.shell.classList.toggle('panel-collapsed', view.desktopCollapsed); @@ -1096,7 +1095,7 @@ function stopTimers(): void { function markReady(message = bilingual('交互式 Shell 已就绪', 'Interactive shell ready')): void { if (connectionState === 'connected') return; - invalidateAndClearCredentials(); + invalidateHistoryPasswordLoad(); setState('connected'); setPanelOpen(false); void saveConnectedProfile().catch(() => { @@ -1154,7 +1153,6 @@ function handleServerMessage(message: ServerMessage): void { } if (type === 'rtt') { if (typeof message.latency === 'number') ui.metricRtt.textContent = `${Math.round(message.latency)} ms`; - if (message.colo) ui.metricEdge.textContent = message.colo; return; } if (type === 'host_key') { @@ -1246,7 +1244,7 @@ function failActiveConnection(activeSocket: WebSocket | null, closeReason: strin currentExpectedFingerprint = ''; stopTimers(); clearHostKeyPrompt(); - invalidateAndClearCredentials(); + invalidateHistoryPasswordLoad(); currentSessionSubtitle = displayReason; ui.sessionSubtitle.textContent = localize(displayReason); setState('error'); @@ -1306,7 +1304,6 @@ async function connect(): Promise { const username = ui.username.value.trim(); currentTargetKey = targetKey(host, port, username); currentTargetLabel = targetLabel(host, port, username); - currentTerminalTarget = `${username}@${host.includes(':') ? `[${host}]` : host}`; const pinnedKey = ui.fingerprint.value.trim() || hostKeys[currentTargetKey] || ''; currentExpectedFingerprint = pinnedKey; if (pinnedKey) ui.fingerprint.value = pinnedKey; @@ -1318,10 +1315,8 @@ async function connect(): Promise { ui.sessionTitle.textContent = currentTargetLabel; currentSessionSubtitle = localized('正在授权 Worker 会话...', 'Authorizing Worker session...'); ui.sessionSubtitle.textContent = localize(currentSessionSubtitle); - ui.metricEdge.textContent = '--'; ui.metricRtt.textContent = '--'; ui.metricHostKey.textContent = '--'; - ui.terminalLabel.textContent = `${bilingual('终端', 'terminal')} · ${currentTerminalTarget}`; event(bilingual(`正在连接 ${username}@${host}:${port}`, `Starting ${username}@${host}:${port}`), 'connect'); try { @@ -1334,7 +1329,6 @@ async function connect(): Promise { void historyProfile.catch(() => undefined); pendingHistory = { generation, target: currentTargetKey, profile: historyProfile }; const ticketRequest = issueTicket(abortController.signal); - clearCredentials(); const { ticket, sessionId } = await ticketRequest; if (authorizationAbort === abortController) authorizationAbort = null; if (generation !== connectGeneration) return; @@ -1389,7 +1383,7 @@ async function connect(): Promise { currentExpectedFingerprint = ''; stopTimers(); clearHostKeyPrompt(); - invalidateAndClearCredentials(); + invalidateHistoryPasswordLoad(); const wasActive = connectionState === 'connected'; const reason = closeEvent.reason ? bilingualServerMessage(closeEvent.reason) @@ -1406,7 +1400,7 @@ async function connect(): Promise { if (authorizationAbort === abortController) authorizationAbort = null; if (generation !== connectGeneration) return; pendingHistory = null; - invalidateAndClearCredentials(); + invalidateHistoryPasswordLoad(); clearHostKeyPrompt(); const message = error instanceof DOMException && error.name === 'AbortError' ? bilingual('连接授权已取消。', 'Connection authorization was cancelled.') @@ -1431,7 +1425,7 @@ function disconnect(reason = bilingual('已由用户断开连接', 'Disconnected socket = null; stopTimers(); clearHostKeyPrompt(); - invalidateAndClearCredentials(); + invalidateHistoryPasswordLoad(); currentExpectedFingerprint = ''; currentSessionSubtitle = messageTranslation(reason); ui.sessionSubtitle.textContent = reason; @@ -1685,9 +1679,6 @@ ui.languageToggle.addEventListener('click', () => { setState(connectionState); setPanelOpen(panelOpen); if (connectionState === 'idle' && !currentTargetLabel) ui.sessionTitle.textContent = bilingual('无活动会话', 'No active session'); - ui.terminalLabel.textContent = connectionState === 'idle' - ? bilingual('终端 · 等待', 'terminal · waiting') - : `${bilingual('终端', 'terminal')} · ${currentTerminalTarget}`; }); ui.themeToggle.addEventListener('click', () => { const next = document.documentElement.dataset.theme === 'light' ? 'dark' : 'light'; @@ -1725,7 +1716,6 @@ async function initialize(): Promise { setState('idle'); ui.sessionTitle.textContent = bilingual('无活动会话', 'No active session'); ui.sessionSubtitle.textContent = bilingual('选择目标并连接', 'Choose a target and connect'); - ui.terminalLabel.textContent = bilingual('终端 · 等待', 'terminal · waiting'); ui.eventMessage.textContent = bilingual('Worker 运行时待命', 'Worker runtime standing by'); initializeCompatibilityAPI(); const shouldAutoConnect = applyURLParameters(); diff --git a/frontend/src/style.css b/frontend/src/style.css index d1cdf55..59d43e2 100644 --- a/frontend/src/style.css +++ b/frontend/src/style.css @@ -962,7 +962,7 @@ button:disabled { .terminal-toolbar { display: grid; - grid-template-columns: 74px minmax(0, 1fr) auto; + grid-template-columns: 74px 1fr; align-items: center; gap: 10px; padding: 0 11px; @@ -991,17 +991,9 @@ button:disabled { background: #5fd49d; } -.terminal-label { - overflow: hidden; - font-family: var(--mono); - font-size: 9px; - text-align: center; - text-overflow: ellipsis; - white-space: nowrap; -} - .terminal-actions { gap: 5px; + justify-self: end; } .terminal-actions .toolbar-button { @@ -1450,10 +1442,6 @@ button:disabled { display: none; } - .terminal-label { - text-align: left; - } - .field-grid.two { grid-template-columns: 1fr; }