Native viewer: use guruconnect:// protocol with fallback to download
This commit is contained in:
@@ -596,14 +596,17 @@
|
|||||||
<div class="connect-option-desc">Full keyboard capture including Win key, Alt+Tab, and Ctrl+Alt+Del.</div>
|
<div class="connect-option-desc">Full keyboard capture including Win key, Alt+Tab, and Ctrl+Alt+Del.</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="nativeCommandContainer" style="display: none;">
|
<div id="nativeNotInstalled" style="display: none; margin-top: 16px; padding: 16px; background: hsla(45, 93%, 47%, 0.1); border: 1px solid hsla(45, 93%, 47%, 0.3); border-radius: 8px;">
|
||||||
<div class="native-command">
|
<p style="margin-bottom: 12px; color: hsl(45, 93%, 55%);">
|
||||||
<button class="copy-btn" onclick="copyNativeCommand()">Copy</button>
|
<strong>Native viewer not installed</strong>
|
||||||
<code id="nativeCommandText"></code>
|
|
||||||
</div>
|
|
||||||
<p style="margin-top: 12px; font-size: 12px; color: hsl(var(--muted-foreground));">
|
|
||||||
Run this command in PowerShell or CMD where guruconnect-viewer.exe is located.
|
|
||||||
</p>
|
</p>
|
||||||
|
<p style="font-size: 13px; margin-bottom: 12px; color: hsl(var(--muted-foreground));">
|
||||||
|
Download and run GuruConnect to register the protocol handler, then click "Try Again".
|
||||||
|
</p>
|
||||||
|
<div style="display: flex; gap: 8px;">
|
||||||
|
<a href="/downloads/guruconnect.exe" class="btn btn-primary" style="text-decoration: none;">Download GuruConnect</a>
|
||||||
|
<button class="btn btn-outline" onclick="retryNativeViewer()">Try Again</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -927,24 +930,26 @@
|
|||||||
// Connect modal state
|
// Connect modal state
|
||||||
let connectSessionId = null;
|
let connectSessionId = null;
|
||||||
let connectMachineName = null;
|
let connectMachineName = null;
|
||||||
|
let protocolCheckTimeout = null;
|
||||||
|
|
||||||
const connectModal = document.getElementById("connectModal");
|
const connectModal = document.getElementById("connectModal");
|
||||||
const connectClose = document.getElementById("connectClose");
|
const connectClose = document.getElementById("connectClose");
|
||||||
const connectWeb = document.getElementById("connectWeb");
|
const connectWeb = document.getElementById("connectWeb");
|
||||||
const connectNative = document.getElementById("connectNative");
|
const connectNative = document.getElementById("connectNative");
|
||||||
const nativeCommandContainer = document.getElementById("nativeCommandContainer");
|
const nativeNotInstalled = document.getElementById("nativeNotInstalled");
|
||||||
const nativeCommandText = document.getElementById("nativeCommandText");
|
|
||||||
const connectMachineNameEl = document.getElementById("connectMachineName");
|
const connectMachineNameEl = document.getElementById("connectMachineName");
|
||||||
|
|
||||||
connectClose.addEventListener("click", () => {
|
connectClose.addEventListener("click", () => {
|
||||||
connectModal.classList.remove("active");
|
connectModal.classList.remove("active");
|
||||||
nativeCommandContainer.style.display = "none";
|
nativeNotInstalled.style.display = "none";
|
||||||
|
if (protocolCheckTimeout) clearTimeout(protocolCheckTimeout);
|
||||||
});
|
});
|
||||||
|
|
||||||
connectModal.addEventListener("click", (e) => {
|
connectModal.addEventListener("click", (e) => {
|
||||||
if (e.target === connectModal) {
|
if (e.target === connectModal) {
|
||||||
connectModal.classList.remove("active");
|
connectModal.classList.remove("active");
|
||||||
nativeCommandContainer.style.display = "none";
|
nativeNotInstalled.style.display = "none";
|
||||||
|
if (protocolCheckTimeout) clearTimeout(protocolCheckTimeout);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -953,27 +958,63 @@
|
|||||||
const viewerUrl = "/viewer.html?session_id=" + connectSessionId;
|
const viewerUrl = "/viewer.html?session_id=" + connectSessionId;
|
||||||
window.open(viewerUrl, "viewer_" + connectSessionId, "width=1280,height=800,menubar=no,toolbar=no,location=no,status=no");
|
window.open(viewerUrl, "viewer_" + connectSessionId, "width=1280,height=800,menubar=no,toolbar=no,location=no,status=no");
|
||||||
connectModal.classList.remove("active");
|
connectModal.classList.remove("active");
|
||||||
nativeCommandContainer.style.display = "none";
|
nativeNotInstalled.style.display = "none";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
connectNative.addEventListener("click", () => {
|
connectNative.addEventListener("click", () => {
|
||||||
if (connectSessionId) {
|
launchNativeViewer();
|
||||||
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
|
||||||
const serverUrl = protocol + "//" + window.location.host + "/ws/viewer";
|
|
||||||
const cmd = `guruconnect-viewer.exe -i ${connectSessionId} -s ${serverUrl}`;
|
|
||||||
nativeCommandText.textContent = cmd;
|
|
||||||
nativeCommandContainer.style.display = "block";
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function copyNativeCommand() {
|
function launchNativeViewer() {
|
||||||
const cmd = nativeCommandText.textContent;
|
if (!connectSessionId) return;
|
||||||
navigator.clipboard.writeText(cmd).then(() => {
|
|
||||||
const btn = document.querySelector(".copy-btn");
|
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
||||||
btn.textContent = "Copied!";
|
const serverUrl = encodeURIComponent(protocol + "//" + window.location.host + "/ws/viewer");
|
||||||
setTimeout(() => btn.textContent = "Copy", 2000);
|
const protocolUrl = `guruconnect://view/${connectSessionId}?server=${serverUrl}`;
|
||||||
});
|
|
||||||
|
// Try to launch the protocol handler
|
||||||
|
// We use a hidden iframe to avoid navigation issues
|
||||||
|
const iframe = document.createElement("iframe");
|
||||||
|
iframe.style.display = "none";
|
||||||
|
document.body.appendChild(iframe);
|
||||||
|
|
||||||
|
// Set up timeout to detect if protocol handler didn't work
|
||||||
|
let launched = false;
|
||||||
|
const startTime = Date.now();
|
||||||
|
|
||||||
|
// If window loses focus quickly, protocol likely worked
|
||||||
|
const blurHandler = () => {
|
||||||
|
if (Date.now() - startTime < 2000) {
|
||||||
|
launched = true;
|
||||||
|
connectModal.classList.remove("active");
|
||||||
|
nativeNotInstalled.style.display = "none";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.addEventListener("blur", blurHandler, { once: true });
|
||||||
|
|
||||||
|
// Try to launch
|
||||||
|
try {
|
||||||
|
iframe.contentWindow.location.href = protocolUrl;
|
||||||
|
} catch (e) {
|
||||||
|
// Some browsers throw on custom protocols
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: also try window.location for browsers that block iframe protocols
|
||||||
|
protocolCheckTimeout = setTimeout(() => {
|
||||||
|
window.removeEventListener("blur", blurHandler);
|
||||||
|
document.body.removeChild(iframe);
|
||||||
|
|
||||||
|
if (!launched) {
|
||||||
|
// Protocol handler probably not installed, show download option
|
||||||
|
nativeNotInstalled.style.display = "block";
|
||||||
|
}
|
||||||
|
}, 1500);
|
||||||
|
}
|
||||||
|
|
||||||
|
function retryNativeViewer() {
|
||||||
|
nativeNotInstalled.style.display = "none";
|
||||||
|
launchNativeViewer();
|
||||||
}
|
}
|
||||||
|
|
||||||
function connectToMachine(sessionId) {
|
function connectToMachine(sessionId) {
|
||||||
@@ -981,7 +1022,7 @@
|
|||||||
connectSessionId = sessionId;
|
connectSessionId = sessionId;
|
||||||
connectMachineName = machine?.agent_name || sessionId.slice(0, 8);
|
connectMachineName = machine?.agent_name || sessionId.slice(0, 8);
|
||||||
connectMachineNameEl.textContent = connectMachineName;
|
connectMachineNameEl.textContent = connectMachineName;
|
||||||
nativeCommandContainer.style.display = "none";
|
nativeNotInstalled.style.display = "none";
|
||||||
connectModal.classList.add("active");
|
connectModal.classList.add("active");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user