--
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
.tls-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
margin: 12px 0;
|
||||
}
|
||||
.tls-input {
|
||||
background-color: #202020;
|
||||
color: #E0E0E0;
|
||||
border: 1px solid #3a3a3a;
|
||||
border-radius: 6px;
|
||||
padding: 10px 12px;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
.tls-input:focus {
|
||||
outline: none;
|
||||
border-color: #00C0FF;
|
||||
}
|
||||
.tls-submit {
|
||||
align-self: flex-start;
|
||||
background-color: #202020;
|
||||
color: #E0E0E0;
|
||||
border: 1px solid #3a3a3a;
|
||||
border-radius: 6px;
|
||||
padding: 10px 20px;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
}
|
||||
.tls-submit:hover {
|
||||
border-color: #00C878;
|
||||
color: #00C878;
|
||||
}
|
||||
|
||||
.tls-progress-track {
|
||||
height: 6px;
|
||||
background-color: #202020;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin: 12px 0;
|
||||
}
|
||||
.tls-progress-bar {
|
||||
height: 100%;
|
||||
background-color: #00C878;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.tls-log {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
font-family: "MesloLGS NF", "Menlo", "Consolas", monospace;
|
||||
font-size: 10pt;
|
||||
}
|
||||
.tls-log-row {
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
.tls-summary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.tls-rank-badge {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 50%;
|
||||
background-color: #202020;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 4px solid currentColor;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.tls-rank-letters {
|
||||
font-size: 48pt;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
}
|
||||
.tls-rank-score {
|
||||
font-size: 10pt;
|
||||
color: #939393;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.tls-summary-meta {
|
||||
min-width: 200px;
|
||||
}
|
||||
.tls-target {
|
||||
margin: 0 0 8px 0;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.tls-finding {
|
||||
margin: 4px 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.tls-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.tls-table th, .tls-table td {
|
||||
border-bottom: 1px solid #3a3a3a;
|
||||
padding: 6px 8px;
|
||||
text-align: left;
|
||||
}
|
||||
.tls-table th {
|
||||
color: #939393;
|
||||
font-weight: 400;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
.tls-raw {
|
||||
max-height: 400px;
|
||||
overflow: auto;
|
||||
background-color: #202020;
|
||||
padding: 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 10pt;
|
||||
}
|
||||
@@ -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();
|
||||
})();
|
||||
Reference in New Issue
Block a user