fix: 修复前端多个问题

- 修复 favicon 未加载:构建脚本未内联 public/favicon.svg
- 修复终端重连残留:断开连接后清空终端内容和状态
- 修复 sz 下载文件损坏:将 Uint8Array 转为普通数组传给 save_to_disk
- 修复 rz 上传失败:input 元素过早移除导致 onchange 无法触发
This commit is contained in:
TanXin committed 2026-06-22 16:04:02 +08:00
1 parent 0871b64426
commit bdc8d0da17
3 files changed
+41 -6

No files matched your search

+11
View File
@@ -113,6 +113,11 @@ export class SSHTerminal {
async connect(config: SSHConnectionConfig): Promise<void> {
this.lastConfig = config;
this.terminal.clear();
const termStatus = document.getElementById('term-status');
if (termStatus) termStatus.innerHTML = '<div class="w-2 h-2 bg-primary-container animate-pulse"></div> Connected';
const wsUrl = new URL(window.location.href);
wsUrl.protocol = wsUrl.protocol === 'https:' ? 'wss:' : 'ws:';
wsUrl.pathname = '/api/ssh';
@@ -156,6 +161,10 @@ export class SSHTerminal {
connectWithWebSocket(ws: WebSocket): void {
this.lastConfig = null;
this.ws = ws;
this.terminal.clear();
const termStatus = document.getElementById('term-status');
if (termStatus) termStatus.innerHTML = '<div class="w-2 h-2 bg-primary-container animate-pulse"></div> Connected';
ws.onopen = () => {
this.terminal.writeln('\x1b[32m[+] WebSocket connected, authenticating...\x1b[0m');
@@ -301,6 +310,8 @@ export class SSHTerminal {
this.ws = null;
this.disposables.forEach(d => d.dispose());
this.disposables = [];
this.lastConfig = null;
this.terminal.clear();
}
dispose(): void {
+16 -3
View File
@@ -55,9 +55,9 @@ export class ZmodemHandler {
return;
}
const packets: Array<Uint8Array> = [];
const packets: Array<number>[] = [];
xfer.on('input', (payload: Uint8Array) => {
packets.push(payload);
packets.push(Array.from(payload));
});
xfer.accept().then(() => {
@@ -89,14 +89,27 @@ export class ZmodemHandler {
}
}).then(() => {
zsession.close();
input.remove();
});
} else {
zsession.close();
input.remove();
}
};
// Handle cancel (focus returns without file selection)
const onFocusBack = () => {
setTimeout(() => {
if (input.files?.length === 0) {
zsession.close();
input.remove();
}
}, 1000);
window.removeEventListener('focus', onFocusBack);
};
window.addEventListener('focus', onFocusBack);
document.body.appendChild(input);
input.click();
document.body.removeChild(input);
}
}
+14 -3
View File
@@ -34,15 +34,26 @@ try {
}
}
// 4. Inline CSS (replace <link rel="stylesheet" ...>)
// 4. Inline favicon.svg as base64 data URL
const faviconPath = path.join(frontendDir, 'public', 'favicon.svg');
if (fs.existsSync(faviconPath)) {
const faviconContent = fs.readFileSync(faviconPath, 'utf8');
const faviconBase64 = Buffer.from(faviconContent).toString('base64');
html = html.replace(
/href=["']\/favicon\.svg["']/i,
`href="data:image/svg+xml;base64,${faviconBase64}"`
);
}
// 5. Inline CSS (replace <link rel="stylesheet" ...>)
html = html.replace(/<link[^>]*rel=["']stylesheet["'][^>]*href=["']\/assets\/[^"']+["'][^>]*>/i, () => `<style>${cssContent}</style>`);
html = html.replace(/<link[^>]*rel=["']stylesheet["'][^>]*href=["']\/src\/[^"']+["'][^>]*>/i, () => `<style>${cssContent}</style>`);
// 5. Inline JS (replace <script type="module" src="/src/main.ts"></script> or similar)
// 6. Inline JS (replace <script type="module" src="/src/main.ts"></script> or similar)
html = html.replace(/<script[^>]*type=["']module["'][^>]*src=["']\/assets\/[^"']+["'][^>]*><\/script>/i, () => `<script type="module">${jsContent}</script>`);
html = html.replace(/<script[^>]*type=["']module["'][^>]*src=["']\/src\/[^"']+["'][^>]*><\/script>/i, () => `<script type="module">${jsContent}</script>`);
// 6. Write to src/worker/html.ts
// 7. Write to src/worker/html.ts
const escapedHtml = html
.replace(/\\/g, '\\\\')
.replace(/`/g, '\\`')