src/popup/popup.ts

Total Symbols
5
Lines of Code
141
Avg Complexity
2.8
Symbol Types
1

File Relationships

graph LR doSend["doSend"] showStatus["showStatus"] bindHandlers["bindHandlers"] init["init"] doSend -->|calls| showStatus bindHandlers -->|calls| doSend init -->|calls| bindHandlers click doSend "../symbols/a64fadbf3863fefa.html" click showStatus "../symbols/2aab6178fa5261e5.html" click bindHandlers "../symbols/e0addfeca217a6b2.html" click init "../symbols/9c03e8cd6a18eb5d.html"

Symbols by Kind

function 5

All Symbols

Name Kind Visibility Status Lines Signature
render function - 22-45 render(): : void
showStatus function - 49-58 showStatus(msg: string, isError = false): : void
doSend function - 62-80 doSend( message: import("@/types").ExtensionMessage, btn: HTMLButtonElement, originalText: string, ): : Promise<void>
bindHandlers function - 82-123 bindHandlers(): : void
init function - 127-138 init(): : Promise<void>

Full Source

/**
 * Popup script — minimal UI with 4 actions.
 *
 * Actions: Fill All, Fill with AI, Fill Only Empties, Settings.
 */

import "./popup.css";

import { initI18n, t } from "@/lib/i18n";
import { createLogger, initLogger } from "@/lib/logger";
import { openAIContextModal } from "./popup-ai-context-modal";
import { sendToActiveTab } from "./popup-messaging";

const log = createLogger("Popup");

// ── State ────────────────────────────────────────────────────────────────────

let pageUrl = "";

// ── Render ───────────────────────────────────────────────────────────────────

function render(): void {
  document.body.innerHTML = `
    <div class="popup-container">
      <div class="popup-header">
        <span class="popup-logo">Fill All</span>
      </div>
      <div class="popup-body">
        <div id="status" class="status" style="display:none"></div>
        <button id="btn-fill-all" class="action-btn primary">
          ⚡ ${t("fillAll")}
        </button>
        <button id="btn-fill-ai" class="action-btn">
          🤖 ${t("fillContextualAI")}
        </button>
        <button id="btn-fill-empty" class="action-btn">
          🔍 ${t("fillOnlyEmpty")}
        </button>
        <button id="btn-settings" class="action-btn info">
          ⚙️ ${t("settings")}
        </button>
      </div>
    </div>
  `;
}

// ── Status ───────────────────────────────────────────────────────────────────

function showStatus(msg: string, isError = false): void {
  const el = document.getElementById("status");
  if (!el) return;
  el.textContent = msg;
  el.className = isError ? "status error" : "status success";
  el.style.display = "block";
  setTimeout(() => {
    el.style.display = "none";
  }, 2000);
}

// ── Handlers ─────────────────────────────────────────────────────────────────

async function doSend(
  message: import("@/types").ExtensionMessage,
  btn: HTMLButtonElement,
  originalText: string,
): Promise<void> {
  btn.disabled = true;
  try {
    const result = await sendToActiveTab(message);
    const res = result as { filled?: number } | null;
    const count = res?.filled ?? 0;
    showStatus(`✓ ${count} ${t("filled")}`);
  } catch (err) {
    log.warn("Failed to send message:", err);
    showStatus(t("error"), true);
  } finally {
    btn.disabled = false;
    btn.textContent = originalText;
  }
}

function bindHandlers(): void {
  const btnFillAll = document.getElementById(
    "btn-fill-all",
  ) as HTMLButtonElement | null;
  btnFillAll?.addEventListener("click", async () => {
    const orig = btnFillAll.textContent ?? "";
    btnFillAll.textContent = "…";
    await doSend({ type: "FILL_ALL_FIELDS" }, btnFillAll, orig);
  });

  const btnFillAi = document.getElementById(
    "btn-fill-ai",
  ) as HTMLButtonElement | null;
  btnFillAi?.addEventListener("click", async () => {
    const context = await openAIContextModal();
    if (context === null) return;
    const orig = btnFillAi.textContent ?? "";
    btnFillAi.textContent = "…";
    await doSend(
      { type: "FILL_CONTEXTUAL_AI", payload: context },
      btnFillAi,
      orig,
    );
  });

  const btnFillEmpty = document.getElementById(
    "btn-fill-empty",
  ) as HTMLButtonElement | null;
  btnFillEmpty?.addEventListener("click", async () => {
    const orig = btnFillEmpty.textContent ?? "";
    btnFillEmpty.textContent = "…";
    await doSend(
      { type: "FILL_ALL_FIELDS", payload: { fillEmptyOnly: true } },
      btnFillEmpty,
      orig,
    );
  });

  document.getElementById("btn-settings")?.addEventListener("click", () => {
    chrome.runtime.openOptionsPage();
  });
}

// ── Init ─────────────────────────────────────────────────────────────────────

async function init(): Promise<void> {
  await initLogger();
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
  pageUrl = tab?.url ?? "";

  await initI18n("auto");

  render();
  bindHandlers();

  log.debug("Popup init, url:", pageUrl);
}

void init();