4 files changed
+105
-8
No files matched your search
+38
-5
@@ -144,7 +144,18 @@
|
||||
</label>
|
||||
<span class="input-action-wrap">
|
||||
<input id="password" name="password" type="password" maxlength="4096" autocomplete="current-password" />
|
||||
<button id="reveal-password" class="input-action" type="button" aria-label="显示密码">显示</button>
|
||||
<button id="reveal-password" class="input-action" type="button" aria-label="显示密码" aria-pressed="false">
|
||||
<svg class="reveal-icon reveal-icon-eye" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z" />
|
||||
<circle cx="12" cy="12" r="3" />
|
||||
</svg>
|
||||
<svg class="reveal-icon reveal-icon-eye-off" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M9.88 9.88a3 3 0 1 0 4.24 4.24" />
|
||||
<path d="M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68" />
|
||||
<path d="M6.61 6.61A13.53 13.53 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61" />
|
||||
<line x1="2" x2="22" y1="2" y2="22" />
|
||||
</svg>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -258,8 +269,24 @@
|
||||
<div><span data-i18n-zh="主机密钥" data-i18n-en="HOST KEY">主机密钥</span><strong id="metric-host-key">--</strong></div>
|
||||
</div>
|
||||
<div class="terminal-actions">
|
||||
<button id="clear-terminal" class="toolbar-button" type="button" data-i18n-zh="清屏" data-i18n-en="Clear">清屏</button>
|
||||
<button id="fullscreen-terminal" class="toolbar-button" type="button" data-i18n-zh="全屏" data-i18n-en="Fullscreen">全屏</button>
|
||||
<button id="clear-terminal" class="toolbar-button" type="button" aria-label="清屏" data-i18n-aria-label-zh="清屏" data-i18n-aria-label-en="Clear">
|
||||
<svg class="toolbar-button-icon" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M3 6h18" />
|
||||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
|
||||
<line x1="10" x2="10" y1="11" y2="17" />
|
||||
<line x1="14" x2="14" y1="11" y2="17" />
|
||||
</svg>
|
||||
<span class="button-label" data-i18n-zh="清屏" data-i18n-en="Clear">清屏</span>
|
||||
</button>
|
||||
<button id="fullscreen-terminal" class="toolbar-button" type="button" aria-label="全屏" data-i18n-aria-label-zh="全屏" data-i18n-aria-label-en="Fullscreen">
|
||||
<svg class="toolbar-button-icon" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M8 3H5a2 2 0 0 0-2 2v3" />
|
||||
<path d="M21 8V5a2 2 0 0 0-2-2h-3" />
|
||||
<path d="M3 16v3a2 2 0 0 0 2 2h3" />
|
||||
<path d="M16 21h3a2 2 0 0 0 2-2v-3" />
|
||||
</svg>
|
||||
<span class="button-label" data-i18n-zh="全屏" data-i18n-en="Fullscreen">全屏</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="terminal-stage" class="terminal-stage">
|
||||
@@ -295,9 +322,12 @@
|
||||
aria-controls="file-manager-panel"
|
||||
aria-expanded="false"
|
||||
tabindex="0"
|
||||
aria-label="文件管理"
|
||||
data-i18n-aria-label-zh="文件管理"
|
||||
data-i18n-aria-label-en="Files"
|
||||
>
|
||||
<svg class="dock-tab-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M3.5 7.5h6l2-2h9a1.5 1.5 0 0 1 1.5 1.5v11a1.5 1.5 0 0 1-1.5 1.5h-17A1.5 1.5 0 0 1 2 18V9a1.5 1.5 0 0 1 1.5-1.5Z" /></svg>
|
||||
<span data-i18n-zh="文件管理" data-i18n-en="Files">文件管理</span>
|
||||
<span class="button-label" data-i18n-zh="文件管理" data-i18n-en="Files">文件管理</span>
|
||||
</button>
|
||||
<button
|
||||
id="event-toggle"
|
||||
@@ -308,9 +338,12 @@
|
||||
aria-controls="event-log"
|
||||
aria-expanded="false"
|
||||
tabindex="-1"
|
||||
aria-label="会话日志"
|
||||
data-i18n-aria-label-zh="会话日志"
|
||||
data-i18n-aria-label-en="Session log"
|
||||
>
|
||||
<svg class="dock-tab-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 2.5h9l4 4V21H6a2 2 0 0 1-2-2V4.5a2 2 0 0 1 2-2Z" /><path d="M15 2.5V7h4M8 11h7M8 15h7" /></svg>
|
||||
<span data-i18n-zh="会话日志" data-i18n-en="Session log">会话日志</span>
|
||||
<span class="button-label" data-i18n-zh="会话日志" data-i18n-en="Session log">会话日志</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,7 @@ import { decryptPasswordResult, encryptPassword, isEncryptedPassword } from './p
|
||||
import { resolveConnectionControl, resolveConnectionPanel } from './ui-state';
|
||||
import { classifyHostKey, SSH_FINGERPRINT_RE, type HostKeyPrompt } from './host-key';
|
||||
import { FileManager, collectFileManagerElements } from './file-manager';
|
||||
import { resetTerminalForConnection } from './terminal-session';
|
||||
import './style.css';
|
||||
|
||||
type AuthMethod = 'password' | 'publickey';
|
||||
@@ -312,7 +313,7 @@ const ui = {
|
||||
|
||||
function updateRevealPasswordButton(): void {
|
||||
const revealed = ui.password.type === 'text';
|
||||
ui.revealPassword.textContent = revealed ? bilingual('隐藏', 'Hide') : bilingual('显示', 'Show');
|
||||
ui.revealPassword.setAttribute('aria-pressed', revealed ? 'true' : 'false');
|
||||
ui.revealPassword.setAttribute('aria-label', revealed
|
||||
? bilingual('隐藏密码', 'Hide password')
|
||||
: bilingual('显示密码', 'Show password'));
|
||||
@@ -1467,7 +1468,7 @@ async function connect(): Promise<void> {
|
||||
authorizationAbort = abortController;
|
||||
setState('connecting');
|
||||
ui.terminalEmpty.hidden = true;
|
||||
terminal.clear();
|
||||
resetTerminalForConnection(terminal);
|
||||
fitTerminal(false);
|
||||
|
||||
const host = normalizeHost(ui.host.value);
|
||||
|
||||
+54
-1
@@ -715,7 +715,7 @@ button:disabled {
|
||||
}
|
||||
|
||||
.input-action-wrap input {
|
||||
padding-right: 82px;
|
||||
padding-right: 44px;
|
||||
}
|
||||
|
||||
.input-action {
|
||||
@@ -723,6 +723,9 @@ button:disabled {
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
bottom: 5px;
|
||||
display: grid;
|
||||
width: 34px;
|
||||
place-items: center;
|
||||
border: 0;
|
||||
border-radius: 5px;
|
||||
background: var(--surface-2);
|
||||
@@ -732,6 +735,32 @@ button:disabled {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.input-action:hover {
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.reveal-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
fill: none;
|
||||
stroke: currentColor;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
stroke-width: 1.8;
|
||||
}
|
||||
|
||||
.reveal-icon-eye-off {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.input-action[aria-pressed='true'] .reveal-icon-eye {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.input-action[aria-pressed='true'] .reveal-icon-eye-off {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.key-field {
|
||||
display: grid;
|
||||
gap: 9px;
|
||||
@@ -1014,6 +1043,10 @@ button:disabled {
|
||||
}
|
||||
|
||||
.terminal-actions .toolbar-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
padding: 5px 7px;
|
||||
border-color: #263642;
|
||||
background: #151f28;
|
||||
@@ -1168,6 +1201,17 @@ button:disabled {
|
||||
stroke-width: 1.7;
|
||||
}
|
||||
|
||||
.toolbar-button-icon {
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
flex: 0 0 auto;
|
||||
fill: none;
|
||||
stroke: currentColor;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
stroke-width: 1.8;
|
||||
}
|
||||
|
||||
.dock-tab:hover {
|
||||
background: var(--surface-3);
|
||||
color: var(--text);
|
||||
@@ -1816,6 +1860,15 @@ button:disabled {
|
||||
.file-table th:nth-child(2) { width: 16%; }
|
||||
.file-table th:nth-child(3) { width: 16%; }
|
||||
.file-table th:nth-child(4) { width: 28%; }
|
||||
|
||||
.button-label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dock-tab {
|
||||
min-width: 0;
|
||||
padding: 0 13px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
const TERMINAL_RESET_SEQUENCE = '\x1bc';
|
||||
|
||||
interface WritableTerminal {
|
||||
write(data: string): void;
|
||||
}
|
||||
|
||||
export function resetTerminalForConnection(terminal: WritableTerminal): void {
|
||||
// Queue RIS behind pending SSH output so stale writes cannot land after the reset.
|
||||
terminal.write(TERMINAL_RESET_SEQUENCE);
|
||||
}
|
||||
Reference in new issue
Block a user