feat: implement M3 multi-browser support (Firefox, Edge, Safari)
Add browser abstraction layer (compat.ts) that normalizes Chrome/Firefox/ Edge/Safari extension APIs behind a unified promise-based interface. Replace all direct chrome.* calls with compat layer across service worker, sync engine, badge, storage, popup, and options modules. - Browser-specific manifests in manifests/ (Firefox MV3 with gecko settings, Edge/Safari variants) - Multi-target build system: `npm run build` produces all four browser builds in build/<browser>/ - Per-browser build scripts: build:chrome, build:firefox, build:edge, build:safari - Auto-detects browser at runtime for platform-specific device registration - All 38 existing tests pass, typecheck clean Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -1,24 +1,19 @@
|
||||
/**
|
||||
* Options page script — manages extension settings.
|
||||
* Uses the browser-agnostic compat layer.
|
||||
*/
|
||||
import { runtime, storage } from "../lib/compat";
|
||||
|
||||
export {};
|
||||
|
||||
// --- Messaging helper ---
|
||||
|
||||
function sendMessage(type: string, payload?: unknown): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
chrome.runtime.sendMessage({ type, payload }, (response) => {
|
||||
if (chrome.runtime.lastError) {
|
||||
reject(new Error(chrome.runtime.lastError.message));
|
||||
return;
|
||||
}
|
||||
if (response?.error) {
|
||||
reject(new Error(response.error));
|
||||
return;
|
||||
}
|
||||
resolve(response);
|
||||
});
|
||||
});
|
||||
async function sendMessage(type: string, payload?: unknown): Promise<any> {
|
||||
const response = await runtime.sendMessage({ type, payload });
|
||||
if (response?.error) {
|
||||
throw new Error(response.error);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
// --- Elements ---
|
||||
@@ -59,7 +54,7 @@ let blacklist: string[] = [];
|
||||
// --- Load Settings ---
|
||||
|
||||
async function loadSettings() {
|
||||
const state = await chrome.storage.local.get(null);
|
||||
const state = await storage.get(null);
|
||||
|
||||
optDeviceName.textContent = state.deviceName || "—";
|
||||
optDeviceId.textContent = state.deviceId || "—";
|
||||
@@ -168,7 +163,7 @@ btnAddBlacklist.addEventListener("click", () => {
|
||||
|
||||
// Save
|
||||
btnSave.addEventListener("click", async () => {
|
||||
await chrome.storage.local.set({
|
||||
await storage.set({
|
||||
serverUrl: optServerUrl.value.trim(),
|
||||
connectionMode: optConnectionMode.value,
|
||||
pollIntervalSec: parseInt(optPollInterval.value),
|
||||
@@ -247,7 +242,7 @@ btnClearData.addEventListener("click", async () => {
|
||||
return;
|
||||
}
|
||||
await sendMessage("LOGOUT");
|
||||
await chrome.storage.local.clear();
|
||||
await storage.clear();
|
||||
alert("All data cleared.");
|
||||
window.close();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user