commands.ts

2.7 KB · raw

1/**2 * Keyboard command handler — routing for summon-inline:3 *   1. Restricted URL (non-http/https) → open popup4 *   2. Ping content script (150ms timeout) → if alive, send SUMMON_AUTOFILL5 *      with source:"shortcut"; content script handles inline (autofill selector6 *      or search-mode palette). Always returns early — no popup fallback.7 *   3. Content script not ready → open popup fallback8 */910import { createSummonCapability } from "./messages";1112const PING_TIMEOUT_MS = 150;1314async function pingContentScript(tabId: number): Promise<boolean> {15  return new Promise((resolve) => {16    const timer = setTimeout(() => resolve(false), PING_TIMEOUT_MS);17    try {18      chrome.tabs.sendMessage(tabId, { type: "PING" }, (response) => {19        clearTimeout(timer);20        if (chrome.runtime.lastError) {21          resolve(false);22        } else {23          resolve(response?.ok === true);24        }25      });26    } catch {27      clearTimeout(timer);28      resolve(false);29    }30  });31}3233async function openPopup(): Promise<void> {34  try {35    // chrome.action.openPopup() requires Chrome 127+ and user gesture context;36    // it may throw — that's acceptable, we degrade gracefully.37    await (chrome.action as { openPopup?: () => Promise<void> }).openPopup?.();38  } catch {39    // Silently ignore — user can click the toolbar icon instead40  }41}4243export function setupCommands(): void {44  chrome.commands.onCommand.addListener(async (command) => {45    if (command !== "summon-inline") return;4647    const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });48    if (!tab?.id || !tab.url) return;4950    // ── Step 1: restricted URL? ───────────────────────────────────51    if (!/^https?:\/\//i.test(tab.url)) {52      await openPopup();53      return;54    }5556    const tabId = tab.id;5758    // ── Step 2: ping existing content script ─────────────────────59    const alive = await pingContentScript(tabId);60    if (alive) {61      // Content script fully handles SUMMON_AUTOFILL with source:"shortcut":62      // shows the autofill selector when a form is present, or falls back to63      // a search-mode palette that copies to clipboard. No popup fallback needed.64      const origin = new URL(tab.url).origin;65      const capability = createSummonCapability(tabId, origin, 0);66      chrome.tabs.sendMessage(67        tabId,68        { type: "SUMMON_AUTOFILL", source: "shortcut", capability },69        { frameId: 0 },70      ).catch(() => {});71      return;72    }7374    // ── Step 3: content script unavailable ───────────────────────75    await openPopup();76  });77}