This commit is contained in:
2026-04-19 11:33:11 +09:00
parent 867ae25fa0
commit da8d91b87f
43 changed files with 4044 additions and 15 deletions
+118
View File
@@ -0,0 +1,118 @@
(function () {
const init = window.__TLS_INIT__;
if (!init) return;
const phaseEl = document.getElementById("tls-phase");
const barEl = document.getElementById("tls-progress-bar");
const logEl = document.getElementById("tls-log");
const SEV_COLOR = {
good: "bright-green",
normal: "bright-yellow",
notgood: "bright-orange",
bad: "bright-red",
serious: "magenta",
info: "tx",
};
let reconnectAttempts = 0;
const MAX_RECONNECTS = 3;
let ws = null;
let closedByDone = false;
function appendLog(phase, detail, severity) {
if (!logEl) return;
const row = document.createElement("div");
row.className = "tls-log-row";
const label = document.createElement("span");
label.className = "text-tx-alt font-small";
label.textContent = `[${phase}] `;
const msg = document.createElement("span");
msg.className = `text-${SEV_COLOR[severity] || "tx"}`;
msg.textContent = detail || "";
row.appendChild(label);
row.appendChild(msg);
logEl.appendChild(row);
logEl.scrollTop = logEl.scrollHeight;
}
function setProgress(value, phase) {
if (barEl) barEl.style.width = `${Math.max(0, Math.min(1, value)) * 100}%`;
if (phaseEl && phase) phaseEl.textContent = phase;
}
function connect() {
try {
ws = new WebSocket(init.wsUrl);
} catch (e) {
scheduleReconnect();
return;
}
ws.onmessage = (ev) => {
let msg;
try {
msg = JSON.parse(ev.data);
} catch (_) {
return;
}
if (msg.type === "history") {
(msg.entries || []).forEach((e) => appendLog(e.phase, e.detail, e.severity));
if (msg.status === "done") {
closedByDone = true;
location.replace(init.resultsUrl);
}
return;
}
if (msg.type === "progress") {
appendLog(msg.phase, msg.detail, msg.severity);
if (typeof msg.progress === "number") {
setProgress(msg.progress, msg.detail || msg.phase);
}
return;
}
if (msg.type === "finding") {
const f = msg.finding || {};
appendLog(f.category || "finding", `${f.severity_label || ""} ${f.title || ""} ${f.detail ? "— " + f.detail : ""}`.trim(), f.severity || "info");
return;
}
if (msg.type === "done") {
closedByDone = true;
setProgress(1.0, `done (rank ${msg.rank}, score ${msg.score})`);
location.replace(msg.redirect || init.resultsUrl);
return;
}
if (msg.type === "error") {
appendLog("error", msg.message || "engine failed", "serious");
return;
}
if (msg.type === "started") {
appendLog("started", msg.target || "", "info");
return;
}
};
ws.onclose = () => {
if (!closedByDone) scheduleReconnect();
};
ws.onerror = () => {
try { ws.close(); } catch (_) {}
};
}
function scheduleReconnect() {
if (closedByDone) return;
if (reconnectAttempts >= MAX_RECONNECTS) {
appendLog("ws", "WebSocket 接続が切断されました。ページをリロードしてください。", "bad");
return;
}
const delay = Math.min(10_000, 1000 * Math.pow(2, reconnectAttempts));
reconnectAttempts += 1;
setTimeout(connect, delay);
}
window.addEventListener("beforeunload", () => {
closedByDone = true;
try { ws && ws.close(); } catch (_) {}
});
connect();
})();