mirror of
https://github.com/hansjone/netx.git
synced 2026-10-09 13:00:44 +08:00
fix(webcrt): replay login banner/prompt on attach
Drain and nudge the device prompt after Netmiko login, replay bootstrap stdout when the WebSocket attaches, and surface connect errors in the terminal pane. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
8e3bed562f
commit
30f2db9708
6 changed files with 87 additions and 9 deletions
|
|
@ -75,7 +75,6 @@ export function WebTerminal({ wsUrl, title, onStatus, onReady }: Props) {
|
|||
|
||||
ws.onopen = () => {
|
||||
onStatusRef.current?.("open");
|
||||
term.writeln("\x1b[32mConnected.\x1b[0m");
|
||||
sendResize();
|
||||
onReadyRef.current?.();
|
||||
};
|
||||
|
|
@ -87,6 +86,9 @@ export function WebTerminal({ wsUrl, title, onStatus, onReady }: Props) {
|
|||
data?: string;
|
||||
state?: string;
|
||||
message?: string;
|
||||
ne_name?: string;
|
||||
ne_ip?: string;
|
||||
protocol?: string;
|
||||
};
|
||||
if (msg.type === "stdout" && typeof msg.data === "string") {
|
||||
term.write(msg.data);
|
||||
|
|
@ -95,7 +97,8 @@ export function WebTerminal({ wsUrl, title, onStatus, onReady }: Props) {
|
|||
if (msg.type === "status") {
|
||||
onStatusRef.current?.(String(msg.state || ""), msg.message);
|
||||
if (msg.state === "connected") {
|
||||
// Server ack; keep terminal clean.
|
||||
const where = [msg.ne_name || title, msg.ne_ip, msg.protocol].filter(Boolean).join(" · ");
|
||||
term.writeln(`\x1b[90m--- session ready${where ? `: ${where}` : ""} ---\x1b[0m`);
|
||||
return;
|
||||
}
|
||||
if (msg.state === "closed" || msg.state === "error") {
|
||||
|
|
|
|||
|
|
@ -1741,6 +1741,16 @@ pre {
|
|||
color: #fecaca;
|
||||
}
|
||||
|
||||
.webcrt-error-detail {
|
||||
margin: 8px 0 0;
|
||||
max-width: 80%;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
font-size: 12px;
|
||||
color: #fee2e2;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.webcrt-statusline {
|
||||
padding: 3px 8px;
|
||||
background: #ececec;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ type TermTab = {
|
|||
wsUrl: string;
|
||||
target: CliTargetItem;
|
||||
status: "connecting" | "connected" | "error" | "closed";
|
||||
errorMessage?: string;
|
||||
};
|
||||
|
||||
function targetKey(t: Pick<CliTargetItem, "source" | "id">): string {
|
||||
|
|
@ -120,8 +121,9 @@ export function WebcrtPage() {
|
|||
updateTab(key, { sessionId: sess.session_id, wsUrl, status: "connecting" });
|
||||
showOk(t("webcrt.opened", { name: target.name || target.ip_address }));
|
||||
} catch (err) {
|
||||
updateTab(key, { status: "error" });
|
||||
showError(webcrtErrorMessage(err, t));
|
||||
const message = webcrtErrorMessage(err, t);
|
||||
updateTab(key, { status: "error", errorMessage: message });
|
||||
showError(message);
|
||||
} finally {
|
||||
connectingKeysRef.current.delete(key);
|
||||
}
|
||||
|
|
@ -344,7 +346,8 @@ export function WebcrtPage() {
|
|||
) : null}
|
||||
{tab.status === "error" && !tab.wsUrl ? (
|
||||
<div className="webcrt-main__placeholder webcrt-main__placeholder--error">
|
||||
{t("webcrt.status.error")}
|
||||
<div>{t("webcrt.status.error")}</div>
|
||||
{tab.errorMessage ? <pre className="webcrt-error-detail">{tab.errorMessage}</pre> : null}
|
||||
</div>
|
||||
) : null}
|
||||
{tab.wsUrl ? (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue