From dce772f330db12a73e85297918882c0c4f28f030 Mon Sep 17 00:00:00 2001 From: TJ Dong <122852285+tj-dong@users.noreply.github.com> Date: Mon, 8 Jun 2026 20:39:14 -0700 Subject: feat: show remaining auth time in Chrome Bridge status bar (#5) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 0.15.36: richer chrome_snapshot + chrome_find/chrome_inspect (ported from foxfirecodes fork) - chrome_snapshot returns a concise observation (layout, hints, actions, forms, page map, query matches, diff) with mode/query/maxTextChars params - new chrome_find (query -> ranked uids) and chrome_inspect (deep element context, with page.inspect fallback) - includeSnapshot on click/type/fill/key now embeds the formatted snapshot - snapshot/inspect logic shipped as eval-free packaged snapshot_injected.js, sharing window.__PI_CHROME_STATE__ (el- uid scheme) with existing input helpers * feat: show remaining auth time in Chrome Bridge status bar Before: ● Chrome Bridge After: ● Chrome Bridge (14m) ● Chrome Bridge (indefinite) Changes: - authCountdownLabel() computes remaining minutes from chromeAuthorizedUntil - updateChromeStatus appends the label to the status line - startCountdownTicker() runs setInterval every 60s to refresh the display while a timed authorization is active; no-ops for indefinite auth - clearCountdownInterval() called in lockChromeControl and session_shutdown so no interval leaks on revoke or reload - scheduleAuthExpiry calls startCountdownTicker so the ticker starts whenever authorization is granted or restored after /reload --------- Co-authored-by: Tianren Dong --- extensions/chrome-profile-bridge/index.ts | 38 ++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/extensions/chrome-profile-bridge/index.ts b/extensions/chrome-profile-bridge/index.ts index 424b9c1..51d8e26 100644 --- a/extensions/chrome-profile-bridge/index.ts +++ b/extensions/chrome-profile-bridge/index.ts @@ -702,6 +702,7 @@ export default function (pi: ExtensionAPI): void { }; let chromeToolsRegistered = false; let authExpiryTimer: NodeJS.Timeout | undefined; + let countdownInterval: NodeJS.Timeout | undefined; // Remembered so bridge sends can tag tabs with this session's group even when ctx isn't handy. let sessionCtx: ExtensionContext | undefined; @@ -711,6 +712,12 @@ export default function (pi: ExtensionAPI): void { authExpiryTimer = undefined; }; + const clearCountdownInterval = (): void => { + if (!countdownInterval) return; + clearInterval(countdownInterval); + countdownInterval = undefined; + }; + const activeToolNamesWithoutChrome = (): string[] => pi.getActiveTools().filter((name) => !CHROME_TOOL_NAME_SET.has(name)); const activateChromeTools = (): void => { @@ -724,6 +731,7 @@ export default function (pi: ExtensionAPI): void { const lockChromeControl = (): void => { clearAuthExpiryTimer(); + clearCountdownInterval(); chromeAuthorizedUntil = undefined; persistAuth(); deactivateChromeTools(); @@ -759,16 +767,42 @@ export default function (pi: ExtensionAPI): void { return `Pi Session: ${name || id || "unknown"}`; }; + const authCountdownLabel = (): string => { + if (chromeAuthorizedUntil === "indefinite") return " (indefinite)"; + if (typeof chromeAuthorizedUntil === "number") { + const remainingMs = chromeAuthorizedUntil - Date.now(); + if (remainingMs > 0) { + const mins = Math.ceil(remainingMs / 60_000); + return mins >= 1 ? ` (${mins}m)` : " (<1m)"; + } + } + return ""; + }; + const updateChromeStatus = (ctx: ExtensionContext): void => { if (chromeControlAuthorized()) { - ctx.ui.setStatus("chrome", ctx.ui.theme.fg("success", "●") + " Chrome Bridge"); + ctx.ui.setStatus("chrome", ctx.ui.theme.fg("success", "●") + " Chrome Bridge" + authCountdownLabel()); } else { ctx.ui.setStatus("chrome", undefined); } }; + // Ticks every 60 s while a timed authorization is active to keep the countdown current. + const startCountdownTicker = (ctx: ExtensionContext): void => { + clearCountdownInterval(); + if (chromeAuthorizedUntil === "indefinite" || typeof chromeAuthorizedUntil !== "number") return; + countdownInterval = setInterval(() => { + if (!chromeControlAuthorized()) { + clearCountdownInterval(); + return; + } + updateChromeStatus(ctx); + }, 60_000); + }; + const scheduleAuthExpiry = (ctx: ExtensionContext, until: number | "indefinite"): void => { clearAuthExpiryTimer(); + startCountdownTicker(ctx); if (until === "indefinite") return; authExpiryTimer = setTimeout(() => { if (chromeAuthorizedUntil !== until) return; @@ -816,12 +850,14 @@ export default function (pi: ExtensionAPI): void { if (chromeControlAuthorized()) { activateChromeTools(); if (typeof chromeAuthorizedUntil === "number") scheduleAuthExpiry(ctx, chromeAuthorizedUntil); + else if (chromeAuthorizedUntil === "indefinite") startCountdownTicker(ctx); } updateChromeStatus(ctx); }); pi.on("session_shutdown", () => { clearAuthExpiryTimer(); + clearCountdownInterval(); bridge.stop(); if (globalState[PI_CHROME_GLOBAL_KEY]?.token === instanceToken) { delete globalState[PI_CHROME_GLOBAL_KEY]; -- cgit v1.2.3