mirror of
https://github.com/hansjone/netx.git
synced 2026-10-09 17:50:44 +08:00
Improve WebCRT UX: real login transcript, session tools, and first-input sync.
Capture Netmiko session_log for login replay, open NE terminals in a fresh tab, tidy the device list UI with reconnect/log/clear/copy, and paint a live prompt after attach so the first keystrokes behave normally. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
30f2db9708
commit
89ff0a7212
11 changed files with 593 additions and 183 deletions
|
|
@ -30,8 +30,12 @@ function moduleAckKey(moduleId: string): string {
|
|||
}
|
||||
|
||||
export function registerModuleWindow(moduleId: string): () => void {
|
||||
const sharedName = moduleWindowName(moduleId);
|
||||
try {
|
||||
window.name = moduleWindowName(moduleId);
|
||||
// Keep unique names from openNewModuleWindow; only claim the singleton if unnamed.
|
||||
if (!window.name) {
|
||||
window.name = sharedName;
|
||||
}
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
|
|
@ -40,9 +44,29 @@ export function registerModuleWindow(moduleId: string): () => void {
|
|||
MODULE_CHANNEL,
|
||||
moduleAckKey(moduleId),
|
||||
(data) => {
|
||||
// One-shot session windows (unique name) must not merge into the singleton module tab.
|
||||
if (window.name && window.name !== sharedName) {
|
||||
return;
|
||||
}
|
||||
const targetPath = String(data.path || "").trim();
|
||||
if (targetPath && window.location.pathname !== targetPath) {
|
||||
window.location.assign(targetPath);
|
||||
if (targetPath) {
|
||||
// Compare pathname only for hard navigation. Same route + new query
|
||||
// must soft-navigate so React state (open tabs) survives.
|
||||
try {
|
||||
const url = new URL(targetPath, window.location.origin);
|
||||
const next = `${url.pathname}${url.search}${url.hash}`;
|
||||
const cur = `${window.location.pathname}${window.location.search}${window.location.hash}`;
|
||||
if (window.location.pathname !== url.pathname) {
|
||||
window.location.assign(next);
|
||||
} else if (cur !== next) {
|
||||
window.history.pushState({}, "", next);
|
||||
window.dispatchEvent(new PopStateEvent("popstate"));
|
||||
}
|
||||
} catch {
|
||||
if (window.location.pathname + window.location.search !== targetPath) {
|
||||
window.location.assign(targetPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
window.focus();
|
||||
},
|
||||
|
|
@ -74,4 +98,16 @@ export function openOrFocusModule({ moduleId, path }: ModuleWindowSpec): void {
|
|||
});
|
||||
}
|
||||
|
||||
/** Always open a fresh browser tab/window; never focus or merge with an existing module tab. */
|
||||
export function openNewModuleWindow({ moduleId, path }: ModuleWindowSpec): void {
|
||||
const mod = getModuleById(moduleId);
|
||||
const base = mod?.path ?? "/";
|
||||
const targetPath = path && (path === base || path.startsWith(`${base}?`) || path.startsWith(`${base}/`))
|
||||
? path
|
||||
: base;
|
||||
const uniqueName = `${moduleWindowName(moduleId)}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
focusWindowSafe(window.open(targetPath, uniqueName));
|
||||
}
|
||||
|
||||
|
||||
export { moduleIdFromPath } from "../config/modules";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue