[feat](trx-frontend-http): implement Phase 1 UX/UI quick wins from Settings analysis
- IX-2: Add confirm() dialogs before all destructive actions (10 history clear buttons, scheduler reset, background decode reset) - IX-6: Add Select All / Deselect All buttons for background decode bookmark checklist - IX-1: Add dirty-state indicator (pulsing dot) on Save buttons when unsaved changes exist in scheduler and background decode panels - A-4: Add role="alert" and aria-live="polite" to toast notification elements for screen reader accessibility - A-3: Add Unicode symbol prefixes to background decode state labels (checkmark/triangle/cross) so state is distinguishable without color https://claude.ai/code/session_01ShfPMW9hPLD3czp9YovkbJ Signed-off-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1074,7 +1074,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="subtab-settings-scheduler" class="sub-tab-panel">
|
<div id="subtab-settings-scheduler" class="sub-tab-panel">
|
||||||
<div id="scheduler-panel" class="sch-panel">
|
<div id="scheduler-panel" class="sch-panel">
|
||||||
<div class="sch-toast" id="scheduler-toast" style="display:none;"></div>
|
<div class="sch-toast" id="scheduler-toast" role="alert" aria-live="polite" style="display:none;"></div>
|
||||||
<!-- Now Playing status card (moved to top) -->
|
<!-- Now Playing status card (moved to top) -->
|
||||||
<div class="now-playing-card">
|
<div class="now-playing-card">
|
||||||
<div id="scheduler-status-card" class="sch-status-card">No activity yet.</div>
|
<div id="scheduler-status-card" class="sch-status-card">No activity yet.</div>
|
||||||
@@ -1254,7 +1254,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="subtab-settings-background-decode" class="sub-tab-panel" style="display:none;">
|
<div id="subtab-settings-background-decode" class="sub-tab-panel" style="display:none;">
|
||||||
<div id="background-decode-panel" class="sch-panel">
|
<div id="background-decode-panel" class="sch-panel">
|
||||||
<div class="sch-toast" id="background-decode-toast" style="display:none;"></div>
|
<div class="sch-toast" id="background-decode-toast" role="alert" aria-live="polite" style="display:none;"></div>
|
||||||
<!-- Now Playing status card (moved to top) -->
|
<!-- Now Playing status card (moved to top) -->
|
||||||
<div class="now-playing-card">
|
<div class="now-playing-card">
|
||||||
<div id="background-decode-status-card" class="sch-status-card">No background decode bookmarks configured.</div>
|
<div id="background-decode-status-card" class="sch-status-card">No background decode bookmarks configured.</div>
|
||||||
@@ -1273,6 +1273,10 @@
|
|||||||
<label class="sch-label" style="min-width:100%;">Bookmarks
|
<label class="sch-label" style="min-width:100%;">Bookmarks
|
||||||
<input type="text" id="bgd-bookmark-filter" class="bgd-checklist-filter" placeholder="Filter bookmarks..." />
|
<input type="text" id="bgd-bookmark-filter" class="bgd-checklist-filter" placeholder="Filter bookmarks..." />
|
||||||
</label>
|
</label>
|
||||||
|
<div class="bgd-select-actions">
|
||||||
|
<button type="button" id="bgd-select-all-btn" class="bgd-select-btn" aria-label="Select all bookmarks">Select All</button>
|
||||||
|
<button type="button" id="bgd-deselect-all-btn" class="bgd-select-btn" aria-label="Deselect all bookmarks">Deselect All</button>
|
||||||
|
</div>
|
||||||
<div id="bgd-bookmark-checklist" class="bgd-checklist"></div>
|
<div id="bgd-bookmark-checklist" class="bgd-checklist"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -378,6 +378,7 @@ window.pruneAisHistoryView = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
document.getElementById("settings-clear-ais-history")?.addEventListener("click", async () => {
|
document.getElementById("settings-clear-ais-history")?.addEventListener("click", async () => {
|
||||||
|
if (!confirm("Clear all AIS decode history? This cannot be undone.")) return;
|
||||||
try {
|
try {
|
||||||
await postPath("/clear_ais_decode");
|
await postPath("/clear_ais_decode");
|
||||||
window.resetAisHistoryView();
|
window.resetAisHistoryView();
|
||||||
|
|||||||
@@ -438,6 +438,7 @@ window.restoreAprsHistory = function(packets) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
document.getElementById("settings-clear-aprs-history")?.addEventListener("click", async () => {
|
document.getElementById("settings-clear-aprs-history")?.addEventListener("click", async () => {
|
||||||
|
if (!confirm("Clear all APRS decode history? This cannot be undone.")) return;
|
||||||
try {
|
try {
|
||||||
await postPath("/clear_aprs_decode");
|
await postPath("/clear_aprs_decode");
|
||||||
window.resetAprsHistoryView();
|
window.resetAprsHistoryView();
|
||||||
|
|||||||
+67
-11
@@ -12,6 +12,7 @@
|
|||||||
let currentConfig = null;
|
let currentConfig = null;
|
||||||
let bookmarkList = [];
|
let bookmarkList = [];
|
||||||
let statusInterval = null;
|
let statusInterval = null;
|
||||||
|
let bgdDirty = false;
|
||||||
|
|
||||||
function initBackgroundDecode(rigId, role) {
|
function initBackgroundDecode(rigId, role) {
|
||||||
backgroundDecodeRole = role;
|
backgroundDecodeRole = role;
|
||||||
@@ -77,6 +78,7 @@
|
|||||||
currentConfig = config || { remote: rigId, enabled: false, bookmark_ids: [] };
|
currentConfig = config || { remote: rigId, enabled: false, bookmark_ids: [] };
|
||||||
bookmarkList = Array.isArray(bookmarks) ? bookmarks : [];
|
bookmarkList = Array.isArray(bookmarks) ? bookmarks : [];
|
||||||
renderBackgroundDecode();
|
renderBackgroundDecode();
|
||||||
|
clearBgdDirty();
|
||||||
pollBackgroundDecodeStatus();
|
pollBackgroundDecodeStatus();
|
||||||
})
|
})
|
||||||
.catch(function (err) {
|
.catch(function (err) {
|
||||||
@@ -175,6 +177,7 @@
|
|||||||
} else if (!checked) {
|
} else if (!checked) {
|
||||||
currentConfig.bookmark_ids = currentConfig.bookmark_ids.filter(function (id) { return id !== bookmarkId; });
|
currentConfig.bookmark_ids = currentConfig.bookmark_ids.filter(function (id) { return id !== bookmarkId; });
|
||||||
}
|
}
|
||||||
|
markBgdDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveBackgroundDecode() {
|
function saveBackgroundDecode() {
|
||||||
@@ -191,6 +194,7 @@
|
|||||||
.then(function (saved) {
|
.then(function (saved) {
|
||||||
currentConfig = saved;
|
currentConfig = saved;
|
||||||
renderBackgroundDecode();
|
renderBackgroundDecode();
|
||||||
|
clearBgdDirty();
|
||||||
pollBackgroundDecodeStatus();
|
pollBackgroundDecodeStatus();
|
||||||
showToast("Background decode saved.");
|
showToast("Background decode saved.");
|
||||||
})
|
})
|
||||||
@@ -205,10 +209,12 @@
|
|||||||
function resetBackgroundDecode() {
|
function resetBackgroundDecode() {
|
||||||
const rigId = currentRigId;
|
const rigId = currentRigId;
|
||||||
if (!rigId) return;
|
if (!rigId) return;
|
||||||
|
if (!confirm("Reset background decode configuration? This cannot be undone.")) return;
|
||||||
apiResetConfig(rigId)
|
apiResetConfig(rigId)
|
||||||
.then(function (saved) {
|
.then(function (saved) {
|
||||||
currentConfig = saved;
|
currentConfig = saved;
|
||||||
renderBackgroundDecode();
|
renderBackgroundDecode();
|
||||||
|
clearBgdDirty();
|
||||||
pollBackgroundDecodeStatus();
|
pollBackgroundDecodeStatus();
|
||||||
showToast("Background decode reset.");
|
showToast("Background decode reset.");
|
||||||
})
|
})
|
||||||
@@ -272,17 +278,17 @@
|
|||||||
|
|
||||||
function prettyState(state) {
|
function prettyState(state) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case "active": return "Active";
|
case "active": return "\u2713 Active";
|
||||||
case "out_of_span": return "Out of span";
|
case "out_of_span": return "\u25B3 Out of span";
|
||||||
case "waiting_for_spectrum": return "Waiting";
|
case "waiting_for_spectrum": return "\u25B3 Waiting";
|
||||||
case "waiting_for_user": return "No user";
|
case "waiting_for_user": return "\u25B3 No user";
|
||||||
case "missing_bookmark": return "Missing";
|
case "missing_bookmark": return "\u2717 Missing";
|
||||||
case "no_supported_decoders": return "Unsupported";
|
case "no_supported_decoders": return "\u2717 Unsupported";
|
||||||
case "disabled": return "Disabled";
|
case "disabled": return "\u25B3 Disabled";
|
||||||
case "handled_by_scheduler": return "Scheduler";
|
case "handled_by_scheduler": return "\u25B3 Scheduler";
|
||||||
case "scheduler_has_control": return "Scheduler";
|
case "scheduler_has_control": return "\u25B3 Scheduler";
|
||||||
case "handled_by_virtual_channel": return "VChan";
|
case "handled_by_virtual_channel": return "\u25B3 VChan";
|
||||||
default: return "Inactive";
|
default: return "\u25B3 Inactive";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,6 +312,19 @@
|
|||||||
.replace(/"/g, """);
|
.replace(/"/g, """);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function markBgdDirty() {
|
||||||
|
if (bgdDirty) return;
|
||||||
|
bgdDirty = true;
|
||||||
|
var btn = document.getElementById("background-decode-save-btn");
|
||||||
|
if (btn) btn.classList.add("sch-dirty");
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearBgdDirty() {
|
||||||
|
bgdDirty = false;
|
||||||
|
var btn = document.getElementById("background-decode-save-btn");
|
||||||
|
if (btn) btn.classList.remove("sch-dirty");
|
||||||
|
}
|
||||||
|
|
||||||
function showToast(msg, isError) {
|
function showToast(msg, isError) {
|
||||||
const el = document.getElementById("background-decode-toast");
|
const el = document.getElementById("background-decode-toast");
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
@@ -317,6 +336,25 @@
|
|||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function selectAllBookmarks() {
|
||||||
|
if (!currentConfig) {
|
||||||
|
currentConfig = { remote: currentRigId, enabled: false, bookmark_ids: [] };
|
||||||
|
}
|
||||||
|
var ids = supportedBookmarks().map(function (bm) { return bm.id; });
|
||||||
|
currentConfig.bookmark_ids = ids;
|
||||||
|
renderBookmarkChecklist(document.getElementById("bgd-bookmark-filter")?.value);
|
||||||
|
markBgdDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
function deselectAllBookmarks() {
|
||||||
|
if (!currentConfig) {
|
||||||
|
currentConfig = { remote: currentRigId, enabled: false, bookmark_ids: [] };
|
||||||
|
}
|
||||||
|
currentConfig.bookmark_ids = [];
|
||||||
|
renderBookmarkChecklist(document.getElementById("bgd-bookmark-filter")?.value);
|
||||||
|
markBgdDirty();
|
||||||
|
}
|
||||||
|
|
||||||
function wireBackgroundDecodeEvents() {
|
function wireBackgroundDecodeEvents() {
|
||||||
const filterInput = document.getElementById("bgd-bookmark-filter");
|
const filterInput = document.getElementById("bgd-bookmark-filter");
|
||||||
if (filterInput && !filterInput._wired) {
|
if (filterInput && !filterInput._wired) {
|
||||||
@@ -326,6 +364,24 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const enabledCb = document.getElementById("background-decode-enabled");
|
||||||
|
if (enabledCb && !enabledCb._wired) {
|
||||||
|
enabledCb._wired = true;
|
||||||
|
enabledCb.addEventListener("change", function () { markBgdDirty(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectAllBtn = document.getElementById("bgd-select-all-btn");
|
||||||
|
if (selectAllBtn && !selectAllBtn._wired) {
|
||||||
|
selectAllBtn._wired = true;
|
||||||
|
selectAllBtn.addEventListener("click", selectAllBookmarks);
|
||||||
|
}
|
||||||
|
|
||||||
|
const deselectAllBtn = document.getElementById("bgd-deselect-all-btn");
|
||||||
|
if (deselectAllBtn && !deselectAllBtn._wired) {
|
||||||
|
deselectAllBtn._wired = true;
|
||||||
|
deselectAllBtn.addEventListener("click", deselectAllBookmarks);
|
||||||
|
}
|
||||||
|
|
||||||
const saveBtn = document.getElementById("background-decode-save-btn");
|
const saveBtn = document.getElementById("background-decode-save-btn");
|
||||||
if (saveBtn && !saveBtn._wired) {
|
if (saveBtn && !saveBtn._wired) {
|
||||||
saveBtn._wired = true;
|
saveBtn._wired = true;
|
||||||
|
|||||||
@@ -360,6 +360,7 @@ window.resetCwHistoryView = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
document.getElementById("settings-clear-cw-history")?.addEventListener("click", async () => {
|
document.getElementById("settings-clear-cw-history")?.addEventListener("click", async () => {
|
||||||
|
if (!confirm("Clear all CW decode history? This cannot be undone.")) return;
|
||||||
try {
|
try {
|
||||||
await postPath("/clear_cw_decode");
|
await postPath("/clear_cw_decode");
|
||||||
window.resetCwHistoryView();
|
window.resetCwHistoryView();
|
||||||
|
|||||||
@@ -190,6 +190,7 @@ ft2DecodeToggleBtn?.addEventListener("click", async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById("settings-clear-ft2-history")?.addEventListener("click", async () => {
|
document.getElementById("settings-clear-ft2-history")?.addEventListener("click", async () => {
|
||||||
|
if (!confirm("Clear all FT2 decode history? This cannot be undone.")) return;
|
||||||
try {
|
try {
|
||||||
await postPath("/clear_ft2_decode");
|
await postPath("/clear_ft2_decode");
|
||||||
window.resetFt2HistoryView();
|
window.resetFt2HistoryView();
|
||||||
|
|||||||
@@ -190,6 +190,7 @@ ft4DecodeToggleBtn?.addEventListener("click", async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById("settings-clear-ft4-history")?.addEventListener("click", async () => {
|
document.getElementById("settings-clear-ft4-history")?.addEventListener("click", async () => {
|
||||||
|
if (!confirm("Clear all FT4 decode history? This cannot be undone.")) return;
|
||||||
try {
|
try {
|
||||||
await postPath("/clear_ft4_decode");
|
await postPath("/clear_ft4_decode");
|
||||||
window.resetFt4HistoryView();
|
window.resetFt4HistoryView();
|
||||||
|
|||||||
@@ -458,6 +458,7 @@ ft8DecodeToggleBtn?.addEventListener("click", async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById("settings-clear-ft8-history")?.addEventListener("click", async () => {
|
document.getElementById("settings-clear-ft8-history")?.addEventListener("click", async () => {
|
||||||
|
if (!confirm("Clear all FT8 decode history? This cannot be undone.")) return;
|
||||||
try {
|
try {
|
||||||
await postPath("/clear_ft8_decode");
|
await postPath("/clear_ft8_decode");
|
||||||
window.resetFt8HistoryView();
|
window.resetFt8HistoryView();
|
||||||
|
|||||||
@@ -384,6 +384,7 @@ hfAprsDecodeToggleBtn?.addEventListener("click", async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById("settings-clear-hf-aprs-history")?.addEventListener("click", async () => {
|
document.getElementById("settings-clear-hf-aprs-history")?.addEventListener("click", async () => {
|
||||||
|
if (!confirm("Clear all HF APRS decode history? This cannot be undone.")) return;
|
||||||
try {
|
try {
|
||||||
await postPath("/clear_hf_aprs_decode");
|
await postPath("/clear_hf_aprs_decode");
|
||||||
window.resetHfAprsHistoryView();
|
window.resetHfAprsHistoryView();
|
||||||
|
|||||||
@@ -278,6 +278,7 @@ satDom.typeFilter?.addEventListener("change", () => renderSatHistoryTable());
|
|||||||
document
|
document
|
||||||
.getElementById("settings-clear-sat-history")
|
.getElementById("settings-clear-sat-history")
|
||||||
?.addEventListener("click", async () => {
|
?.addEventListener("click", async () => {
|
||||||
|
if (!confirm("Clear all satellite decode history? This cannot be undone.")) return;
|
||||||
try {
|
try {
|
||||||
await postPath("/clear_lrpt_decode");
|
await postPath("/clear_lrpt_decode");
|
||||||
window.resetSatHistoryView();
|
window.resetSatHistoryView();
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
let interleaveTicker = null;
|
let interleaveTicker = null;
|
||||||
let schedulerStepPending = false;
|
let schedulerStepPending = false;
|
||||||
let schEntryEditIdx = null; // null = adding, number = editing that index
|
let schEntryEditIdx = null; // null = adding, number = editing that index
|
||||||
|
let schedulerDirty = false; // true when unsaved changes exist
|
||||||
// Satellite entry editing state moved to sat-scheduler.js
|
// Satellite entry editing state moved to sat-scheduler.js
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
@@ -127,6 +128,7 @@
|
|||||||
bookmarkList = Array.isArray(bms) ? bms : [];
|
bookmarkList = Array.isArray(bms) ? bms : [];
|
||||||
populateTsBookmarkSelect();
|
populateTsBookmarkSelect();
|
||||||
renderScheduler();
|
renderScheduler();
|
||||||
|
clearSchedulerDirty();
|
||||||
renderSchedulerInterleaveStatus();
|
renderSchedulerInterleaveStatus();
|
||||||
})
|
})
|
||||||
.catch(function (e) {
|
.catch(function (e) {
|
||||||
@@ -569,6 +571,7 @@
|
|||||||
|
|
||||||
schCloseEntryForm();
|
schCloseEntryForm();
|
||||||
renderTimespanEntries();
|
renderTimespanEntries();
|
||||||
|
markSchedulerDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
@@ -781,6 +784,7 @@
|
|||||||
if (!currentConfig || !currentConfig.entries) return;
|
if (!currentConfig || !currentConfig.entries) return;
|
||||||
currentConfig.entries.splice(idx, 1);
|
currentConfig.entries.splice(idx, 1);
|
||||||
renderTimespanEntries();
|
renderTimespanEntries();
|
||||||
|
markSchedulerDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
@@ -830,6 +834,7 @@
|
|||||||
.then(function (saved) {
|
.then(function (saved) {
|
||||||
currentConfig = saved;
|
currentConfig = saved;
|
||||||
renderScheduler();
|
renderScheduler();
|
||||||
|
clearSchedulerDirty();
|
||||||
showSchedulerToast("Scheduler saved.");
|
showSchedulerToast("Scheduler saved.");
|
||||||
})
|
})
|
||||||
.catch(function (e) {
|
.catch(function (e) {
|
||||||
@@ -859,6 +864,7 @@
|
|||||||
entries: [],
|
entries: [],
|
||||||
};
|
};
|
||||||
renderScheduler();
|
renderScheduler();
|
||||||
|
clearSchedulerDirty();
|
||||||
showSchedulerToast("Scheduler reset.");
|
showSchedulerToast("Scheduler reset.");
|
||||||
})
|
})
|
||||||
.catch(function (e) {
|
.catch(function (e) {
|
||||||
@@ -866,6 +872,22 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Dirty-state tracking
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
function markSchedulerDirty() {
|
||||||
|
if (schedulerDirty) return;
|
||||||
|
schedulerDirty = true;
|
||||||
|
var btn = document.getElementById("scheduler-save-btn");
|
||||||
|
if (btn) btn.classList.add("sch-dirty");
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearSchedulerDirty() {
|
||||||
|
schedulerDirty = false;
|
||||||
|
var btn = document.getElementById("scheduler-save-btn");
|
||||||
|
if (btn) btn.classList.remove("sch-dirty");
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// Toast helper
|
// Toast helper
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
@@ -918,6 +940,21 @@
|
|||||||
schedulerSelectRelativeEntry(1);
|
schedulerSelectRelativeEntry(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Dirty-state: mark dirty on any user input/change within the scheduler panel
|
||||||
|
var schPanel = document.getElementById("scheduler-panel");
|
||||||
|
if (schPanel && !schPanel._dirtyWired) {
|
||||||
|
schPanel._dirtyWired = true;
|
||||||
|
schPanel.addEventListener("input", function (e) {
|
||||||
|
// Ignore the entry-form inputs (they don't affect saved config until submitted)
|
||||||
|
if (e.target.closest("#sch-entry-form") || e.target.closest("#sch-sat-form")) return;
|
||||||
|
markSchedulerDirty();
|
||||||
|
});
|
||||||
|
schPanel.addEventListener("change", function (e) {
|
||||||
|
if (e.target.closest("#sch-entry-form") || e.target.closest("#sch-sat-form")) return;
|
||||||
|
markSchedulerDirty();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
wireExtraBmAdd();
|
wireExtraBmAdd();
|
||||||
wireSatelliteEvents();
|
wireSatelliteEvents();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -313,6 +313,7 @@ window.restoreVdesHistory = function(messages) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
document.getElementById("settings-clear-vdes-history")?.addEventListener("click", async () => {
|
document.getElementById("settings-clear-vdes-history")?.addEventListener("click", async () => {
|
||||||
|
if (!confirm("Clear all VDES decode history? This cannot be undone.")) return;
|
||||||
try {
|
try {
|
||||||
await postPath("/clear_vdes_decode");
|
await postPath("/clear_vdes_decode");
|
||||||
window.resetVdesHistoryView();
|
window.resetVdesHistoryView();
|
||||||
|
|||||||
@@ -266,6 +266,7 @@ wsprDecodeToggleBtn?.addEventListener("click", async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById("settings-clear-wspr-history")?.addEventListener("click", async () => {
|
document.getElementById("settings-clear-wspr-history")?.addEventListener("click", async () => {
|
||||||
|
if (!confirm("Clear all WSPR decode history? This cannot be undone.")) return;
|
||||||
try {
|
try {
|
||||||
await postPath("/clear_wspr_decode");
|
await postPath("/clear_wspr_decode");
|
||||||
window.resetWsprHistoryView();
|
window.resetWsprHistoryView();
|
||||||
|
|||||||
@@ -4502,6 +4502,21 @@ button:focus-visible, input:focus-visible, select:focus-visible {
|
|||||||
border-color: var(--accent-green);
|
border-color: var(--accent-green);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
.sch-save-btn.sch-dirty::after {
|
||||||
|
content: "";
|
||||||
|
display: inline-block;
|
||||||
|
width: 7px;
|
||||||
|
height: 7px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #fff;
|
||||||
|
margin-left: 0.45rem;
|
||||||
|
vertical-align: middle;
|
||||||
|
animation: sch-dirty-pulse 1.5s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
@keyframes sch-dirty-pulse {
|
||||||
|
0%, 100% { opacity: 1; }
|
||||||
|
50% { opacity: 0.4; }
|
||||||
|
}
|
||||||
.sch-status-card {
|
.sch-status-card {
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
@@ -4829,6 +4844,26 @@ button:focus-visible, input:focus-visible, select:focus-visible {
|
|||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
}
|
}
|
||||||
|
/* ── Select All / Deselect All buttons ────────────────────────────── */
|
||||||
|
.bgd-select-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
.bgd-select-btn {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border: 1px solid var(--border-light);
|
||||||
|
border-radius: 0.3rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
font-size: 0.78rem;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 0.25rem 0.65rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.15s, color 0.15s;
|
||||||
|
}
|
||||||
|
.bgd-select-btn:hover {
|
||||||
|
background: var(--card-bg);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
/* ── SVG State Dot Badges ─────────────────────────────────────────── */
|
/* ── SVG State Dot Badges ─────────────────────────────────────────── */
|
||||||
.bgd-state-dot {
|
.bgd-state-dot {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
|
|||||||
Reference in New Issue
Block a user