feat: add options panel embedded in about:addons
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
import { DEFAULT_SETTINGS } from "../lib/defaults.js";
|
||||
import { load, save } from "../lib/settings.js";
|
||||
|
||||
const SAVE_DEBOUNCE_MS = 400;
|
||||
const STATUS_CLEAR_MS = 1500;
|
||||
|
||||
const field = (id) => document.getElementById(id);
|
||||
|
||||
let saveTimer = null;
|
||||
let statusTimer = null;
|
||||
// Suppresses the change/input handlers while we are programmatically writing
|
||||
// values into the form, so a remote sync cannot trigger a save loop.
|
||||
let applying = false;
|
||||
|
||||
function render(settings) {
|
||||
applying = true;
|
||||
field("model").value = settings.model;
|
||||
field("temporaryChat").checked = settings.temporaryChat;
|
||||
field("webSearch").checked = settings.webSearch;
|
||||
field("openIn").value = settings.openIn;
|
||||
field("promptTemplate").value = settings.promptTemplate;
|
||||
applying = false;
|
||||
}
|
||||
|
||||
function collect() {
|
||||
return {
|
||||
model: field("model").value,
|
||||
temporaryChat: field("temporaryChat").checked,
|
||||
webSearch: field("webSearch").checked,
|
||||
openIn: field("openIn").value,
|
||||
promptTemplate: field("promptTemplate").value,
|
||||
};
|
||||
}
|
||||
|
||||
function showStatus(message) {
|
||||
field("status").textContent = message;
|
||||
clearTimeout(statusTimer);
|
||||
statusTimer = setTimeout(() => {
|
||||
field("status").textContent = "";
|
||||
}, STATUS_CLEAR_MS);
|
||||
}
|
||||
|
||||
async function persist(settings) {
|
||||
try {
|
||||
await save(settings);
|
||||
showStatus("Saved");
|
||||
} catch {
|
||||
// storage.sync sanitises its errors, so there is nothing specific to report.
|
||||
showStatus("Could not save settings");
|
||||
}
|
||||
}
|
||||
|
||||
// Debounced so typing in a text field cannot approach any write-rate quota.
|
||||
function scheduleSave() {
|
||||
if (applying) {
|
||||
return;
|
||||
}
|
||||
clearTimeout(saveTimer);
|
||||
saveTimer = setTimeout(() => persist(collect()), SAVE_DEBOUNCE_MS);
|
||||
}
|
||||
|
||||
// Module scripts are deferred, so the DOM is already parsed here.
|
||||
render(await load());
|
||||
|
||||
for (const id of Object.keys(DEFAULT_SETTINGS)) {
|
||||
field(id).addEventListener("input", scheduleSave);
|
||||
field(id).addEventListener("change", scheduleSave);
|
||||
}
|
||||
|
||||
field("restore").addEventListener("click", () => {
|
||||
clearTimeout(saveTimer);
|
||||
render(DEFAULT_SETTINGS);
|
||||
persist({ ...DEFAULT_SETTINGS });
|
||||
});
|
||||
|
||||
// A remote sync landing while the panel is open must update the controls rather
|
||||
// than being clobbered by stale field values on the next edit.
|
||||
browser.storage.onChanged.addListener(async (changes, area) => {
|
||||
if (area !== "sync") {
|
||||
return;
|
||||
}
|
||||
render(await load());
|
||||
});
|
||||
Reference in New Issue
Block a user