diff --git a/frontend/src/terminal.ts b/frontend/src/terminal.ts index f91f51d..bf23196 100644 --- a/frontend/src/terminal.ts +++ b/frontend/src/terminal.ts @@ -113,6 +113,11 @@ export class SSHTerminal { async connect(config: SSHConnectionConfig): Promise { this.lastConfig = config; + this.terminal.clear(); + + const termStatus = document.getElementById('term-status'); + if (termStatus) termStatus.innerHTML = '
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 = '
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 { diff --git a/frontend/src/zmodem-handler.ts b/frontend/src/zmodem-handler.ts index 4a84a17..348d5db 100644 --- a/frontend/src/zmodem-handler.ts +++ b/frontend/src/zmodem-handler.ts @@ -55,9 +55,9 @@ export class ZmodemHandler { return; } - const packets: Array = []; + const packets: Array[] = []; 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); } } diff --git a/scripts/build-html.js b/scripts/build-html.js index 183f2d1..e2ca750 100644 --- a/scripts/build-html.js +++ b/scripts/build-html.js @@ -34,15 +34,26 @@ try { } } - // 4. Inline CSS (replace ) + // 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 ) html = html.replace(/]*rel=["']stylesheet["'][^>]*href=["']\/assets\/[^"']+["'][^>]*>/i, () => ``); html = html.replace(/]*rel=["']stylesheet["'][^>]*href=["']\/src\/[^"']+["'][^>]*>/i, () => ``); - // 5. Inline JS (replace or similar) + // 6. Inline JS (replace or similar) html = html.replace(/]*type=["']module["'][^>]*src=["']\/assets\/[^"']+["'][^>]*><\/script>/i, () => ``); html = html.replace(/]*type=["']module["'][^>]*src=["']\/src\/[^"']+["'][^>]*><\/script>/i, () => ``); - // 6. Write to src/worker/html.ts + // 7. Write to src/worker/html.ts const escapedHtml = html .replace(/\\/g, '\\\\') .replace(/`/g, '\\`')