139 lines
5 KiB
HTML
139 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>
|