Fix deploy.sh CRLF line endings for Linux compatibility
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+217
-217
@@ -1,217 +1,217 @@
|
||||
(function () {
|
||||
const sessionId = window.location.pathname.split("/").pop();
|
||||
const canvas = document.getElementById("screen");
|
||||
const ctx = canvas.getContext("2d");
|
||||
const addressBar = document.getElementById("address-bar");
|
||||
const statusEl = document.getElementById("status");
|
||||
const overlay = document.getElementById("overlay");
|
||||
const overlayMsg = document.getElementById("overlay-msg");
|
||||
|
||||
let ws = null;
|
||||
let viewportWidth = 1280;
|
||||
let viewportHeight = 720;
|
||||
let scaleX = 1;
|
||||
let scaleY = 1;
|
||||
let pingTimer = null;
|
||||
|
||||
const wsProtocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
||||
const wsUrl = `${wsProtocol}//${window.location.host}/ws/${sessionId}`;
|
||||
|
||||
function setStatus(text) {
|
||||
statusEl.textContent = text;
|
||||
}
|
||||
|
||||
function showOverlay(message) {
|
||||
overlayMsg.textContent = message;
|
||||
overlay.classList.remove("hidden");
|
||||
}
|
||||
|
||||
function mapCoords(clientX, clientY) {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const x = (clientX - rect.left) / scaleX;
|
||||
const y = (clientY - rect.top) / scaleY;
|
||||
return { x, y };
|
||||
}
|
||||
|
||||
function send(payload) {
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify(payload));
|
||||
}
|
||||
}
|
||||
|
||||
function drawFrame(blob) {
|
||||
const img = new Image();
|
||||
const url = URL.createObjectURL(blob);
|
||||
img.onload = () => {
|
||||
if (canvas.width !== img.width || canvas.height !== img.height) {
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
viewportWidth = img.width;
|
||||
viewportHeight = img.height;
|
||||
updateScale();
|
||||
}
|
||||
ctx.drawImage(img, 0, 0);
|
||||
URL.revokeObjectURL(url);
|
||||
};
|
||||
img.src = url;
|
||||
}
|
||||
|
||||
function updateScale() {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
scaleX = rect.width / viewportWidth;
|
||||
scaleY = rect.height / viewportHeight;
|
||||
}
|
||||
|
||||
function connect() {
|
||||
ws = new WebSocket(wsUrl);
|
||||
ws.binaryType = "arraybuffer";
|
||||
|
||||
ws.onopen = () => {
|
||||
setStatus("已连接");
|
||||
pingTimer = setInterval(() => send({ type: "ping" }), 60000);
|
||||
};
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
if (event.data instanceof ArrayBuffer) {
|
||||
drawFrame(new Blob([event.data], { type: "image/jpeg" }));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const msg = JSON.parse(event.data);
|
||||
if (msg.type === "init") {
|
||||
viewportWidth = msg.width;
|
||||
viewportHeight = msg.height;
|
||||
addressBar.value = msg.url || "";
|
||||
updateScale();
|
||||
} else if (msg.type === "url" || msg.type === "url_update") {
|
||||
addressBar.value = msg.url || "";
|
||||
} else if (msg.type === "closed") {
|
||||
showOverlay("会话已结束");
|
||||
ws.close();
|
||||
} else if (msg.type === "error") {
|
||||
setStatus(msg.message);
|
||||
}
|
||||
} catch (_) {
|
||||
/* ignore */
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = () => {
|
||||
setStatus("已断开");
|
||||
clearInterval(pingTimer);
|
||||
};
|
||||
|
||||
ws.onerror = () => {
|
||||
setStatus("连接错误");
|
||||
};
|
||||
}
|
||||
|
||||
canvas.addEventListener("click", (e) => {
|
||||
canvas.focus();
|
||||
const { x, y } = mapCoords(e.clientX, e.clientY);
|
||||
send({ action: "click", x, y, button: "left" });
|
||||
});
|
||||
|
||||
canvas.addEventListener("dblclick", (e) => {
|
||||
e.preventDefault();
|
||||
const { x, y } = mapCoords(e.clientX, e.clientY);
|
||||
send({ action: "dblclick", x, y });
|
||||
});
|
||||
|
||||
canvas.addEventListener("wheel", (e) => {
|
||||
e.preventDefault();
|
||||
send({ action: "wheel", deltaX: e.deltaX, deltaY: e.deltaY });
|
||||
}, { passive: false });
|
||||
|
||||
canvas.addEventListener("mousemove", (e) => {
|
||||
const { x, y } = mapCoords(e.clientX, e.clientY);
|
||||
send({ action: "mousemove", x, y });
|
||||
});
|
||||
|
||||
const specialKeys = new Set([
|
||||
"Enter", "Backspace", "Delete", "Tab", "Escape",
|
||||
"ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight",
|
||||
"Home", "End", "PageUp", "PageDown",
|
||||
]);
|
||||
|
||||
canvas.addEventListener("keydown", (e) => {
|
||||
e.preventDefault();
|
||||
if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
||||
send({ action: "type", text: e.key });
|
||||
} else if (specialKeys.has(e.key)) {
|
||||
send({ action: "press", key: e.key });
|
||||
} else {
|
||||
send({ action: "keydown", key: e.key });
|
||||
}
|
||||
});
|
||||
|
||||
canvas.addEventListener("keyup", (e) => {
|
||||
e.preventDefault();
|
||||
if (e.key.length > 1 || e.ctrlKey || e.metaKey || e.altKey) {
|
||||
send({ action: "keyup", key: e.key });
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("resize", updateScale);
|
||||
|
||||
async function apiPost(path) {
|
||||
const res = await fetch(path, { method: "POST", credentials: "include" });
|
||||
if (res.status === 401) {
|
||||
window.location.href = "/";
|
||||
return null;
|
||||
}
|
||||
return res.json();
|
||||
}
|
||||
|
||||
async function ensureAuth() {
|
||||
const res = await fetch("/api/auth/me", { credentials: "include" });
|
||||
if (!res.ok) {
|
||||
window.location.href = "/";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
document.getElementById("btn-back").addEventListener("click", async () => {
|
||||
const data = await apiPost(`/api/session/${sessionId}/back`);
|
||||
if (data) addressBar.value = data.url;
|
||||
});
|
||||
|
||||
document.getElementById("btn-forward").addEventListener("click", async () => {
|
||||
const data = await apiPost(`/api/session/${sessionId}/forward`);
|
||||
if (data) addressBar.value = data.url;
|
||||
});
|
||||
|
||||
document.getElementById("btn-reload").addEventListener("click", async () => {
|
||||
const data = await apiPost(`/api/session/${sessionId}/reload`);
|
||||
if (data) addressBar.value = data.url;
|
||||
});
|
||||
|
||||
function navigateTo(url) {
|
||||
send({ type: "navigate", url });
|
||||
}
|
||||
|
||||
document.getElementById("btn-go").addEventListener("click", () => {
|
||||
navigateTo(addressBar.value.trim());
|
||||
});
|
||||
|
||||
addressBar.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Enter") {
|
||||
navigateTo(addressBar.value.trim());
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById("btn-close").addEventListener("click", async () => {
|
||||
await fetch(`/api/session/${sessionId}`, { method: "DELETE", credentials: "include" });
|
||||
showOverlay("会话已关闭");
|
||||
if (ws) ws.close();
|
||||
});
|
||||
|
||||
ensureAuth().then((ok) => {
|
||||
if (ok) {
|
||||
connect();
|
||||
canvas.focus();
|
||||
}
|
||||
});
|
||||
})();
|
||||
(function () {
|
||||
const sessionId = window.location.pathname.split("/").pop();
|
||||
const canvas = document.getElementById("screen");
|
||||
const ctx = canvas.getContext("2d");
|
||||
const addressBar = document.getElementById("address-bar");
|
||||
const statusEl = document.getElementById("status");
|
||||
const overlay = document.getElementById("overlay");
|
||||
const overlayMsg = document.getElementById("overlay-msg");
|
||||
|
||||
let ws = null;
|
||||
let viewportWidth = 1280;
|
||||
let viewportHeight = 720;
|
||||
let scaleX = 1;
|
||||
let scaleY = 1;
|
||||
let pingTimer = null;
|
||||
|
||||
const wsProtocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
||||
const wsUrl = `${wsProtocol}//${window.location.host}/ws/${sessionId}`;
|
||||
|
||||
function setStatus(text) {
|
||||
statusEl.textContent = text;
|
||||
}
|
||||
|
||||
function showOverlay(message) {
|
||||
overlayMsg.textContent = message;
|
||||
overlay.classList.remove("hidden");
|
||||
}
|
||||
|
||||
function mapCoords(clientX, clientY) {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const x = (clientX - rect.left) / scaleX;
|
||||
const y = (clientY - rect.top) / scaleY;
|
||||
return { x, y };
|
||||
}
|
||||
|
||||
function send(payload) {
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify(payload));
|
||||
}
|
||||
}
|
||||
|
||||
function drawFrame(blob) {
|
||||
const img = new Image();
|
||||
const url = URL.createObjectURL(blob);
|
||||
img.onload = () => {
|
||||
if (canvas.width !== img.width || canvas.height !== img.height) {
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
viewportWidth = img.width;
|
||||
viewportHeight = img.height;
|
||||
updateScale();
|
||||
}
|
||||
ctx.drawImage(img, 0, 0);
|
||||
URL.revokeObjectURL(url);
|
||||
};
|
||||
img.src = url;
|
||||
}
|
||||
|
||||
function updateScale() {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
scaleX = rect.width / viewportWidth;
|
||||
scaleY = rect.height / viewportHeight;
|
||||
}
|
||||
|
||||
function connect() {
|
||||
ws = new WebSocket(wsUrl);
|
||||
ws.binaryType = "arraybuffer";
|
||||
|
||||
ws.onopen = () => {
|
||||
setStatus("已连接");
|
||||
pingTimer = setInterval(() => send({ type: "ping" }), 60000);
|
||||
};
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
if (event.data instanceof ArrayBuffer) {
|
||||
drawFrame(new Blob([event.data], { type: "image/jpeg" }));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const msg = JSON.parse(event.data);
|
||||
if (msg.type === "init") {
|
||||
viewportWidth = msg.width;
|
||||
viewportHeight = msg.height;
|
||||
addressBar.value = msg.url || "";
|
||||
updateScale();
|
||||
} else if (msg.type === "url" || msg.type === "url_update") {
|
||||
addressBar.value = msg.url || "";
|
||||
} else if (msg.type === "closed") {
|
||||
showOverlay("会话已结束");
|
||||
ws.close();
|
||||
} else if (msg.type === "error") {
|
||||
setStatus(msg.message);
|
||||
}
|
||||
} catch (_) {
|
||||
/* ignore */
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = () => {
|
||||
setStatus("已断开");
|
||||
clearInterval(pingTimer);
|
||||
};
|
||||
|
||||
ws.onerror = () => {
|
||||
setStatus("连接错误");
|
||||
};
|
||||
}
|
||||
|
||||
canvas.addEventListener("click", (e) => {
|
||||
canvas.focus();
|
||||
const { x, y } = mapCoords(e.clientX, e.clientY);
|
||||
send({ action: "click", x, y, button: "left" });
|
||||
});
|
||||
|
||||
canvas.addEventListener("dblclick", (e) => {
|
||||
e.preventDefault();
|
||||
const { x, y } = mapCoords(e.clientX, e.clientY);
|
||||
send({ action: "dblclick", x, y });
|
||||
});
|
||||
|
||||
canvas.addEventListener("wheel", (e) => {
|
||||
e.preventDefault();
|
||||
send({ action: "wheel", deltaX: e.deltaX, deltaY: e.deltaY });
|
||||
}, { passive: false });
|
||||
|
||||
canvas.addEventListener("mousemove", (e) => {
|
||||
const { x, y } = mapCoords(e.clientX, e.clientY);
|
||||
send({ action: "mousemove", x, y });
|
||||
});
|
||||
|
||||
const specialKeys = new Set([
|
||||
"Enter", "Backspace", "Delete", "Tab", "Escape",
|
||||
"ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight",
|
||||
"Home", "End", "PageUp", "PageDown",
|
||||
]);
|
||||
|
||||
canvas.addEventListener("keydown", (e) => {
|
||||
e.preventDefault();
|
||||
if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
||||
send({ action: "type", text: e.key });
|
||||
} else if (specialKeys.has(e.key)) {
|
||||
send({ action: "press", key: e.key });
|
||||
} else {
|
||||
send({ action: "keydown", key: e.key });
|
||||
}
|
||||
});
|
||||
|
||||
canvas.addEventListener("keyup", (e) => {
|
||||
e.preventDefault();
|
||||
if (e.key.length > 1 || e.ctrlKey || e.metaKey || e.altKey) {
|
||||
send({ action: "keyup", key: e.key });
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("resize", updateScale);
|
||||
|
||||
async function apiPost(path) {
|
||||
const res = await fetch(path, { method: "POST", credentials: "include" });
|
||||
if (res.status === 401) {
|
||||
window.location.href = "/";
|
||||
return null;
|
||||
}
|
||||
return res.json();
|
||||
}
|
||||
|
||||
async function ensureAuth() {
|
||||
const res = await fetch("/api/auth/me", { credentials: "include" });
|
||||
if (!res.ok) {
|
||||
window.location.href = "/";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
document.getElementById("btn-back").addEventListener("click", async () => {
|
||||
const data = await apiPost(`/api/session/${sessionId}/back`);
|
||||
if (data) addressBar.value = data.url;
|
||||
});
|
||||
|
||||
document.getElementById("btn-forward").addEventListener("click", async () => {
|
||||
const data = await apiPost(`/api/session/${sessionId}/forward`);
|
||||
if (data) addressBar.value = data.url;
|
||||
});
|
||||
|
||||
document.getElementById("btn-reload").addEventListener("click", async () => {
|
||||
const data = await apiPost(`/api/session/${sessionId}/reload`);
|
||||
if (data) addressBar.value = data.url;
|
||||
});
|
||||
|
||||
function navigateTo(url) {
|
||||
send({ type: "navigate", url });
|
||||
}
|
||||
|
||||
document.getElementById("btn-go").addEventListener("click", () => {
|
||||
navigateTo(addressBar.value.trim());
|
||||
});
|
||||
|
||||
addressBar.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Enter") {
|
||||
navigateTo(addressBar.value.trim());
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById("btn-close").addEventListener("click", async () => {
|
||||
await fetch(`/api/session/${sessionId}`, { method: "DELETE", credentials: "include" });
|
||||
showOverlay("会话已关闭");
|
||||
if (ws) ws.close();
|
||||
});
|
||||
|
||||
ensureAuth().then((ok) => {
|
||||
if (ok) {
|
||||
connect();
|
||||
canvas.focus();
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user