Files
CookieBridge/extension/src/lib/badge.ts
徐枫 f39ff8c215 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>
2026-03-17 16:58:44 +08:00

72 lines
1.9 KiB
TypeScript

/**
* Badge/icon management — updates extension icon color and badge text
* based on connection status and sync activity.
* Uses the browser-agnostic compat layer.
*
* States:
* - gray: Not logged in / no device identity
* - blue: Connected, idle
* - green: Syncing (with count badge)
* - red: Error / disconnected
*/
import { action, storage } from "./compat";
type IconColor = "gray" | "blue" | "green" | "red";
function iconPath(color: IconColor, size: number): string {
return `src/icons/icon-${color}-${size}.png`;
}
function iconSet(color: IconColor): Record<string, string> {
return {
"16": iconPath(color, 16),
"48": iconPath(color, 48),
"128": iconPath(color, 128),
};
}
export async function setIconState(
state: "not_logged_in" | "connected" | "syncing" | "error",
syncCount?: number,
) {
switch (state) {
case "not_logged_in":
await action.setIcon({ path: iconSet("gray") });
await action.setBadgeText({ text: "" });
break;
case "connected":
await action.setIcon({ path: iconSet("blue") });
await action.setBadgeText({ text: "" });
break;
case "syncing":
await action.setIcon({ path: iconSet("green") });
if (syncCount && syncCount > 0) {
await action.setBadgeText({
text: syncCount > 99 ? "99+" : String(syncCount),
});
await action.setBadgeBackgroundColor({ color: "#22C55E" });
}
break;
case "error":
await action.setIcon({ path: iconSet("red") });
await action.setBadgeText({ text: "!" });
await action.setBadgeBackgroundColor({ color: "#EF4444" });
break;
}
}
/** Clear the sync badge after a delay. */
export function clearSyncBadge(delayMs = 3000) {
setTimeout(async () => {
const state = await storage.get(["apiToken"]);
if (state.apiToken) {
await setIconState("connected");
} else {
await setIconState("not_logged_in");
}
}, delayMs);
}