|
|
|
|
@@ -16,6 +16,7 @@ const indexHTML = `<!doctype html>
|
|
|
|
|
<p id="summary">Pruefe Updates...</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="actions">
|
|
|
|
|
<button id="settingsBtn" type="button">Einstellungen</button>
|
|
|
|
|
<button id="refreshBtn" type="button" title="Updates pruefen">Refresh</button>
|
|
|
|
|
<button id="installSelectedBtn" type="button" disabled>Auswahl installieren</button>
|
|
|
|
|
<button id="installBtn" type="button">Alle installieren</button>
|
|
|
|
|
@@ -114,35 +115,56 @@ const indexHTML = `<!doctype html>
|
|
|
|
|
</div>
|
|
|
|
|
</dl>
|
|
|
|
|
</section>
|
|
|
|
|
</aside>
|
|
|
|
|
</section>
|
|
|
|
|
</main>
|
|
|
|
|
|
|
|
|
|
<section class="panel settings-panel">
|
|
|
|
|
<div class="panel-head">
|
|
|
|
|
<dialog id="settingsDialog">
|
|
|
|
|
<form id="settingsForm" method="dialog" class="settings-modal">
|
|
|
|
|
<header class="modal-head">
|
|
|
|
|
<div>
|
|
|
|
|
<h2>Einstellungen</h2>
|
|
|
|
|
<span id="saveState"></span>
|
|
|
|
|
<p>Update-Verhalten, Installation und ausgeblendete Pakete</p>
|
|
|
|
|
</div>
|
|
|
|
|
<form id="settingsForm">
|
|
|
|
|
<button id="closeSettingsBtn" type="button" class="icon-btn">Schliessen</button>
|
|
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
<nav class="tabs" aria-label="Einstellungen">
|
|
|
|
|
<button class="tab active" type="button" data-tab="general">Allgemein</button>
|
|
|
|
|
<button class="tab" type="button" data-tab="notifications">Benachrichtigungen</button>
|
|
|
|
|
<button class="tab" type="button" data-tab="install">Installation</button>
|
|
|
|
|
<button class="tab" type="button" data-tab="ignored">Ausgeblendet</button>
|
|
|
|
|
</nav>
|
|
|
|
|
|
|
|
|
|
<section class="tab-panel active" data-panel="general">
|
|
|
|
|
<label class="toggle">
|
|
|
|
|
<input id="checkAur" type="checkbox">
|
|
|
|
|
<span>AUR Updates pruefen</span>
|
|
|
|
|
</label>
|
|
|
|
|
|
|
|
|
|
<label class="toggle">
|
|
|
|
|
<input id="showIgnored" type="checkbox">
|
|
|
|
|
<span>Ausgeblendete anzeigen</span>
|
|
|
|
|
<span>Ausgeblendete Updates in der App anzeigen</span>
|
|
|
|
|
</label>
|
|
|
|
|
|
|
|
|
|
<label>
|
|
|
|
|
<span>Erinnerung alle</span>
|
|
|
|
|
<input id="reminderHours" type="number" min="1" step="1">
|
|
|
|
|
<small>Stunden</small>
|
|
|
|
|
</label>
|
|
|
|
|
|
|
|
|
|
<label>
|
|
|
|
|
<span>Auto-Refresh</span>
|
|
|
|
|
<input id="autoRefreshMinutes" type="number" min="0" step="1">
|
|
|
|
|
<small>Minuten, 0 deaktiviert</small>
|
|
|
|
|
</label>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<section class="tab-panel" data-panel="notifications">
|
|
|
|
|
<label class="toggle">
|
|
|
|
|
<input id="notificationsEnabled" type="checkbox">
|
|
|
|
|
<span>Woechentliche Erinnerungen senden</span>
|
|
|
|
|
</label>
|
|
|
|
|
<label>
|
|
|
|
|
<span>Erinnerung alle</span>
|
|
|
|
|
<input id="reminderHours" type="number" min="1" step="1">
|
|
|
|
|
<small>Stunden</small>
|
|
|
|
|
</label>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<section class="tab-panel" data-panel="install">
|
|
|
|
|
<label>
|
|
|
|
|
<span>Terminal</span>
|
|
|
|
|
<select id="terminal">
|
|
|
|
|
@@ -156,13 +178,26 @@ const indexHTML = `<!doctype html>
|
|
|
|
|
<option value="xterm">xterm</option>
|
|
|
|
|
</select>
|
|
|
|
|
</label>
|
|
|
|
|
<label class="toggle">
|
|
|
|
|
<input id="keepTerminalOpen" type="checkbox">
|
|
|
|
|
<span>Terminal nach Installationen offen lassen</span>
|
|
|
|
|
</label>
|
|
|
|
|
<label class="toggle">
|
|
|
|
|
<input id="confirmSelectedInstalls" type="checkbox">
|
|
|
|
|
<span>Selektive Installationen bestaetigen</span>
|
|
|
|
|
</label>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<section class="tab-panel" data-panel="ignored">
|
|
|
|
|
<div id="ignoredSettingsList" class="ignored-settings-list"></div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<footer class="modal-actions">
|
|
|
|
|
<span id="saveState"></span>
|
|
|
|
|
<button type="submit">Speichern</button>
|
|
|
|
|
</footer>
|
|
|
|
|
</form>
|
|
|
|
|
</section>
|
|
|
|
|
</aside>
|
|
|
|
|
</section>
|
|
|
|
|
</main>
|
|
|
|
|
</dialog>
|
|
|
|
|
<script src="/app.js"></script>
|
|
|
|
|
</body>
|
|
|
|
|
</html>`
|
|
|
|
|
@@ -498,6 +533,105 @@ form {
|
|
|
|
|
padding: 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dialog {
|
|
|
|
|
width: min(760px, calc(100vw - 28px));
|
|
|
|
|
max-height: min(760px, calc(100vh - 28px));
|
|
|
|
|
border: 1px solid var(--line);
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
padding: 0;
|
|
|
|
|
color: var(--text);
|
|
|
|
|
background: var(--panel);
|
|
|
|
|
box-shadow: 0 28px 80px rgba(0, 0, 0, 0.45);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dialog::backdrop {
|
|
|
|
|
background: rgba(0, 0, 0, 0.62);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.settings-modal {
|
|
|
|
|
padding: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.modal-head,
|
|
|
|
|
.modal-actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: 16px;
|
|
|
|
|
padding: 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.modal-head {
|
|
|
|
|
border-bottom: 1px solid var(--line);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.modal-head p {
|
|
|
|
|
margin-top: 4px;
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.icon-btn {
|
|
|
|
|
min-height: 36px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tabs {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
padding: 12px 16px 0;
|
|
|
|
|
border-bottom: 1px solid var(--line);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tab {
|
|
|
|
|
min-height: 38px;
|
|
|
|
|
border-bottom-left-radius: 0;
|
|
|
|
|
border-bottom-right-radius: 0;
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
background: transparent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tab.active {
|
|
|
|
|
border-color: var(--line);
|
|
|
|
|
border-bottom-color: var(--panel);
|
|
|
|
|
color: var(--text);
|
|
|
|
|
background: var(--panel-soft);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tab-panel {
|
|
|
|
|
display: none;
|
|
|
|
|
gap: 16px;
|
|
|
|
|
padding: 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tab-panel.active {
|
|
|
|
|
display: grid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.modal-actions {
|
|
|
|
|
border-top: 1px solid var(--line);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.ignored-settings-list {
|
|
|
|
|
display: grid;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.ignored-setting-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
min-height: 42px;
|
|
|
|
|
padding: 0 12px;
|
|
|
|
|
border: 1px solid var(--line);
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
background: var(--panel-soft);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.empty-inline {
|
|
|
|
|
margin: 0;
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
label {
|
|
|
|
|
display: grid;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
@@ -539,6 +673,10 @@ input[type="checkbox"] {
|
|
|
|
|
display: grid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tabs {
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#searchInput {
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
@@ -557,6 +695,9 @@ const ignoredCount = document.querySelector("#ignoredCount");
|
|
|
|
|
const diskFree = document.querySelector("#diskFree");
|
|
|
|
|
const kernel = document.querySelector("#kernel");
|
|
|
|
|
const refreshBtn = document.querySelector("#refreshBtn");
|
|
|
|
|
const settingsBtn = document.querySelector("#settingsBtn");
|
|
|
|
|
const settingsDialog = document.querySelector("#settingsDialog");
|
|
|
|
|
const closeSettingsBtn = document.querySelector("#closeSettingsBtn");
|
|
|
|
|
const installBtn = document.querySelector("#installBtn");
|
|
|
|
|
const installSelectedBtn = document.querySelector("#installSelectedBtn");
|
|
|
|
|
const selectAll = document.querySelector("#selectAll");
|
|
|
|
|
@@ -578,10 +719,16 @@ const lockState = document.querySelector("#lockState");
|
|
|
|
|
const form = document.querySelector("#settingsForm");
|
|
|
|
|
const checkAur = document.querySelector("#checkAur");
|
|
|
|
|
const showIgnored = document.querySelector("#showIgnored");
|
|
|
|
|
const notificationsEnabled = document.querySelector("#notificationsEnabled");
|
|
|
|
|
const keepTerminalOpen = document.querySelector("#keepTerminalOpen");
|
|
|
|
|
const confirmSelectedInstalls = document.querySelector("#confirmSelectedInstalls");
|
|
|
|
|
const reminderHours = document.querySelector("#reminderHours");
|
|
|
|
|
const autoRefreshMinutes = document.querySelector("#autoRefreshMinutes");
|
|
|
|
|
const terminal = document.querySelector("#terminal");
|
|
|
|
|
const saveState = document.querySelector("#saveState");
|
|
|
|
|
const ignoredSettingsList = document.querySelector("#ignoredSettingsList");
|
|
|
|
|
const tabs = document.querySelectorAll(".tab");
|
|
|
|
|
const tabPanels = document.querySelectorAll(".tab-panel");
|
|
|
|
|
|
|
|
|
|
let currentData = null;
|
|
|
|
|
let refreshTimer = null;
|
|
|
|
|
@@ -718,6 +865,9 @@ function render(data) {
|
|
|
|
|
|
|
|
|
|
checkAur.checked = data.settings.check_aur;
|
|
|
|
|
showIgnored.checked = data.settings.show_ignored;
|
|
|
|
|
notificationsEnabled.checked = data.settings.notifications_enabled;
|
|
|
|
|
keepTerminalOpen.checked = data.settings.keep_terminal_open;
|
|
|
|
|
confirmSelectedInstalls.checked = data.settings.confirm_selected_installs;
|
|
|
|
|
reminderHours.value = data.settings.reminder_interval_hours;
|
|
|
|
|
autoRefreshMinutes.value = data.settings.auto_refresh_minutes;
|
|
|
|
|
terminal.value = data.settings.terminal || "auto";
|
|
|
|
|
@@ -727,9 +877,36 @@ function render(data) {
|
|
|
|
|
|
|
|
|
|
renderTable();
|
|
|
|
|
renderIgnored();
|
|
|
|
|
renderIgnoredSettings();
|
|
|
|
|
scheduleAutoRefresh();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderIgnoredSettings() {
|
|
|
|
|
ignoredSettingsList.replaceChildren();
|
|
|
|
|
const names = currentData?.settings.ignored_packages || [];
|
|
|
|
|
if (names.length === 0) {
|
|
|
|
|
const empty = document.createElement("p");
|
|
|
|
|
empty.className = "empty-inline";
|
|
|
|
|
empty.textContent = "Keine ausgeblendeten Pakete.";
|
|
|
|
|
ignoredSettingsList.append(empty);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const name of names) {
|
|
|
|
|
const row = document.createElement("div");
|
|
|
|
|
row.className = "ignored-setting-row";
|
|
|
|
|
const label = document.createElement("span");
|
|
|
|
|
label.textContent = name;
|
|
|
|
|
const button = document.createElement("button");
|
|
|
|
|
button.type = "button";
|
|
|
|
|
button.className = "ghost";
|
|
|
|
|
button.textContent = "Einblenden";
|
|
|
|
|
button.addEventListener("click", () => setIgnored(name, false));
|
|
|
|
|
row.append(label, button);
|
|
|
|
|
ignoredSettingsList.append(row);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function updateSelectionUi(packages = visiblePackages()) {
|
|
|
|
|
const selectedCount = selectedPackages.size;
|
|
|
|
|
selectionText.textContent = selectedCount === 1 ? "1 Update ausgewaehlt" : selectedCount + " Updates ausgewaehlt";
|
|
|
|
|
@@ -768,6 +945,8 @@ async function setIgnored(name, ignored) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
refreshBtn.addEventListener("click", loadStatus);
|
|
|
|
|
settingsBtn.addEventListener("click", () => settingsDialog.showModal());
|
|
|
|
|
closeSettingsBtn.addEventListener("click", () => settingsDialog.close());
|
|
|
|
|
searchInput.addEventListener("input", renderTable);
|
|
|
|
|
sourceFilter.addEventListener("change", renderTable);
|
|
|
|
|
selectAll.addEventListener("change", () => {
|
|
|
|
|
@@ -799,10 +978,12 @@ installSelectedBtn.addEventListener("click", async () => {
|
|
|
|
|
if (packages.length === 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (currentData?.settings.confirm_selected_installs) {
|
|
|
|
|
const ok = window.confirm("Ausgewaehlte Pakete gezielt installieren? Unter Arch ist ein vollstaendiges Systemupdate meistens sicherer.");
|
|
|
|
|
if (!ok) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
installSelectedBtn.disabled = true;
|
|
|
|
|
try {
|
|
|
|
|
@@ -827,6 +1008,9 @@ form.addEventListener("submit", async (event) => {
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
check_aur: checkAur.checked,
|
|
|
|
|
show_ignored: showIgnored.checked,
|
|
|
|
|
notifications_enabled: notificationsEnabled.checked,
|
|
|
|
|
keep_terminal_open: keepTerminalOpen.checked,
|
|
|
|
|
confirm_selected_installs: confirmSelectedInstalls.checked,
|
|
|
|
|
reminder_interval_hours: Number(reminderHours.value),
|
|
|
|
|
auto_refresh_minutes: Number(autoRefreshMinutes.value),
|
|
|
|
|
terminal: terminal.value,
|
|
|
|
|
@@ -840,5 +1024,17 @@ form.addEventListener("submit", async (event) => {
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
for (const tab of tabs) {
|
|
|
|
|
tab.addEventListener("click", () => {
|
|
|
|
|
const target = tab.dataset.tab;
|
|
|
|
|
for (const item of tabs) {
|
|
|
|
|
item.classList.toggle("active", item === tab);
|
|
|
|
|
}
|
|
|
|
|
for (const panel of tabPanels) {
|
|
|
|
|
panel.classList.toggle("active", panel.dataset.panel === target);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
loadStatus();
|
|
|
|
|
`
|
|
|
|
|
|