provisioning-platform/crates/provisioning-desktop/dist/output.html

138 lines
5 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Provisioning — Output</title>
<style>
/* Follows the system theme; color-scheme also adapts native scrollbars. */
:root {
color-scheme: light dark;
--bg: #ffffff; --fg: #1f2328; --muted: #656d76; --line: #d0d7de;
--ok: #1a7f37; --err: #cf222e; --accent: #0969da;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #0d1117; --fg: #e6edf3; --muted: #8b949e; --line: #30363d;
--ok: #3fb950; --err: #f85149; --accent: #58a6ff;
}
}
* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body {
background: var(--bg); color: var(--fg);
font-family: ui-monospace, "Cascadia Code", "Fira Code", "JetBrains Mono", monospace;
font-size: 13px; display: flex; flex-direction: column;
}
header {
padding: 10px 14px; border-bottom: 1px solid var(--line);
display: flex; align-items: baseline; gap: 10px; flex-shrink: 0;
}
header h1 { font-size: 13px; margin: 0; font-weight: 600; }
header .cmd { color: var(--muted); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
header .code { font-size: 11px; padding: 1px 7px; border-radius: 5px; border: 1px solid var(--line); }
header .code.ok { color: var(--ok); }
header .code.err { color: var(--err); }
main { flex: 1; overflow: auto; padding: 10px 14px; }
section { margin-bottom: 14px; }
section h2 {
font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em;
color: var(--muted); margin: 0 0 4px; font-weight: 600;
}
pre {
margin: 0; white-space: pre-wrap; word-break: break-word;
line-height: 1.5; font-size: 12px;
}
pre.stderr { color: var(--err); }
.empty { color: var(--muted); }
footer {
flex-shrink: 0; padding: 6px 14px; border-top: 1px solid var(--line);
display: flex; gap: 8px; justify-content: flex-end;
}
button {
background: transparent; color: var(--muted); border: 1px solid var(--line);
border-radius: 5px; padding: 3px 10px; font: inherit; font-size: 11px; cursor: pointer;
}
button:hover { color: var(--fg); border-color: var(--accent); }
</style>
</head>
<body>
<header>
<h1 id="title">Output</h1>
<span class="cmd" id="cmd"></span>
<span class="code" id="code"></span>
</header>
<main>
<section id="stdout-sec">
<h2>stdout</h2>
<pre id="stdout" class="empty">(no output yet)</pre>
</section>
<section id="stderr-sec" style="display:none">
<h2>stderr</h2>
<pre id="stderr" class="stderr"></pre>
</section>
</main>
<footer>
<button id="copy">copy</button>
<button id="refresh">refresh</button>
</footer>
<script>
const invoke = window.__TAURI_INTERNALS__ && window.__TAURI_INTERNALS__.invoke;
function render(o) {
if (!o) return;
document.title = "Output — " + o.title;
document.getElementById("title").textContent = o.title || "Output";
document.getElementById("cmd").textContent = o.command || "";
const codeEl = document.getElementById("code");
if (o.code === null || o.code === undefined) {
codeEl.textContent = "no exit"; codeEl.className = "code err";
} else {
codeEl.textContent = "exit " + o.code;
codeEl.className = "code " + (o.code === 0 ? "ok" : "err");
}
const out = document.getElementById("stdout");
if (o.stdout && o.stdout.length) { out.textContent = o.stdout; out.className = ""; }
else { out.textContent = "(empty)"; out.className = "empty"; }
const errSec = document.getElementById("stderr-sec");
const err = document.getElementById("stderr");
if (o.stderr && o.stderr.trim().length) {
err.textContent = o.stderr; errSec.style.display = "";
} else {
errSec.style.display = "none";
}
}
async function load() {
if (!invoke) return;
try {
const o = await invoke("get_launcher_output");
render(o);
} catch (e) {
document.getElementById("stderr-sec").style.display = "";
document.getElementById("stderr").textContent = "failed to load output: " + e;
}
}
// Rust injects a call to this after writing fresh output (see show_output_window).
window.__reloadOutput = load;
document.getElementById("refresh").addEventListener("click", load);
document.getElementById("copy").addEventListener("click", () => {
const text = document.getElementById("stdout").textContent;
navigator.clipboard.writeText(text).then(() => {
const b = document.getElementById("copy");
b.textContent = "copied!";
setTimeout(() => (b.textContent = "copy"), 1200);
}).catch(() => {});
});
document.addEventListener("DOMContentLoaded", load);
window.addEventListener("focus", load);
</script>
</body>
</html>