package gui
const indexHTML = `
LazyUpdateManager
LazyUpdateManager
Pruefe Updates...
Updates
0
Keine Updates gefunden.
| Quelle |
Paket |
Aktuell |
Verfuegbar |
`
const appCSS = `
:root {
color-scheme: dark;
--bg: #101114;
--panel: #191b20;
--panel-soft: #20242b;
--text: #f1f5f9;
--muted: #9aa4b2;
--line: #303640;
--accent: #42d392;
--accent-strong: #2ab67c;
--warn: #f5c451;
--danger: #ff6b6b;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
min-width: 320px;
min-height: 100vh;
color: var(--text);
background: var(--bg);
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
button,
input,
select {
font: inherit;
}
.shell {
width: min(1180px, calc(100vw - 32px));
margin: 0 auto;
padding: 28px 0;
}
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 18px;
margin-bottom: 18px;
}
h1,
h2,
p {
margin: 0;
}
h1 {
font-size: 28px;
font-weight: 720;
}
h2 {
font-size: 16px;
font-weight: 680;
}
#summary {
margin-top: 5px;
color: var(--muted);
}
.actions {
display: flex;
gap: 10px;
}
button {
border: 1px solid var(--line);
border-radius: 8px;
min-height: 40px;
padding: 0 14px;
color: var(--text);
background: var(--panel-soft);
cursor: pointer;
}
button:hover {
border-color: var(--accent);
}
button:disabled {
cursor: progress;
opacity: 0.65;
}
#installBtn,
form button {
border-color: transparent;
background: var(--accent);
color: #07110d;
font-weight: 700;
}
#installBtn:hover,
form button:hover {
background: var(--accent-strong);
}
.content {
display: grid;
grid-template-columns: minmax(0, 1fr) 320px;
gap: 18px;
align-items: start;
}
.panel {
border: 1px solid var(--line);
border-radius: 8px;
background: var(--panel);
}
.panel-head {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 54px;
padding: 0 16px;
border-bottom: 1px solid var(--line);
}
#countBadge {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 32px;
height: 26px;
border-radius: 999px;
color: #07110d;
background: var(--accent);
font-weight: 760;
}
.warnings {
margin: 14px 16px 0;
padding: 12px;
border: 1px solid rgba(245, 196, 81, 0.45);
border-radius: 8px;
color: var(--warn);
background: rgba(245, 196, 81, 0.08);
}
.empty {
padding: 44px 16px;
color: var(--muted);
text-align: center;
}
table {
width: 100%;
border-collapse: collapse;
}
th,
td {
padding: 12px 16px;
border-bottom: 1px solid var(--line);
text-align: left;
vertical-align: middle;
}
th {
color: var(--muted);
font-size: 12px;
text-transform: uppercase;
}
td {
overflow-wrap: anywhere;
}
td:first-child {
width: 92px;
color: var(--accent);
font-weight: 700;
}
tr:last-child td {
border-bottom: 0;
}
form {
display: grid;
gap: 16px;
padding: 16px;
}
label {
display: grid;
gap: 8px;
color: var(--muted);
}
.toggle {
grid-template-columns: 18px 1fr;
align-items: center;
color: var(--text);
}
input,
select {
width: 100%;
min-height: 40px;
border: 1px solid var(--line);
border-radius: 8px;
padding: 0 10px;
color: var(--text);
background: var(--panel-soft);
}
input[type="checkbox"] {
width: 18px;
min-height: 18px;
accent-color: var(--accent);
}
small,
#saveState {
color: var(--muted);
}
@media (max-width: 820px) {
.shell {
width: min(100vw - 20px, 1180px);
padding: 16px 0;
}
.topbar,
.content {
display: grid;
grid-template-columns: 1fr;
}
.actions {
grid-template-columns: 48px 1fr;
display: grid;
}
th:nth-child(3),
td:nth-child(3) {
display: none;
}
}
`
const appJS = `
const summary = document.querySelector("#summary");
const countBadge = document.querySelector("#countBadge");
const refreshBtn = document.querySelector("#refreshBtn");
const installBtn = document.querySelector("#installBtn");
const updatesTable = document.querySelector("#updatesTable");
const updatesBody = document.querySelector("#updatesBody");
const emptyState = document.querySelector("#emptyState");
const warnings = document.querySelector("#warnings");
const form = document.querySelector("#settingsForm");
const checkAur = document.querySelector("#checkAur");
const reminderHours = document.querySelector("#reminderHours");
const terminal = document.querySelector("#terminal");
const saveState = document.querySelector("#saveState");
async function request(path, options = {}) {
const response = await fetch(path, {
headers: { "Content-Type": "application/json" },
...options,
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || "Request failed");
}
return data;
}
function render(data) {
summary.textContent = data.summary;
countBadge.textContent = data.total;
installBtn.disabled = data.total === 0;
checkAur.checked = data.settings.check_aur;
reminderHours.value = data.settings.reminder_interval_hours;
terminal.value = data.settings.terminal || "auto";
updatesBody.replaceChildren();
for (const pkg of data.packages) {
const row = document.createElement("tr");
for (const value of [pkg.Source, pkg.Name, pkg.Current || "-", pkg.Available || "-"]) {
const cell = document.createElement("td");
cell.textContent = value;
row.appendChild(cell);
}
updatesBody.appendChild(row);
}
updatesTable.hidden = data.packages.length === 0;
emptyState.hidden = data.packages.length !== 0;
warnings.hidden = data.warnings.length === 0;
warnings.textContent = data.warnings.join("\\n");
}
async function loadStatus() {
refreshBtn.disabled = true;
summary.textContent = "Pruefe Updates...";
try {
render(await request("/api/status"));
} catch (error) {
summary.textContent = error.message;
} finally {
refreshBtn.disabled = false;
}
}
refreshBtn.addEventListener("click", loadStatus);
installBtn.addEventListener("click", async () => {
installBtn.disabled = true;
try {
await request("/api/install", { method: "POST" });
summary.textContent = "Installation im Terminal gestartet.";
} catch (error) {
summary.textContent = error.message;
} finally {
installBtn.disabled = false;
}
});
form.addEventListener("submit", async (event) => {
event.preventDefault();
saveState.textContent = "Speichere...";
try {
await request("/api/settings", {
method: "PUT",
body: JSON.stringify({
check_aur: checkAur.checked,
reminder_interval_hours: Number(reminderHours.value),
terminal: terminal.value,
}),
});
saveState.textContent = "Gespeichert";
await loadStatus();
} catch (error) {
saveState.textContent = error.message;
}
});
loadStatus();
`